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 assert_project_exists_or_404, update_object_from_payload, verify_id_list from app.models import ( OrgRole, Project, SlotTag, SlotTemplate, User, association_table_template_tags, ) from app.schemas.requests import TemplateCreateRequest, TemplateUpdateRequest from app.schemas.responses import TemplateResponse router = APIRouter( prefix="/project/{project_id}", ) READ_ROLES = (OrgRole.ORG_ADMIN, OrgRole.RESPO_BENEVOLE, OrgRole.RESPO_COMMISSION) WRITE_ROLES = (OrgRole.ORG_ADMIN, OrgRole.RESPO_BENEVOLE, OrgRole.RESPO_COMMISSION) @router.get("/templates", response_model=list[TemplateResponse]) async def list_project_templates( project_id: UUID, current_user: User = Depends(deps.require_org_role(*READ_ROLES)), session: Session = Depends(deps.get_session), ): """List slots from project""" p = session.get(Project, project_id) if p is None: raise HTTPException(status_code=404, detail="Project not found") return p.templates @router.post( "/template", response_model=TemplateResponse, openapi_extra={"sse_event": "template_created"} ) async def create_template( project_id: UUID, payload: TemplateCreateRequest, current_user: User = Depends(deps.require_org_role(*WRITE_ROLES)), session: Session = Depends(deps.get_session), ): """Create a new template to the project""" assert_project_exists_or_404(session, project_id) if payload.commission_id is not None: deps.assert_commission_ownership(session, current_user, project_id, payload.commission_id) template = SlotTemplate(project_id=project_id, title=payload.title) session.add(template) session.commit() dic = payload.model_dump(exclude_unset=True) if payload.tags is not None: del dic["tags"] if len(payload.tags) > 0: await verify_id_list( session, payload.tags, project_id, SlotTag, "Invalid template list" ) session.execute( association_table_template_tags.insert().values( [(template.id, t_id) for t_id in payload.tags] ) ) update_object_from_payload(template, dic) session.commit() return template @router.post( "/template/{template_id}", response_model=TemplateResponse, openapi_extra={"sse_event": "template_updated"}, ) async def update_template( project_id: UUID, template_id: UUID, payload: TemplateUpdateRequest, current_user: User = Depends(deps.require_org_role(*WRITE_ROLES)), session: Session = Depends(deps.get_session), ): """Update a template""" template = session.get(SlotTemplate, template_id) if (template is None) or (template.project_id != str(project_id)): raise HTTPException(status_code=404, detail="Template not found") if template.commission_id is not None: deps.assert_commission_ownership(session, current_user, project_id, template.commission_id) if payload.commission_id is not None: deps.assert_commission_ownership(session, current_user, project_id, payload.commission_id) input_dict = payload.model_dump(exclude_unset=True) if payload.tags is not None: # Remove previous keys in association table session.execute( association_table_template_tags.delete().where( association_table_template_tags.c.description_id == template.id ) ) if len(payload.tags) > 0: await verify_id_list( session, payload.tags, project_id, SlotTag, "Invalid template list" ) session.execute( association_table_template_tags.insert().values( [(template.id, t_id) for t_id in payload.tags] ) ) del input_dict["tags"] update_object_from_payload(template, input_dict) session.commit() session.refresh(template) return template @router.delete("/template/{template_id}", openapi_extra={"sse_event": "template_deleted"}) async def delete_template( project_id: UUID, template_id: UUID, current_user: User = Depends(deps.require_org_role(*WRITE_ROLES)), session: Session = Depends(deps.get_session), ): """Delete a slot from the project""" result = session.execute( select(SlotTemplate.id, SlotTemplate.commission_id).where( SlotTemplate.id == template_id, SlotTemplate.project_id == project_id ) ).first() if result is None: raise HTTPException(status_code=404, detail="Template not found") if result[1] is not None: deps.assert_commission_ownership(session, current_user, project_id, result[1]) session.execute( delete(SlotTemplate).where( (SlotTemplate.id == template_id) & (SlotTemplate.project_id == project_id) ) ) session.commit()