from uuid import UUID from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.orm import Session from app.api import deps from app.api.utils import assert_project_exists_or_404 from app.models import Commission, CommissionMember, OrgRole, User from app.schemas.requests import ( CommissionCreateRequest, CommissionMembershipRequest, CommissionUpdateRequest, ) from app.schemas.responses import CommissionResponse router = APIRouter(prefix="/project/{project_id}", tags=["commissions"]) READ_COMMISSIONS = (OrgRole.ORG_ADMIN, OrgRole.RESPO_BENEVOLE, OrgRole.RESPO_COMMISSION) MANAGE_COMMISSIONS = (OrgRole.ORG_ADMIN, OrgRole.RESPO_BENEVOLE) def _get_commission_or_404(session: Session, project_id: UUID, commission_id: UUID) -> Commission: commission = session.get(Commission, commission_id) if commission is None or commission.project_id != str(project_id): raise HTTPException(status_code=404, detail="Commission not found") return commission @router.get("/commissions", response_model=list[CommissionResponse]) async def list_project_commissions( project_id: UUID, current_user: User = Depends(deps.require_org_role(*READ_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """List all commissions for a specific project""" assert_project_exists_or_404(session, project_id) results = session.execute(select(Commission).where(Commission.project_id == project_id)) return results.scalars().all() @router.post("/commission", response_model=CommissionResponse) async def create_commission( project_id: UUID, new_commission: CommissionCreateRequest, current_user: User = Depends(deps.require_org_role(*MANAGE_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """Create a new commission in the project""" assert_project_exists_or_404(session, project_id) commission = Commission(project_id=project_id, **new_commission.model_dump()) session.add(commission) session.commit() session.refresh(commission) return commission @router.get("/commission/{commission_id}", response_model=CommissionResponse) async def get_commission( project_id: UUID, commission_id: UUID, current_user: User = Depends(deps.require_org_role(*READ_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """Get a single commission""" return _get_commission_or_404(session, project_id, commission_id) @router.patch("/commission/{commission_id}", response_model=CommissionResponse) async def update_commission( project_id: UUID, commission_id: UUID, edit_commission: CommissionUpdateRequest, current_user: User = Depends(deps.require_org_role(*MANAGE_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """Edit a commission's details""" commission = _get_commission_or_404(session, project_id, commission_id) commission.name = edit_commission.name session.commit() session.refresh(commission) return commission @router.delete("/commission/{commission_id}") async def delete_commission( project_id: UUID, commission_id: UUID, current_user: User = Depends(deps.require_org_role(*MANAGE_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """Delete a commission (automatically deletes CommissionMember links via cascade)""" commission = _get_commission_or_404(session, project_id, commission_id) session.delete(commission) session.commit() @router.post("/commission/{commission_id}/members", response_model=CommissionResponse) async def add_members_to_commission( project_id: UUID, commission_id: UUID, payload: CommissionMembershipRequest, current_user: User = Depends(deps.require_org_role(*MANAGE_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """Bulk-add users as members to a commission (idempotent)""" commission = _get_commission_or_404(session, project_id, commission_id) # Verify users exist valid_users = session.scalars(select(User).where(User.id.in_(payload.user_ids))).all() if len(valid_users) != len(payload.user_ids): raise HTTPException(status_code=400, detail="One or more invalid user IDs") existing_user_ids = {str(m.user_id) for m in commission.members} for user in valid_users: if str(user.id) in existing_user_ids: continue new_member = CommissionMember(commission_id=commission.id, user_id=user.id) commission.members.append(new_member) session.commit() session.refresh(commission) return commission @router.delete("/commission/{commission_id}/member/{user_id}", response_model=CommissionResponse) async def remove_member_from_commission( project_id: UUID, commission_id: UUID, user_id: UUID, current_user: User = Depends(deps.require_org_role(*MANAGE_COMMISSIONS)), session: Session = Depends(deps.get_session), ): """Remove a single user from a commission""" commission = _get_commission_or_404(session, project_id, commission_id) member_to_remove = next((m for m in commission.members if str(m.user_id) == str(user_id)), None) if member_to_remove: session.delete(member_to_remove) session.commit() session.refresh(commission) return commission