from uuid import UUID from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import delete, select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.sql import func 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/{id}/volunteers", response_model=list[VolunteerResponse]) async def list_project_volunteers( id: UUID, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """List volunteers from project""" p = await session.get(Project, id) if p is None: raise HTTPException(status_code=404, detail="Project not found") results = await session.execute(select(Volunteer).where(Volunteer.project_id == id)) return results.scalars() @router.post("/project/{id}/volunteer", response_model=VolunteerResponse) async def create_volunteer( id: UUID, new_volunteer: VolunteerCreateRequest, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Create a new volunteer to the project""" p = await session.get(Project, id) if p is None: raise HTTPException(status_code=404, detail="Project not found") input_dict = new_volunteer.dict() # 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, id, Slot, "Invalid slot list") del input_dict["slots"] volunteer = Volunteer(project_id=id, **input_dict) session.add(volunteer) # commit to optain an id for the volunteer await session.commit() await session.execute( association_table_volunteer_slot.insert().values( [(volunteer.id, slot_id) for slot_id in slots] ) ) await 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: AsyncSession = Depends(deps.get_session), ): """Update a volunteer from the project""" volunteer = await session.get(Volunteer, volunteer_id) if (volunteer is None) or (volunteer.project_id != project_id): raise HTTPException(status_code=404, detail="Volunteer not found") input_dict = new_volunteer.dict() # Extract slots list from input dict if input_dict["slots"] is not None: slots: list[UUID] = input_dict["slots"] await verify_id_list(session, slots, project_id, Slot, "Invalid slot list") # Remove previous values await session.execute( association_table_volunteer_slot.delete().where( association_table_volunteer_slot.c.volunteer_id == volunteer.id ) ) # Add the new await 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) await session.commit() await 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: AsyncSession = Depends(deps.get_session), ): """Delete a volunteer from the project""" await session.execute( delete(Volunteer).where( (Volunteer.id == volunteer_id) & (Volunteer.project_id == project_id) ) ) await session.commit()