from typing import Any, List, Optional from fastapi import APIRouter, Depends, HTTPException, Path from sqlalchemy.orm import Session from app import crud, models, schemas from app.api.v1.deps import get_db, get_current_active_user, get_teacher_user router = APIRouter() @router.get("/", response_model=List[schemas.QuestionOption]) def read_options( db: Session = Depends(get_db), question_id: Optional[int] = None, current_user: models.User = Depends(get_current_active_user), ) -> Any: """ Retrieve question options. """ if question_id: options = crud.question_option.get_by_question_id(db, question_id=question_id) else: options = crud.question_option.get_multi(db) return options @router.post("/", response_model=schemas.QuestionOption) def create_option( *, db: Session = Depends(get_db), option_in: schemas.QuestionOptionCreate, current_user: models.User = Depends(get_teacher_user), ) -> Any: """ Create new question option. """ # Check if the question exists question = crud.question.get(db=db, id=option_in.question_id) if not question: raise HTTPException( status_code=404, detail=f"Question with ID {option_in.question_id} not found", ) # Check if the user is a teacher of this course or an admin is_admin = any(role.name == "admin" for role in db.query(models.Role).all() if role.id == current_user.role_id) if not is_admin: exam = db.query(models.Exam).filter(models.Exam.id == question.exam_id).first() if not exam: raise HTTPException( status_code=404, detail="Exam not found", ) course = db.query(models.Course).filter(models.Course.id == exam.course_id).first() teacher = db.query(models.Teacher).filter(models.Teacher.user_id == current_user.id).first() if not teacher or not course or teacher.id != course.teacher_id: raise HTTPException( status_code=403, detail="You don't have permission to create an option for this question", ) # If this is marked as correct, check if there are other correct options if option_in.is_correct and question.question_type != schemas.QuestionType.MULTIPLE_CHOICE: existing_correct = crud.question_option.get_correct_option(db=db, question_id=question.id) if existing_correct: # For non-multiple-choice questions, only one option can be correct raise HTTPException( status_code=400, detail=f"This question type ({question.question_type}) can only have one correct option", ) option = crud.question_option.create(db=db, obj_in=option_in) return option @router.get("/{option_id}", response_model=schemas.QuestionOption) def read_option( *, db: Session = Depends(get_db), option_id: int = Path(..., title="The ID of the option to get"), current_user: models.User = Depends(get_current_active_user), ) -> Any: """ Get question option by ID. """ option = crud.question_option.get(db=db, id=option_id) if not option: raise HTTPException( status_code=404, detail="Question option not found", ) return option @router.put("/{option_id}", response_model=schemas.QuestionOption) def update_option( *, db: Session = Depends(get_db), option_id: int = Path(..., title="The ID of the option to update"), option_in: schemas.QuestionOptionUpdate, current_user: models.User = Depends(get_teacher_user), ) -> Any: """ Update a question option. """ option = crud.question_option.get(db=db, id=option_id) if not option: raise HTTPException( status_code=404, detail="Question option not found", ) # Check if the user is a teacher of this course or an admin is_admin = any(role.name == "admin" for role in db.query(models.Role).all() if role.id == current_user.role_id) if not is_admin: question = db.query(models.Question).filter(models.Question.id == option.question_id).first() if not question: raise HTTPException( status_code=404, detail="Question not found", ) exam = db.query(models.Exam).filter(models.Exam.id == question.exam_id).first() if not exam: raise HTTPException( status_code=404, detail="Exam not found", ) course = db.query(models.Course).filter(models.Course.id == exam.course_id).first() teacher = db.query(models.Teacher).filter(models.Teacher.user_id == current_user.id).first() if not teacher or not course or teacher.id != course.teacher_id: raise HTTPException( status_code=403, detail="You don't have permission to update this question option", ) # If changing to correct, check if there are other correct options if option_in.is_correct is not None and option_in.is_correct: question = db.query(models.Question).filter(models.Question.id == option.question_id).first() if question and question.question_type != schemas.QuestionType.MULTIPLE_CHOICE: existing_correct = crud.question_option.get_correct_option(db=db, question_id=question.id) if existing_correct and existing_correct.id != option_id: # For non-multiple-choice questions, only one option can be correct raise HTTPException( status_code=400, detail=f"This question type ({question.question_type}) can only have one correct option", ) option = crud.question_option.update(db=db, db_obj=option, obj_in=option_in) return option @router.delete("/{option_id}", response_model=schemas.QuestionOption) def delete_option( *, db: Session = Depends(get_db), option_id: int = Path(..., title="The ID of the option to delete"), current_user: models.User = Depends(get_teacher_user), ) -> Any: """ Delete a question option. """ option = crud.question_option.get(db=db, id=option_id) if not option: raise HTTPException( status_code=404, detail="Question option not found", ) # Check if the user is a teacher of this course or an admin is_admin = any(role.name == "admin" for role in db.query(models.Role).all() if role.id == current_user.role_id) if not is_admin: question = db.query(models.Question).filter(models.Question.id == option.question_id).first() if not question: raise HTTPException( status_code=404, detail="Question not found", ) exam = db.query(models.Exam).filter(models.Exam.id == question.exam_id).first() if not exam: raise HTTPException( status_code=404, detail="Exam not found", ) course = db.query(models.Course).filter(models.Course.id == exam.course_id).first() teacher = db.query(models.Teacher).filter(models.Teacher.user_id == current_user.id).first() if not teacher or not course or teacher.id != course.teacher_id: raise HTTPException( status_code=403, detail="You don't have permission to delete this question option", ) option = crud.question_option.remove(db=db, id=option_id) return option