Files
benevoles/backend/app/api/artists.py
T
AdamThomasandClaude Sonnet 5 24caece7df Bénévole : vœux d'artistes (entité Artist) et vœux de postes
Nouvelle entité `Artist` (nom + jour de passage), CRUD
`/editions/{id}/artists` (lecture ouverte à tout compte de l'édition,
écriture COORDINATION), unicité (édition, nom, date).

La fiche bénévole porte deux listes de vœux, sur le modèle de
`skill_codes` (None = inchangé, [] = vider) :
- `artist_ids` : artistes que le bénévole veut voir (`volunteer_artist`) ;
- `wished_position_ids` : postes qu'il voudrait tenir
  (`volunteer_position_wish`), distinct de l'affectation.

Un id étranger à l'édition renvoie 422 (`artist.unknown` /
`position.unknown`). Suppression d'un artiste : cascade sur les vœux.
Migration 0006. Jeu de démonstration et frontend inchangés (le front
suivra).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
2026-09-10 22:12:55 +02:00

109 lines
3.5 KiB
Python

"""CRUD de la programmation (`Artist`) d'une édition.
La coordination constitue la liste ; elle est présentée par jour de passage
(`performance_date`). Les bénévoles y choisissent les artistes qu'ils veulent
voir (vœux stockés dans `volunteer_artist`, exposés par la fiche bénévole)."""
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 ArtistCreate, ArtistRead, ArtistUpdate
from app.auth.deps import current_account_in_edition, require_coordinator
from app.db import get_session
from app.models import Account, Artist, Edition
router = APIRouter(prefix="/editions/{edition_id}/artists", tags=["artists"])
@router.get("", response_model=list[ArtistRead])
def list_artists(
edition: Edition = Depends(get_edition),
_account: Account = Depends(current_account_in_edition),
session: Session = Depends(get_session),
) -> list[Artist]:
return list(
session.exec(
select(Artist)
.where(Artist.edition_id == edition.id)
.order_by(Artist.performance_date, Artist.name)
)
)
@router.post("", response_model=ArtistRead, status_code=status.HTTP_201_CREATED)
def create_artist(
payload: ArtistCreate,
edition: Edition = Depends(get_edition),
_guard: Account = Depends(require_coordinator),
session: Session = Depends(get_session),
) -> Artist:
name = payload.name.strip()
if not name:
raise HTTPException(
status_code=422, detail={"code": "artist.name_required"}
)
artist = Artist(
edition_id=edition.id,
name=name,
performance_date=payload.performance_date,
)
session.add(artist)
try:
session.commit()
except IntegrityError as exc:
session.rollback()
raise HTTPException(
status_code=409, detail={"code": "artist.duplicate"}
) from exc
session.refresh(artist)
return artist
@router.patch("/{artist_id}", response_model=ArtistRead)
def update_artist(
artist_id: uuid.UUID,
payload: ArtistUpdate,
edition: Edition = Depends(get_edition),
_guard: Account = Depends(require_coordinator),
session: Session = Depends(get_session),
) -> Artist:
artist = owned_or_404(session.get(Artist, artist_id), edition.id)
changes = payload.model_dump(exclude_unset=True)
if "name" in changes:
name = (changes["name"] or "").strip()
if not name:
raise HTTPException(
status_code=422, detail={"code": "artist.name_required"}
)
changes["name"] = name
for field, value in changes.items():
setattr(artist, field, value)
try:
session.commit()
except IntegrityError as exc:
session.rollback()
raise HTTPException(
status_code=409, detail={"code": "artist.duplicate"}
) from exc
session.refresh(artist)
return artist
@router.delete("/{artist_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_artist(
artist_id: uuid.UUID,
edition: Edition = Depends(get_edition),
_guard: Account = Depends(require_coordinator),
session: Session = Depends(get_session),
) -> Response:
artist = owned_or_404(session.get(Artist, artist_id), edition.id)
# Cascade base : les vœux `volunteer_artist` sont supprimés.
session.delete(artist)
session.commit()
return Response(status_code=status.HTTP_204_NO_CONTENT)