from __future__ import annotations from typing import Any, Dict, List from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, status from sqlalchemy.orm import Session from app import models, schemas from app.api import deps from app.db.session import get_db from app.models.progress import ProgressStatus from app.services.lesson import lesson as lesson_service from app.services.progress import user_answer, user_progress from app.services.question import question as question_service router = APIRouter() @router.get("/user/{user_id}/stats", response_model=Dict[str, Any]) def get_user_stats( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get a user's progress statistics. """ # Regular users can only get their own stats if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) stats = user_progress.get_user_progress_stats(db, user_id=user_id) return stats @router.get("/user/{user_id}/lessons", response_model=List[schemas.UserProgress]) def get_user_lesson_progress( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), status: ProgressStatus = Query(None, description="Filter by progress status"), skip: int = 0, limit: int = 100, current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get a user's progress for all lessons. """ # Regular users can only get their own progress if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) if status: progress_items = user_progress.get_by_user_status( db, user_id=user_id, status=status, skip=skip, limit=limit ) else: progress_items = user_progress.get_by_user(db, user_id=user_id, skip=skip, limit=limit) return progress_items @router.get("/user/{user_id}/lessons/{lesson_id}", response_model=schemas.UserProgress) def get_user_lesson_progress_by_id( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), lesson_id: int = Path(..., gt=0), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get a user's progress for a specific lesson. """ # Regular users can only get their own progress if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) # Check if lesson exists lesson = lesson_service.get(db, id=lesson_id) if not lesson: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Lesson not found", ) progress = user_progress.get_by_user_lesson(db, user_id=user_id, lesson_id=lesson_id) if not progress: # Return default progress if none exists return { "id": None, "user_id": user_id, "lesson_id": lesson_id, "status": ProgressStatus.NOT_STARTED, "progress_percentage": 0, "points_earned": 0, "completed_at": None, "created_at": None, "updated_at": None, } return progress @router.post("/user/{user_id}/lessons/{lesson_id}/progress", response_model=schemas.UserProgress) def update_user_lesson_progress( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), lesson_id: int = Path(..., gt=0), progress_percentage: float = Body(..., ge=0, le=100), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Update a user's progress for a lesson. """ # Regular users can only update their own progress if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) # Check if lesson exists lesson = lesson_service.get(db, id=lesson_id) if not lesson: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Lesson not found", ) progress = user_progress.update_progress( db, user_id=user_id, lesson_id=lesson_id, progress_percentage=progress_percentage ) return progress @router.post("/user/{user_id}/lessons/{lesson_id}/complete", response_model=schemas.UserProgress) def complete_user_lesson( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), lesson_id: int = Path(..., gt=0), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Mark a lesson as completed for a user and award points. """ # Regular users can only update their own progress if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) # Check if lesson exists lesson = lesson_service.get(db, id=lesson_id) if not lesson: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Lesson not found", ) progress = user_progress.complete_lesson( db, user_id=user_id, lesson_id=lesson_id, points_earned=lesson.points ) return progress @router.post("/user/{user_id}/questions/{question_id}/answer", response_model=schemas.UserAnswer) def submit_answer( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), question_id: int = Path(..., gt=0), answer_id: int = Body(..., gt=0), time_taken_seconds: int = Body(None, ge=0), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Submit an answer to a question. """ # Regular users can only submit their own answers if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) # Check if question exists question = question_service.get(db, id=question_id) if not question: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Question not found", ) try: user_answer_obj = user_answer.submit_answer( db, user_id=user_id, question_id=question_id, answer_id=answer_id, time_taken_seconds=time_taken_seconds, ) return user_answer_obj except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e), ) @router.get( "/user/{user_id}/questions/{question_id}/answers", response_model=List[schemas.UserAnswer] ) def get_user_question_answers( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), question_id: int = Path(..., gt=0), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get a user's answers for a specific question. """ # Regular users can only get their own answers if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) # Check if question exists question = question_service.get(db, id=question_id) if not question: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Question not found", ) answers = user_answer.get_by_user_question(db, user_id=user_id, question_id=question_id) return answers @router.get("/user/{user_id}/quizzes/{quiz_id}/results", response_model=Dict[str, Any]) def get_quiz_results( *, db: Session = Depends(get_db), user_id: int = Path(..., gt=0), quiz_id: int = Path(..., gt=0), current_user: models.User = Depends(deps.get_current_active_user), ) -> Any: """ Get results for a user's quiz attempt. """ # Regular users can only get their own results if user_id != current_user.id and not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions", ) results = user_answer.get_quiz_results(db, user_id=user_id, quiz_id=quiz_id) return results