diff --git a/ai-ml/requirements.txt b/ai-ml/requirements.txt index 91b30c8..bc380bd 100644 --- a/ai-ml/requirements.txt +++ b/ai-ml/requirements.txt @@ -18,3 +18,4 @@ python-dotenv groq yake pytest +pyjwt \ No newline at end of file diff --git a/ai-ml/weak_topic_detection/app/api/weak_topic_api.py b/ai-ml/weak_topic_detection/app/api/weak_topic_api.py index 33eece7..e6780e4 100644 --- a/ai-ml/weak_topic_detection/app/api/weak_topic_api.py +++ b/ai-ml/weak_topic_detection/app/api/weak_topic_api.py @@ -1,4 +1,4 @@ -from app.services.weak_topic_service import WeakTopicService +from weak_topic_detection.app.services.weak_topic_service import WeakTopicService class WeakTopicAPI: @@ -9,4 +9,4 @@ def __init__(self): def get_weak_topics(self): """Return the weak topics detected from quiz results.""" - return self.service.get_weak_topics() \ No newline at end of file + return self.service.get_weak_topics() diff --git a/ai-ml/weak_topic_detection/app/detectors/weak_topic_detector.py b/ai-ml/weak_topic_detection/app/detectors/weak_topic_detector.py index 4198212..f8839ee 100644 --- a/ai-ml/weak_topic_detection/app/detectors/weak_topic_detector.py +++ b/ai-ml/weak_topic_detection/app/detectors/weak_topic_detector.py @@ -1,5 +1,5 @@ from collections import defaultdict -from app.models.quiz_result import QuizResult +from weak_topic_detection.app.models.quiz_result import QuizResult class WeakTopicDetector: @@ -54,4 +54,4 @@ def detect(self, results: list[QuizResult]) -> list[dict]: # Weakest topics first weak_topics.sort(key=lambda item: item["accuracy"]) - return weak_topics \ No newline at end of file + return weak_topics diff --git a/ai-ml/weak_topic_detection/app/main.py b/ai-ml/weak_topic_detection/app/main.py new file mode 100644 index 0000000..09bbe67 --- /dev/null +++ b/ai-ml/weak_topic_detection/app/main.py @@ -0,0 +1,69 @@ +""" +Team Lambda Weak Topic Detection API — FastAPI service. + +Run from ai-ml/ (so weak_topic_detection.* and quiz_generator.* +imports resolve): + uvicorn weak_topic_detection.app.main:app --reload --port + +Interactive docs (once running): http://127.0.0.1:/docs +""" + +from fastapi import FastAPI, Depends +from fastapi.middleware.cors import CORSMiddleware + +# Reusing the JWT auth dependency already built for quiz_generator +# (Contract v1, Section 10) rather than duplicating it — same secret, +# same failure modes, one source of truth for how auth works. +from quiz_generator.app.auth import get_current_user_id + +from weak_topic_detection.app.services.weak_topic_service import WeakTopicService + +app = FastAPI(title="StudyMind Weak Topic Detection API — Team Lambda") + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:5173"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +_service: WeakTopicService | None = None + + +def get_service() -> WeakTopicService: + global _service + if _service is None: + _service = WeakTopicService() + return _service + + +@app.get("/health") +def health_check(): + return {"status": "ok"} + + +@app.get("/weak-topics") +def get_weak_topics_endpoint(user_id: str = Depends(get_current_user_id)): + """ + Requires "Authorization: Bearer ". user_id is derived from + the verified token and returned alongside the results so the + caller can confirm whose data this is. + + IMPORTANT — current limitation: WeakTopicService reads from a + static demo dataset (data/quiz_results.json), not live per-user + quiz submissions. The detection logic itself works correctly, but + it does not yet filter by user_id — every caller currently sees + the same demo results. Auth is enforced here so the endpoint's + contract (who can call it) is correct now; per-user quiz-result + ingestion is separate follow-up work once real quiz submissions + are wired in from the frontend. + """ + service = get_service() + weak_topics = service.get_weak_topics() + + return { + "success": True, + "user_id": user_id, + "weak_topics": weak_topics, + } \ No newline at end of file diff --git a/ai-ml/weak_topic_detection/app/services/weak_topic_service.py b/ai-ml/weak_topic_detection/app/services/weak_topic_service.py index f95b6d8..6146f23 100644 --- a/ai-ml/weak_topic_detection/app/services/weak_topic_service.py +++ b/ai-ml/weak_topic_detection/app/services/weak_topic_service.py @@ -1,7 +1,7 @@ -from app.detectors.weak_topic_detector import WeakTopicDetector -from app.models.quiz_result import QuizResult -from app.utils.data_loader import load_json_data -from app.config import WEAK_TOPIC_THRESHOLD, MIN_TOPIC_ATTEMPTS +from weak_topic_detection.app.detectors.weak_topic_detector import WeakTopicDetector +from weak_topic_detection.app.models.quiz_result import QuizResult +from weak_topic_detection.app.utils.data_loader import load_json_data +from weak_topic_detection.app.config import WEAK_TOPIC_THRESHOLD, MIN_TOPIC_ATTEMPTS class WeakTopicService: @@ -34,4 +34,4 @@ def get_weak_topics(self) -> list[dict]: results = self.load_results() - return self.detector.detect(results) \ No newline at end of file + return self.detector.detect(results) diff --git a/ai-ml/weak_topic_detection/app/validators/quiz_result_validator.py b/ai-ml/weak_topic_detection/app/validators/quiz_result_validator.py index fb3d48f..e7b1fc4 100644 --- a/ai-ml/weak_topic_detection/app/validators/quiz_result_validator.py +++ b/ai-ml/weak_topic_detection/app/validators/quiz_result_validator.py @@ -1,4 +1,4 @@ -from app.models.quiz_result import QuizResult +from weak_topic_detection.app.models.quiz_result import QuizResult class QuizResultValidator: @@ -33,4 +33,4 @@ def validate(cls, result: QuizResult) -> bool: if not result.date_taken: return False - return True \ No newline at end of file + return True