from fastapi import APIRouter, Depends, HTTPException, status from typing import List from models import SubjectCreate, SubjectUpdate, SubjectResponse, SubjectInDB, 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="/subjects", tags=["subjects"]) @router.post("", response_model=SubjectResponse, status_code=status.HTTP_201_CREATED) async def create_subject( subject: SubjectCreate, current_user: UserInDB = Depends(get_current_user) ): """Create a new subject""" db = get_database() subject_in_db = SubjectInDB( **subject.model_dump(), user_id=current_user.id ) subject_dict = subject_in_db.model_dump(by_alias=True) subject_dict["_id"] = ObjectId(subject_dict["_id"]) result = await db.subjects.insert_one(subject_dict) subject_dict["_id"] = str(result.inserted_id) return SubjectResponse(**subject_dict) @router.get("", response_model=List[SubjectResponse]) async def get_subjects(current_user: UserInDB = Depends(get_current_user)): """Get all subjects for the current user""" db = get_database() cursor = db.subjects.find({"user_id": current_user.id}) subjects = await cursor.to_list(length=None) for subject in subjects: subject["_id"] = str(subject["_id"]) return [SubjectResponse(**subject) for subject in subjects] @router.get("/{subject_id}", response_model=SubjectResponse) async def get_subject( subject_id: str, current_user: UserInDB = Depends(get_current_user) ): """Get a specific subject""" db = get_database() if not ObjectId.is_valid(subject_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid subject ID" ) subject = await db.subjects.find_one({ "_id": ObjectId(subject_id), "user_id": current_user.id }) if not subject: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Subject not found" ) subject["_id"] = str(subject["_id"]) return SubjectResponse(**subject) @router.put("/{subject_id}", response_model=SubjectResponse) async def update_subject( subject_id: str, subject_update: SubjectUpdate, current_user: UserInDB = Depends(get_current_user) ): """Update a subject""" db = get_database() if not ObjectId.is_valid(subject_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid subject ID" ) # Check if subject exists and belongs to user existing_subject = await db.subjects.find_one({ "_id": ObjectId(subject_id), "user_id": current_user.id }) if not existing_subject: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Subject not found" ) # Update only provided fields update_data = subject_update.model_dump(exclude_unset=True) if update_data: update_data["updated_at"] = datetime.utcnow() await db.subjects.update_one( {"_id": ObjectId(subject_id)}, {"$set": update_data} ) # Fetch updated subject updated_subject = await db.subjects.find_one({"_id": ObjectId(subject_id)}) updated_subject["_id"] = str(updated_subject["_id"]) return SubjectResponse(**updated_subject) @router.delete("/{subject_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_subject( subject_id: str, current_user: UserInDB = Depends(get_current_user) ): """Delete a subject and all associated grades""" db = get_database() if not ObjectId.is_valid(subject_id): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid subject ID" ) # Check if subject exists and belongs to user subject = await db.subjects.find_one({ "_id": ObjectId(subject_id), "user_id": current_user.id }) if not subject: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Subject not found" ) # Delete subject await db.subjects.delete_one({"_id": ObjectId(subject_id)}) # Delete all grades for this subject await db.grades.delete_many({"subject_id": subject_id, "user_id": current_user.id}) return None