From 6add14f0c54545cba2a2d7fd63ecd90aeaba5d35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Mar 2026 01:06:40 +0000 Subject: [PATCH 1/3] Initial plan From 60656a370e098f2328b21fb467822a2a8ca3b5f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Mar 2026 01:12:27 +0000 Subject: [PATCH 2/3] Add quiz preparation feature with AI-generated questions Co-authored-by: MansiPatil21 <86612618+MansiPatil21@users.noreply.github.com> --- backend/main.py | 104 ++++ frontend/build/asset-manifest.json | 6 +- frontend/build/index.html | 2 +- .../js/{main.924ed23b.js => main.798d63d4.js} | 6 +- ...CENSE.txt => main.798d63d4.js.LICENSE.txt} | 0 frontend/build/static/js/main.798d63d4.js.map | 1 + frontend/build/static/js/main.924ed23b.js.map | 1 - frontend/src/App.tsx | 2 + frontend/src/pages/QuizView.tsx | 585 ++++++++++++++++++ frontend/src/pages/RoadmapView.tsx | 13 + 10 files changed, 712 insertions(+), 8 deletions(-) rename frontend/build/static/js/{main.924ed23b.js => main.798d63d4.js} (67%) rename frontend/build/static/js/{main.924ed23b.js.LICENSE.txt => main.798d63d4.js.LICENSE.txt} (100%) create mode 100644 frontend/build/static/js/main.798d63d4.js.map delete mode 100644 frontend/build/static/js/main.924ed23b.js.map create mode 100644 frontend/src/pages/QuizView.tsx diff --git a/backend/main.py b/backend/main.py index 1bf9cc9..02a76fe 100644 --- a/backend/main.py +++ b/backend/main.py @@ -73,6 +73,10 @@ class AddTopicRequest(BaseModel): difficulty: Optional[str] = "medium" weightage: Optional[str] = "" +class GenerateQuizRequest(BaseModel): + num_questions: Optional[int] = 5 + difficulty: Optional[str] = None # "easy", "medium", "hard", or None for all + # Auth Utilities def get_password_hash(password: str) -> str: return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') @@ -705,6 +709,106 @@ def get_risk_analysis(current_user: dict = Depends(get_current_user)): traceback.print_exc() raise HTTPException(status_code=500, detail="Failed to calculate risk analysis") +# Quiz Generation +@app.post("/api/roadmaps/{roadmap_id}/quiz") +async def generate_quiz(roadmap_id: str, req: GenerateQuizRequest, current_user: dict = Depends(get_current_user)): + response = roadmaps_table.get_item(Key={'roadmap_id': roadmap_id}) + if 'Item' not in response: + raise HTTPException(status_code=404, detail="Roadmap not found") + + roadmap = response['Item'] + if roadmap.get('user_email') != current_user['email']: + raise HTTPException(status_code=403, detail="Not authorized to access this roadmap") + + topics = roadmap.get('topics', []) + if not topics: + raise HTTPException(status_code=400, detail="Roadmap has no topics to quiz on") + + # Filter by difficulty if requested + candidate_topics = topics + if req.difficulty: + filtered = [t for t in topics if t.get('difficulty') == req.difficulty] + if filtered: + candidate_topics = filtered + + # Limit to a reasonable number of questions + num_questions = max(1, min(req.num_questions or 5, 20)) + + # Build a concise summary of topics for the AI + topic_lines = [] + for t in candidate_topics: + line = f"- {t.get('name', 'Unknown')} (type: {t.get('type', 'lecture')}, difficulty: {t.get('difficulty', 'medium')})" + if t.get('description'): + line += f": {t['description']}" + topic_lines.append(line) + topics_text = "\n".join(topic_lines) + + course_name = roadmap.get('name', 'this course') + + groq_api_key = os.getenv("GROQ_API_KEY") + if not groq_api_key: + raise HTTPException(status_code=503, detail="AI service is not configured") + + try: + client = groq.Groq(api_key=groq_api_key) + prompt = f"""You are an expert academic quiz creator for the course: {course_name}. + +Based on the following course topics, generate exactly {num_questions} multiple-choice quiz questions to help a student prepare for their exam or quiz. + +Course Topics: +{topics_text} + +Requirements: +- Each question must test understanding of one of the topics listed above. +- Each question must have exactly 4 answer options labeled A, B, C, D. +- Only one option is correct. +- Include a brief explanation for why the correct answer is right. +- Questions should vary in difficulty and cover different topics. +- Questions should be clear, concise, and academically appropriate. + +Return a JSON object with this EXACT structure: +{{ + "questions": [ + {{ + "question": "What is the time complexity of binary search?", + "options": ["O(n)", "O(log n)", "O(n^2)", "O(1)"], + "correct_index": 1, + "explanation": "Binary search repeatedly halves the search space, resulting in O(log n) time complexity.", + "topic": "Binary Search" + }} + ] +}} + +Only return the JSON object. No markdown, no extra text.""" + + completion = client.chat.completions.create( + model="openai/gpt-oss-120b", + messages=[{"role": "user", "content": prompt}], + temperature=0.4, + max_completion_tokens=4096, + response_format={"type": "json_object"} + ) + + quiz_data = json.loads(completion.choices[0].message.content) + questions = quiz_data.get("questions", []) + + # Validate and clamp correct_index to be within [0, 3] + for q in questions: + if not isinstance(q.get("correct_index"), int): + q["correct_index"] = 0 + q["correct_index"] = max(0, min(q["correct_index"], len(q.get("options", [""])) - 1)) + + print(f"✅ Generated {len(questions)} quiz questions for roadmap {roadmap_id}") + return { + "roadmap_id": roadmap_id, + "roadmap_name": course_name, + "num_questions": len(questions), + "questions": questions + } + except Exception as e: + print(f"Error generating quiz: {e}") + raise HTTPException(status_code=500, detail="Failed to generate quiz questions") + # Health and Startup @app.get("/api/health") def health_check(): diff --git a/frontend/build/asset-manifest.json b/frontend/build/asset-manifest.json index 558e785..e4d57cd 100644 --- a/frontend/build/asset-manifest.json +++ b/frontend/build/asset-manifest.json @@ -1,15 +1,15 @@ { "files": { "main.css": "/static/css/main.e995b3ee.css", - "main.js": "/static/js/main.924ed23b.js", + "main.js": "/static/js/main.798d63d4.js", "static/js/453.20359781.chunk.js": "/static/js/453.20359781.chunk.js", "index.html": "/index.html", "main.e995b3ee.css.map": "/static/css/main.e995b3ee.css.map", - "main.924ed23b.js.map": "/static/js/main.924ed23b.js.map", + "main.798d63d4.js.map": "/static/js/main.798d63d4.js.map", "453.20359781.chunk.js.map": "/static/js/453.20359781.chunk.js.map" }, "entrypoints": [ "static/css/main.e995b3ee.css", - "static/js/main.924ed23b.js" + "static/js/main.798d63d4.js" ] } \ No newline at end of file diff --git a/frontend/build/index.html b/frontend/build/index.html index 6b7b000..33a010e 100644 --- a/frontend/build/index.html +++ b/frontend/build/index.html @@ -1 +1 @@ -