from typing import Optional from fastapi import APIRouter, HTTPException, Query, status from sqlalchemy.exc import IntegrityError from sqlmodel import Session, select from app.db import SessionDep from app.dependencies import ( CurrentSuperuser, CurrentUser, get_participant_in_organization, get_route_in_organization, ) from app.models.participant import Participant, ParticipantCreate, ParticipantPublic, ParticipantUpdate from app.models.route import Route router = APIRouter(prefix="/participants", tags=["participants"]) def _commit_or_conflict(session: Session) -> None: try: session.commit() except IntegrityError as exc: session.rollback() if "uq_participant_route_email" in str(getattr(exc, "orig", exc)): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="A participant with this email already exists on this route", ) from exc raise @router.post("", response_model=ParticipantPublic, status_code=status.HTTP_201_CREATED) def create_participant(payload: ParticipantCreate, session: SessionDep, current_user: CurrentUser) -> Participant: get_route_in_organization(session, payload.route_id, current_user.organization_id) participant = Participant.model_validate(payload) session.add(participant) _commit_or_conflict(session) session.refresh(participant) return participant @router.get("", response_model=list[ParticipantPublic]) def list_participants( session: SessionDep, current_user: CurrentUser, route_id: Optional[int] = None, is_active: Optional[bool] = None, limit: int = Query(default=100, le=500, gt=0), offset: int = Query(default=0, ge=0), ) -> list[Participant]: statement = select(Participant).join(Route).where(Route.organization_id == current_user.organization_id) if route_id is not None: statement = statement.where(Participant.route_id == route_id) if is_active is not None: statement = statement.where(Participant.is_active == is_active) statement = statement.order_by(Participant.id).offset(offset).limit(limit) return list(session.exec(statement).all()) @router.get("/{participant_id}", response_model=ParticipantPublic) def get_participant(participant_id: int, session: SessionDep, current_user: CurrentUser) -> Participant: return get_participant_in_organization(session, participant_id, current_user.organization_id) @router.patch("/{participant_id}", response_model=ParticipantPublic) def update_participant( participant_id: int, payload: ParticipantUpdate, session: SessionDep, current_user: CurrentUser ) -> Participant: participant = get_participant_in_organization(session, participant_id, current_user.organization_id) for field, value in payload.model_dump(exclude_unset=True).items(): setattr(participant, field, value) session.add(participant) _commit_or_conflict(session) session.refresh(participant) return participant @router.delete("/{participant_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_participant(participant_id: int, session: SessionDep, current_user: CurrentSuperuser) -> None: participant = get_participant_in_organization(session, participant_id, current_user.organization_id) session.delete(participant) session.commit()