from uuid import UUID from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import delete, select from sqlalchemy.orm import Session from app.api import deps from app.api.utils import update_object_from_payload, verify_id_list from app.models import Project, Slot, User, Volunteer, association_table_volunteer_slot from app.schemas.requests import VolunteerCreateRequest, VolunteerUpdateRequest from app.schemas.responses import VolunteerResponse router = APIRouter() @router.get("/project/{project_id}/volunteers", response_model=list[VolunteerResponse]) async def list_project_volunteers( project_id: UUID, current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """List volunteers from project""" p = session.get(Project, project_id) if p is None: raise HTTPException(status_code=404, detail="Project not found") results = session.execute(select(Volunteer).where(Volunteer.project_id == project_id)) return results.scalars().all() @router.post("/project/{project_id}/volunteer", response_model=VolunteerResponse) async def create_volunteer( project_id: UUID, new_volunteer: VolunteerCreateRequest, current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """Create a new volunteer to the project""" p = session.get(Project, project_id) if p is None: raise HTTPException(status_code=404, detail="Project not found") input_dict = new_volunteer.model_dump() # Extract slots list from input dict slots: list[UUID] = [] if input_dict["slots"] is not None: slots = input_dict["slots"] await verify_id_list(session, slots, project_id, Slot, "Invalid slot list") del input_dict["slots"] volunteer = Volunteer(project_id=project_id, **input_dict) session.add(volunteer) # commit to optain an id for the volunteer session.commit() if len(slots) > 0: session.execute( association_table_volunteer_slot.insert().values( [(volunteer.id, slot_id) for slot_id in slots] ) ) session.commit() return volunteer @router.post("/project/{project_id}/volunteer/{volunteer_id}", response_model=VolunteerResponse) async def update_volunteer( project_id: UUID, volunteer_id: UUID, new_volunteer: VolunteerUpdateRequest, current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """Update a volunteer from the project""" volunteer = session.get(Volunteer, volunteer_id) if (volunteer is None) or (volunteer.project_id != str(project_id)): raise HTTPException(status_code=404, detail="Volunteer not found") input_dict = new_volunteer.model_dump(exclude_unset=True) # Extract slots list from input dict if "slots" in input_dict: slots: list[UUID] = input_dict["slots"] await verify_id_list(session, slots, project_id, Slot, "Invalid slot list") # Remove previous values session.execute( association_table_volunteer_slot.delete().where( association_table_volunteer_slot.c.volunteer_id == volunteer.id ) ) # Add the new slots if len(slots) > 0: session.execute( association_table_volunteer_slot.insert().values( [(volunteer.id, slot_id) for slot_id in slots] ) ) del input_dict["slots"] update_object_from_payload(volunteer, input_dict) session.commit() session.refresh(volunteer) return volunteer @router.delete("/project/{project_id}/volunteer/{volunteer_id}") async def delete_volunteer( project_id: UUID, volunteer_id: UUID, current_user: User = Depends(deps.get_current_user), session: Session = Depends(deps.get_session), ): """Delete a volunteer from the project""" session.execute( delete(Volunteer).where( (Volunteer.id == volunteer_id) & (Volunteer.project_id == project_id) ) ) session.commit()