"""CRUD des créneaux.""" import uuid from fastapi import APIRouter, Depends, HTTPException, Response, status 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 ShiftCreate, ShiftRead, ShiftUpdate from app.auth.deps import ( current_account_in_edition, ensure_can_write_position, ensure_can_write_shift, require_staff, ) from app.db import get_session from app.models import Account, Edition, Position, Shift router = APIRouter(prefix="/editions/{edition_id}/shifts", tags=["shifts"]) def _read(shift: Shift) -> ShiftRead: return ShiftRead(**shift.model_dump()) def _check_range(starts_at, ends_at) -> None: if ends_at <= starts_at: raise HTTPException( status_code=422, detail={"code": "shift.ends_not_after_starts"}, ) def _require_position( session: Session, position_id: uuid.UUID, edition_id: uuid.UUID ) -> Position: position = session.get(Position, position_id) if position is None or position.edition_id != edition_id: raise HTTPException( status_code=422, detail={"code": "shift.position_wrong_edition"} ) return position @router.post("", response_model=ShiftRead, status_code=status.HTTP_201_CREATED) def create_shift( payload: ShiftCreate, edition: Edition = Depends(get_edition), account: Account = Depends(current_account_in_edition), session: Session = Depends(get_session), ) -> ShiftRead: _check_range(payload.starts_at, payload.ends_at) position = _require_position(session, payload.position_id, edition.id) ensure_can_write_position(account, position) shift = Shift(edition_id=edition.id, **payload.model_dump()) session.add(shift) session.commit() session.refresh(shift) return _read(shift) @router.get("", response_model=list[ShiftRead]) def list_shifts( position_id: uuid.UUID | None = None, edition: Edition = Depends(get_edition), _guard: Account = Depends(require_staff), session: Session = Depends(get_session), ) -> list[ShiftRead]: stmt = ( select(Shift) .where(Shift.edition_id == edition.id) .order_by(Shift.starts_at) ) if position_id is not None: stmt = stmt.where(Shift.position_id == position_id) return [_read(s) for s in session.exec(stmt)] @router.get("/{shift_id}", response_model=ShiftRead) def get_shift( shift_id: uuid.UUID, edition: Edition = Depends(get_edition), _guard: Account = Depends(require_staff), session: Session = Depends(get_session), ) -> ShiftRead: shift = owned_or_404(session.get(Shift, shift_id), edition.id) return _read(shift) @router.patch("/{shift_id}", response_model=ShiftRead) def update_shift( shift_id: uuid.UUID, payload: ShiftUpdate, edition: Edition = Depends(get_edition), account: Account = Depends(current_account_in_edition), session: Session = Depends(get_session), ) -> ShiftRead: shift = owned_or_404(session.get(Shift, shift_id), edition.id) ensure_can_write_shift(account, session, shift) changes = payload.model_dump(exclude_unset=True) if "position_id" in changes: target = _require_position(session, changes["position_id"], edition.id) ensure_can_write_position(account, target) for field, value in changes.items(): setattr(shift, field, value) _check_range(shift.starts_at, shift.ends_at) session.commit() session.refresh(shift) return _read(shift) @router.delete("/{shift_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_shift( shift_id: uuid.UUID, edition: Edition = Depends(get_edition), account: Account = Depends(current_account_in_edition), session: Session = Depends(get_session), ) -> Response: shift = owned_or_404(session.get(Shift, shift_id), edition.id) ensure_can_write_shift(account, session, shift) session.delete(shift) session.commit() return Response(status_code=status.HTTP_204_NO_CONTENT)