from fastapi import APIRouter, Depends, HTTPException, status, Query from typing import List, Optional from models import TeacherGradeCreate, TeacherGradeUpdate, TeacherGradeResponse, TeacherGradeInDB, UserInDB from database import get_database from routes.auth_routes import get_current_user from bson import ObjectId from datetime import datetime router = APIRouter(prefix="/teacher-grades", tags=["teacher-grades"]) @router.post("", response_model=TeacherGradeResponse, status_code=status.HTTP_201_CREATED) async def create_teacher_grade( teacher_grade: TeacherGradeCreate, current_user: UserInDB = Depends(get_current_user) ): """Create or update a teacher-provided grade override for a subject in a period""" db = get_database() # Verify subject exists and belongs to user if not ObjectId.is_valid(teacher_grade.subject_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid subject ID" ) subject = await db.subjects.find_one({ "_id": ObjectId(teacher_grade.subject_id), "user_id": current_user.id }) if not subject: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Subject not found" ) # Verify period exists and belongs to user if not ObjectId.is_valid(teacher_grade.period_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid period ID" ) period = await db.periods.find_one({ "_id": ObjectId(teacher_grade.period_id), "user_id": current_user.id }) if not period: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Period not found" ) # Check if teacher grade already exists for this subject and period existing = await db.teacher_grades.find_one({ "subject_id": teacher_grade.subject_id, "period_id": teacher_grade.period_id, "user_id": current_user.id }) if existing: # Update existing teacher grade update_data = teacher_grade.model_dump() update_data["updated_at"] = datetime.utcnow() await db.teacher_grades.update_one( {"_id": existing["_id"]}, {"$set": update_data} ) updated = await db.teacher_grades.find_one({"_id": existing["_id"]}) updated["_id"] = str(updated["_id"]) return TeacherGradeResponse(**updated) else: # Create new teacher grade teacher_grade_in_db = TeacherGradeInDB( **teacher_grade.model_dump(), user_id=current_user.id ) grade_dict = teacher_grade_in_db.model_dump(by_alias=True) grade_dict["_id"] = ObjectId(grade_dict["_id"]) result = await db.teacher_grades.insert_one(grade_dict) grade_dict["_id"] = str(result.inserted_id) return TeacherGradeResponse(**grade_dict) @router.get("", response_model=List[TeacherGradeResponse]) async def get_teacher_grades( subject_id: Optional[str] = Query(None), period_id: Optional[str] = Query(None), current_user: UserInDB = Depends(get_current_user) ): """Get all teacher grades for the current user, optionally filtered by subject or period""" db = get_database() query = {"user_id": current_user.id} if subject_id: if not ObjectId.is_valid(subject_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid subject ID" ) query["subject_id"] = subject_id if period_id: if not ObjectId.is_valid(period_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid period ID" ) query["period_id"] = period_id cursor = db.teacher_grades.find(query) teacher_grades = await cursor.to_list(length=None) for grade in teacher_grades: grade["_id"] = str(grade["_id"]) return [TeacherGradeResponse(**grade) for grade in teacher_grades] @router.get("/{teacher_grade_id}", response_model=TeacherGradeResponse) async def get_teacher_grade( teacher_grade_id: str, current_user: UserInDB = Depends(get_current_user) ): """Get a specific teacher grade""" db = get_database() if not ObjectId.is_valid(teacher_grade_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid teacher grade ID" ) teacher_grade = await db.teacher_grades.find_one({ "_id": ObjectId(teacher_grade_id), "user_id": current_user.id }) if not teacher_grade: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Teacher grade not found" ) teacher_grade["_id"] = str(teacher_grade["_id"]) return TeacherGradeResponse(**teacher_grade) @router.delete("/{teacher_grade_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_teacher_grade( teacher_grade_id: str, current_user: UserInDB = Depends(get_current_user) ): """Delete a teacher grade override""" db = get_database() if not ObjectId.is_valid(teacher_grade_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid teacher grade ID" ) # Check if teacher grade exists and belongs to user teacher_grade = await db.teacher_grades.find_one({ "_id": ObjectId(teacher_grade_id), "user_id": current_user.id }) if not teacher_grade: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Teacher grade not found" ) # Delete teacher grade await db.teacher_grades.delete_one({"_id": ObjectId(teacher_grade_id)}) return None