Feat: Complete CBT PDF upload, AI parsing, and MinIO storage integration

This commit is contained in:
2026-07-07 15:56:44 +05:30
parent c5260eec2e
commit b6ebb9d9c3
8 changed files with 476 additions and 8 deletions

View File

@@ -1,4 +1,4 @@
from fastapi import FastAPI, Depends, HTTPException, status, Request
from fastapi import FastAPI, Depends, HTTPException, status, Request, UploadFile, File, Form, BackgroundTasks
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from sqlmodel import Session, select
@@ -75,6 +75,13 @@ def verify_otp(phone_or_email: str, code: str, session: Session = Depends(get_se
return {"status": "authenticated", "token": "mock_jwt_session_token_string"}
# --- CBT TEST ENVIRONMENT CONTENT DELIVERY ENDPOINTS ---
@app.get("/api/tests", response_model=List[models.Test], tags=["CBT Engine"])
def list_active_tests(exam_pattern: Optional[str] = None, session: Session = Depends(get_session)):
query = select(models.Test).where(models.Test.is_active == True)
if exam_pattern:
query = query.where(models.Test.exam_pattern == exam_pattern)
return session.exec(query).all()
@app.get("/api/tests/{test_id}", response_model=models.Test, tags=["CBT Engine"])
def get_test_by_id(test_id: UUID, user_id: UUID, session: Session = Depends(get_session), current_user = Depends(require_tier([models.TierEnum.PRO, models.TierEnum.AI_COACH]))):
@@ -512,3 +519,69 @@ def get_user_attempts(user_id: UUID, session: Session = Depends(get_session)):
query = select(models.Attempt).where(models.Attempt.user_id == user_id)
return session.exec(query).all()
# --- ADMIN ROUTES ---
@app.get("/api/admin/tests", tags=["Admin"])
def get_admin_tests(session: Session = Depends(get_session)):
tests = session.exec(select(models.Test)).all()
return tests
@app.post("/api/admin/tests/upload", tags=["Admin"])
async def upload_admin_test(
file: UploadFile = File(...),
title: str = Form(...),
exam_pattern: str = Form(...),
time_limit: int = Form(...),
scheduled_date: Optional[datetime] = Form(None),
admin_id: UUID = Form(...),
session: Session = Depends(get_session)
):
import storage
import uuid
from worker import process_pdf_test
pdf_bytes = await file.read()
object_name = f"tests/{uuid.uuid4()}_{file.filename}"
url = storage.upload_file(pdf_bytes, object_name, content_type="application/pdf")
if not url:
raise HTTPException(status_code=500, detail="Failed to upload PDF to MinIO")
# Create inactive test
test_obj = models.Test(
title=title,
type="Mock",
exam_pattern=models.ExamEnum(exam_pattern),
sections={},
total_time=time_limit,
difficulty="Medium",
scheduled_date=scheduled_date,
is_active=False, # Inactive until processing finishes
created_by=admin_id
)
session.add(test_obj)
session.commit()
session.refresh(test_obj)
# Trigger async background worker
process_pdf_test.delay(
test_id_str=str(test_obj.id),
object_name=object_name,
admin_id_str=str(admin_id),
exam_pattern=exam_pattern
)
return {"status": "processing", "test_id": test_obj.id, "message": "Test is being processed by AI"}
@app.put("/api/admin/tests/{test_id}/toggle", tags=["Admin"])
def toggle_test_status(test_id: UUID, session: Session = Depends(get_session)):
test_obj = session.get(models.Test, test_id)
if not test_obj:
raise HTTPException(status_code=404, detail="Test not found")
test_obj.is_active = not test_obj.is_active
session.add(test_obj)
session.commit()
return {"status": "success", "is_active": test_obj.is_active}