from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from typing import Optional from .. import crud, schemas, auth from ..database import get_db from ..models import User router = APIRouter(prefix="/learn", tags=["learning"]) @router.get("/steps/{step_id}", response_model=schemas.SuccessResponse) def get_step_content( step_id: int, db: Session = Depends(get_db), current_user: Optional[User] = Depends(auth.get_optional_current_user) ): step = crud.get_step_by_id(db, step_id=step_id) if not step: raise HTTPException(status_code=404, detail="Step not found") lesson = crud.get_lesson_by_id(db, step.lesson_id) if not lesson: raise HTTPException(status_code=404, detail="Lesson not found") module = crud.get_module_by_id(db, lesson.module_id) if not module: raise HTTPException(status_code=404, detail="Module not found") from ..models import Course course = db.query(Course).filter(Course.id == module.course_id).first() if not course: raise HTTPException(status_code=404, detail="Course not found") if course.status != 'published': if current_user is None or course.author_id != current_user.id: raise HTTPException(status_code=403, detail="Not authorized") return schemas.SuccessResponse(data={ "step": { "id": step.id, "step_type": step.step_type, "title": step.title, "content": step.content, "content_html": step.content_html, "file_url": step.file_url, "test_settings": step.test_settings, "order_index": step.order_index } }) @router.get("/steps/{step_id}/test", response_model=schemas.SuccessResponse) def get_test_for_viewing( step_id: int, db: Session = Depends(get_db), current_user: User = Depends(auth.get_current_user) ): """ Get test questions for viewing (without correct answers) """ step = crud.get_step_by_id(db, step_id) if not step: raise HTTPException(status_code=404, detail="Step not found") if step.step_type != 'test': raise HTTPException(status_code=400, detail="Step is not a test") questions = crud.get_test_questions(db, step_id) # Prepare questions without correct answers questions_for_viewer = [] for q in questions: question_data = { "id": q.id, "question_text": q.question_text, "question_type": q.question_type, "points": q.points, "order_index": q.order_index } if q.question_type in ['single_choice', 'multiple_choice']: # Return answers without is_correct flag question_data["answers"] = [ {"id": a.id, "answer_text": a.answer_text, "order_index": a.order_index} for a in q.answers ] elif q.question_type == 'match': # Return match items shuffled question_data["match_items"] = [ {"id": m.id, "left_text": m.left_text, "right_text": m.right_text, "order_index": m.order_index} for m in q.match_items ] questions_for_viewer.append(question_data) # Get user's previous attempts attempts = crud.get_test_attempts(db, current_user.id, step_id) attempts_data = [ { "id": a.id, "score": float(a.score), "max_score": float(a.max_score), "passed": a.passed, "completed_at": a.completed_at.isoformat() if a.completed_at else None } for a in attempts ] return schemas.SuccessResponse(data={ "questions": questions_for_viewer, "settings": step.test_settings, "attempts": attempts_data }) @router.post("/steps/{step_id}/test/submit", response_model=schemas.SuccessResponse) def submit_test( step_id: int, attempt: schemas.TestAttemptCreate, db: Session = Depends(get_db), current_user: User = Depends(auth.get_current_user) ): """ Submit test answers and get results """ step = crud.get_step_by_id(db, step_id) if not step: raise HTTPException(status_code=404, detail="Step not found") if step.step_type != 'test': raise HTTPException(status_code=400, detail="Step is not a test") # Check max attempts limit settings = step.test_settings or {} max_attempts = settings.get('max_attempts', 0) if max_attempts > 0: attempts = crud.get_test_attempts(db, current_user.id, step_id) if len(attempts) >= max_attempts: raise HTTPException(status_code=400, detail="Maximum attempts reached") # Create attempt and calculate score test_attempt = crud.create_test_attempt(db, current_user.id, step_id, attempt.answers) if not test_attempt: raise HTTPException(status_code=500, detail="Failed to create test attempt") return schemas.SuccessResponse(data={ "attempt": { "id": test_attempt.id, "score": float(test_attempt.score), "max_score": float(test_attempt.max_score), "passed": test_attempt.passed, "completed_at": test_attempt.completed_at.isoformat() if test_attempt.completed_at else None } }) @router.get("/steps/{step_id}/test/attempts", response_model=schemas.SuccessResponse) def get_test_attempts( step_id: int, db: Session = Depends(get_db), current_user: User = Depends(auth.get_current_user) ): """ Get user's test attempts history """ step = crud.get_step_by_id(db, step_id) if not step: raise HTTPException(status_code=404, detail="Step not found") attempts = crud.get_test_attempts(db, current_user.id, step_id) attempts_data = [ { "id": a.id, "score": float(a.score), "max_score": float(a.max_score), "passed": a.passed, "completed_at": a.completed_at.isoformat() if a.completed_at else None } for a in attempts ] return schemas.SuccessResponse(data={"attempts": attempts_data})