"""CRUD des créneaux types (`DayPart`) d'une édition. Un poste, à sa création, reçoit un créneau (besoin 0/0) par jour et par créneau type. Le `code` sert de clé stable pour l'import des disponibilités : il est figé après création.""" import uuid from fastapi import APIRouter, Depends, HTTPException, Response, status from sqlalchemy.exc import IntegrityError from sqlmodel import Session, select from app.api._common import owned_or_404 from app.api.deps import get_edition from app.api.schemas import DayPartCreate, DayPartRead, DayPartUpdate from app.auth.deps import current_account_in_edition, require_coordinator from app.db import get_session from app.importing.text import slug_code from app.models import Account, DayPart, Edition router = APIRouter( prefix="/editions/{edition_id}/day-parts", tags=["day-parts"] ) @router.get("", response_model=list[DayPartRead]) def list_day_parts( edition: Edition = Depends(get_edition), _account: Account = Depends(current_account_in_edition), session: Session = Depends(get_session), ) -> list[DayPart]: return list( session.exec( select(DayPart) .where(DayPart.edition_id == edition.id) .order_by(DayPart.order, DayPart.start_time) ) ) @router.post("", response_model=DayPartRead, status_code=status.HTTP_201_CREATED) def create_day_part( payload: DayPartCreate, edition: Edition = Depends(get_edition), _guard: Account = Depends(require_coordinator), session: Session = Depends(get_session), ) -> DayPart: code = (payload.code or slug_code(payload.label)).strip() if not code: raise HTTPException( status_code=422, detail={"code": "day_part.code_required"} ) day_part = DayPart( edition_id=edition.id, code=code, label=payload.label.strip(), start_time=payload.start_time, end_time=payload.end_time, order=payload.order, ) session.add(day_part) try: session.commit() except IntegrityError as exc: session.rollback() raise HTTPException( status_code=409, detail={"code": "day_part.duplicate_code"} ) from exc session.refresh(day_part) return day_part @router.patch("/{day_part_id}", response_model=DayPartRead) def update_day_part( day_part_id: uuid.UUID, payload: DayPartUpdate, edition: Edition = Depends(get_edition), _guard: Account = Depends(require_coordinator), session: Session = Depends(get_session), ) -> DayPart: day_part = owned_or_404(session.get(DayPart, day_part_id), edition.id) for field, value in payload.model_dump(exclude_unset=True).items(): setattr(day_part, field, value) session.commit() session.refresh(day_part) return day_part @router.delete("/{day_part_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_day_part( day_part_id: uuid.UUID, edition: Edition = Depends(get_edition), _guard: Account = Depends(require_coordinator), session: Session = Depends(get_session), ) -> Response: day_part = owned_or_404(session.get(DayPart, day_part_id), edition.id) # Cascade base : day_part_id des créneaux issus de ce type repasse à NULL. session.delete(day_part) session.commit() return Response(status_code=status.HTTP_204_NO_CONTENT)