Étape 2 : modèle de données complet et migration initiale

- Tables SQLModel : Edition, DayPart, Volunteer, Skill, Position, Shift,
  Availability, Assignment, Account, MagicLinkToken + tables d'association
- Multi-édition (edition_id partout), énumérations en VARCHAR + CHECK,
  suppressions ON DELETE CASCADE (droit à l'effacement)
- Dates/heures naïves en heure locale Europe/Paris (tzdata + TZ conteneur)
- Migration Alembic 0001 écrite à la main, vérifiée par `alembic check`
  (aucun écart avec les modèles) et par un aller-retour downgrade/upgrade
- Tests : graphe complet, unicité email par édition, N-N compétences,
  aller-retour d'énumération, cascade de suppression d'un bénévole

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
AdamThomas
2026-09-09 22:25:29 +02:00
co-authored by Claude Sonnet 5
parent 09d7d70076
commit cb2e84adf0
11 changed files with 1068 additions and 28 deletions
+60
View File
@@ -0,0 +1,60 @@
"""Énumérations du domaine.
Stockées en base comme VARCHAR + contrainte CHECK (`native_enum=False`) : pas de
type ENUM natif PostgreSQL, donc pas de migration de type à gérer.
"""
import enum
class Role(enum.StrEnum):
COORDINATOR = "COORDINATOR"
LEAD = "LEAD"
VOLUNTEER = "VOLUNTEER"
class AvailabilityStatus(enum.StrEnum):
AVAILABLE = "AVAILABLE"
IF_NEEDED = "IF_NEEDED"
UNAVAILABLE = "UNAVAILABLE"
class AssignmentStatus(enum.StrEnum):
PROPOSED = "PROPOSED"
CONFIRMED = "CONFIRMED"
DECLINED = "DECLINED"
CANCELLED = "CANCELLED"
class AssignmentSource(enum.StrEnum):
SOLVER = "SOLVER"
MANUAL = "MANUAL"
class PositionCategory(enum.StrEnum):
BAR = "BAR"
TICKETING = "TICKETING"
WELCOME = "WELCOME"
PARKING = "PARKING"
CLEANING = "CLEANING"
CATERING = "CATERING"
SETUP = "SETUP"
STAGE = "STAGE"
OTHER = "OTHER"
class TshirtSize(enum.StrEnum):
XS = "XS"
S = "S"
M = "M"
L = "L"
XL = "XL"
XXL = "XXL"
XXXL = "XXXL"
# Statuts d'affectation « actifs » : seuls ceux-ci comptent pour les règles de
# chevauchement, de cumul horaire et de repos (voir app/validation).
ACTIVE_ASSIGNMENT_STATUSES: frozenset[AssignmentStatus] = frozenset(
{AssignmentStatus.PROPOSED, AssignmentStatus.CONFIRMED}
)