diff --git a/backend/main.py b/backend/main.py index b16fd71..990441f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -76,10 +76,15 @@ def verify_otp(phone_or_email: str, code: str, session: Session = Depends(get_se # --- 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)): +def list_active_tests(exam_pattern: Optional[str] = None, type: 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) + if type: + query = query.where(models.Test.type == type) + else: + # If type isn't specified, don't return DPS in regular tests query + query = query.where(models.Test.type != "DPS") return session.exec(query).all() diff --git a/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782971559253.pdf b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782971559253.pdf new file mode 100644 index 0000000..756db6b Binary files /dev/null and b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782971559253.pdf differ diff --git a/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782979477911.pdf b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782979477911.pdf new file mode 100644 index 0000000..756db6b Binary files /dev/null and b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782979477911.pdf differ diff --git a/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783273583537.pdf b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783273583537.pdf new file mode 100644 index 0000000..756db6b Binary files /dev/null and b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783273583537.pdf differ diff --git a/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783410386530.pdf b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783410386530.pdf new file mode 100644 index 0000000..756db6b Binary files /dev/null and b/backend/pdfs/media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783410386530.pdf differ diff --git a/backend/seed_dpps.py b/backend/seed_dpps.py new file mode 100644 index 0000000..4d1cca4 --- /dev/null +++ b/backend/seed_dpps.py @@ -0,0 +1,112 @@ +import asyncio +import os +import uuid +from sqlmodel import Session, select +from datetime import datetime + +from database import engine, init_db +import models +import storage +from pdf_parser import parse_pdf_to_questions + +async def seed_pdfs(): + # Admin ID fallback + admin_id_str = "b0e17378-864d-439e-8806-708dc48a891b" # Or fetch first user + + with Session(engine) as session: + admin = session.exec(select(models.User).limit(1)).first() + if admin: + admin_id = admin.id + else: + admin_id = uuid.UUID(admin_id_str) + + # Paths to the 4 PDFs + base_dir = r"/app/pdfs" + pdf_files = [ + "media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782971559253.pdf", + "media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782979477911.pdf", + "media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783273583537.pdf", + "media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783410386530.pdf" + ] + + subjects = ["Physics", "Chemistry", "Biology", "Mathematics"] + topics = ["Rotational Dynamics", "Chemical Kinetics", "Genetics", "Calculus"] + + for idx, filename in enumerate(pdf_files): + file_path = os.path.join(base_dir, filename) + if not os.path.exists(file_path): + print(f"File not found: {file_path}") + continue + + print(f"Processing {filename}...") + + with open(file_path, "rb") as f: + pdf_bytes = f.read() + + # 1. Upload to Minio + object_name = f"dpps/{uuid.uuid4()}_{filename}" + storage.upload_file(pdf_bytes, object_name, content_type="application/pdf") + + # 2. Extract + questions_data = await parse_pdf_to_questions(pdf_bytes, "JEE_Main") + + if not questions_data: + print(f"Warning: No questions extracted for {filename}. Using mock data fallback.") + questions_data = [ + { + "subject": subjects[idx], + "topic": topics[idx], + "subtopic": "General", + "difficulty": "Hard", + "type": "MCQ_Single", + "question_text": f"What is the most fundamental principle of {topics[idx]}?", + "options": {"A": "Conservation of Energy", "B": "Newton's Second Law", "C": "Kinetic Theory", "D": "Thermodynamics"}, + "correct_answer": {"value": "A"}, + "solution_text": "By fundamental principles." + } + ] + + question_ids = [] + for q_data in questions_data: + new_q = models.Question( + subject=q_data.get("subject", subjects[idx]), + topic=q_data.get("topic", topics[idx]), + subtopic=q_data.get("subtopic", "General"), + difficulty=q_data.get("difficulty", "Medium"), + type=models.QuestionTypeEnum.MCQ_SINGLE, + question_text=q_data.get("question_text", ""), + options=q_data.get("options", {}), + correct_answer=q_data.get("correct_answer", {}), + solution_text=q_data.get("solution_text", ""), + language=models.MediumEnum.ENGLISH, + exam=models.ExamEnum.JEE_MAIN, + created_by=admin_id + ) + session.add(new_q) + session.flush() + question_ids.append(str(new_q.id)) + + # Create Test record + test_obj = models.Test( + title=f"AI DPP: {topics[idx]}", + type="DPS", + exam_pattern=models.ExamEnum.JEE_MAIN, + sections={ + "Section 1": { + "questions": question_ids, + "time_limit": 60 + } + }, + total_time=60, + difficulty="Hard" if idx % 2 == 0 else "Medium", + scheduled_date=datetime.utcnow(), + is_active=True, + created_by=admin_id + ) + session.add(test_obj) + session.commit() + print(f"Successfully created DPS Test: {test_obj.title} with {len(question_ids)} questions.") + +if __name__ == "__main__": + asyncio.run(seed_pdfs()) + diff --git a/dpps/dps-bio-ch4-20260707.pdf b/dpps/dps-bio-ch4-20260707.pdf new file mode 100644 index 0000000..8410201 Binary files /dev/null and b/dpps/dps-bio-ch4-20260707.pdf differ diff --git a/dpps/dps-che-ch4-20260707.pdf b/dpps/dps-che-ch4-20260707.pdf new file mode 100644 index 0000000..896d52f Binary files /dev/null and b/dpps/dps-che-ch4-20260707.pdf differ diff --git a/dpps/dps-mth-ch4-20260707.pdf b/dpps/dps-mth-ch4-20260707.pdf new file mode 100644 index 0000000..a9bc02f Binary files /dev/null and b/dpps/dps-mth-ch4-20260707.pdf differ diff --git a/dpps/dps-phy-ch4-20260707.pdf b/dpps/dps-phy-ch4-20260707.pdf new file mode 100644 index 0000000..ce44ad4 Binary files /dev/null and b/dpps/dps-phy-ch4-20260707.pdf differ diff --git a/src/app/dashboard/dpp/page.tsx b/src/app/dashboard/dpp/page.tsx index e90726e..0166064 100644 --- a/src/app/dashboard/dpp/page.tsx +++ b/src/app/dashboard/dpp/page.tsx @@ -2,13 +2,29 @@ import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" import { BrainCircuit, Play, CheckCircle2, Download, Filter, Lock } from "lucide-react" +import { useEffect, useState } from "react" +import Link from "next/link" + export default function DPPPage() { - const dpps = [ - { title: "Top Overturning", subject: "Physics", topic: "Rotational Dynamics", difficulty: "Hard", count: 15, status: "Pending", priority: "High" }, - { title: "First Order Reactions", subject: "Chemistry", topic: "Chemical Kinetics", difficulty: "Medium", count: 20, status: "Pending", priority: "Medium" }, - { title: "Mendelian Inheritance", subject: "Biology", topic: "Genetics", difficulty: "Easy", count: 25, status: "Completed", score: "23/25" }, - { title: "Definite Integration", subject: "Mathematics", topic: "Calculus", difficulty: "Mixed", count: 15, status: "Completed", score: "14/15" }, - ] + const [dpps, setDpps] = useState([]) + const [loading, setLoading] = useState(true) + + useEffect(() => { + async function fetchDpps() { + try { + const res = await fetch("http://localhost:8000/api/tests?type=DPS") + if (res.ok) { + const data = await res.json() + setDpps(data) + } + } catch (err) { + console.error("Failed to fetch DPPs", err) + } finally { + setLoading(false) + } + } + fetchDpps() + }, []) const getDifficultyColor = (diff: string) => { switch(diff) { @@ -51,18 +67,31 @@ export default function DPPPage() {
- {dpps.map((dpp, i) => ( - + {loading ? ( +
Loading AI-generated DPPs...
+ ) : dpps.length === 0 ? ( +
No DPPs available yet. Upload some PDFs in the Admin Panel!
+ ) : dpps.map((dpp, i) => { + // Calculate total questions dynamically from sections + let count = 0; + if (dpp.sections) { + Object.values(dpp.sections).forEach((s: any) => { count += (s.questions?.length || 0) }); + } + // Temporary mock for status + const status = "Pending" + + return ( +
-
- {dpp.status === 'Completed' ? :
} +
+ {status === 'Completed' ? :
}
- {dpp.subject} + {dpp.exam_pattern?.replace("_", " ") || "JEE/NEET"} • - {dpp.topic} + {dpp.difficulty} Level

{dpp.title} @@ -71,20 +100,24 @@ export default function DPPPage() {

- {dpp.count} Questions • {dpp.status === 'Completed' ? `Score: ${dpp.score}` : `${dpp.priority} Priority`} + {count} Questions • {dpp.total_time} mins
- {dpp.status === 'Completed' ? ( - + {status === 'Completed' ? ( + + + ) : ( - + + + )}
- ))} + )})}
)