Jeu de démo : programmation et vœux des bénévoles (manquaient)
Le seed créait déjà des disponibilités, mais aucun Artist ni vœu (VolunteerPositionWish, VolunteerArtist) — la démo ne montrait donc rien du solveur ni des vœux : postes/artistes jamais choisis, programmation vide. - 3 artistes par jour de festival (pas montage/démontage), horaires échelonnés en soirée (18h-19h / 19h30-21h / 21h30-23h). - ~40 % des bénévoles ont un vœu de poste ; ~15 % de chance par artiste d'être un vœu (~1 par bénévole en moyenne sur 6 artistes). - Summary affiche désormais artists / position_wishes / artist_wishes ; tests étendus (présence + déterminisme). Vérifié en conditions réelles (`python -m app.seed` + appels API) : 6 artistes, 47 vœux de poste, 95 vœux d'artiste sur 120 bénévoles ; le solveur tourne toujours en ~0,3 s dessus. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
304b3c1d9e
commit
282be85498
@@ -19,6 +19,7 @@ from app.db import engine
|
||||
from app.importing.text import normalize
|
||||
from app.models import (
|
||||
Account,
|
||||
Artist,
|
||||
Assignment,
|
||||
AssignmentSource,
|
||||
Availability,
|
||||
@@ -29,6 +30,8 @@ from app.models import (
|
||||
Role,
|
||||
Shift,
|
||||
Volunteer,
|
||||
VolunteerArtist,
|
||||
VolunteerPositionWish,
|
||||
)
|
||||
from app.validation import (
|
||||
AvailabilityWindow,
|
||||
@@ -188,6 +191,17 @@ _STAFF: dict[PositionCategory, tuple[int, int]] = {
|
||||
_C.OTHER: (1, 2),
|
||||
}
|
||||
|
||||
# Programmation : trois passages par jour de festival (pas montage/démontage).
|
||||
ARTIST_NAMES = [
|
||||
"La Battante", "Orchestre Miroir", "Kntongo", "Louve",
|
||||
"Les Frères Vent", "Nuage Bleu", "Sœur Machine", "Comète",
|
||||
]
|
||||
ARTIST_SLOTS: list[tuple[dt.time, dt.time]] = [
|
||||
(dt.time(18, 0), dt.time(19, 0)),
|
||||
(dt.time(19, 30), dt.time(21, 0)),
|
||||
(dt.time(21, 30), dt.time(23, 0)),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Summary:
|
||||
@@ -197,6 +211,9 @@ class Summary:
|
||||
shifts: int = 0
|
||||
understaffed_shifts: int = 0
|
||||
assignments: int = 0
|
||||
artists: int = 0
|
||||
position_wishes: int = 0
|
||||
artist_wishes: int = 0
|
||||
warnings: Counter = field(default_factory=Counter)
|
||||
blocking: int = 0
|
||||
|
||||
@@ -209,6 +226,9 @@ class Summary:
|
||||
f" Créneaux : {self.shifts}",
|
||||
f" Créneaux sous l'effectif minimal (trous) : {self.understaffed_shifts}",
|
||||
f" Affectations : {self.assignments}",
|
||||
f" Artistes programmés : {self.artists}",
|
||||
f" Vœux de poste déclarés : {self.position_wishes}",
|
||||
f" Vœux d'artiste déclarés : {self.artist_wishes}",
|
||||
f" Avertissements actifs : {sum(self.warnings.values())}",
|
||||
]
|
||||
for code, count in sorted(self.warnings.items()):
|
||||
@@ -316,8 +336,26 @@ def build(session: Session, *, rng: random.Random | None = None) -> Summary:
|
||||
dates[3]: "demontage",
|
||||
}
|
||||
|
||||
artists: list[Artist] = []
|
||||
for day in dates:
|
||||
if phase_of[day] != "festival":
|
||||
continue
|
||||
for start, end in ARTIST_SLOTS:
|
||||
artist = Artist(
|
||||
edition_id=edition.id,
|
||||
name=ARTIST_NAMES[len(artists) % len(ARTIST_NAMES)],
|
||||
performance_date=day,
|
||||
start_time=start,
|
||||
end_time=end,
|
||||
)
|
||||
session.add(artist)
|
||||
artists.append(artist)
|
||||
session.flush()
|
||||
|
||||
volunteers: list[Volunteer] = []
|
||||
used_names: set[tuple[str, str]] = set()
|
||||
n_position_wishes = 0
|
||||
n_artist_wishes = 0
|
||||
for i in range(120):
|
||||
while True:
|
||||
first = rng.choice(FIRST_NAMES)
|
||||
@@ -345,6 +383,21 @@ def build(session: Session, *, rng: random.Random | None = None) -> Summary:
|
||||
volunteer_id=volunteer.id,
|
||||
)
|
||||
)
|
||||
# Vœux : poste qu'il voudrait tenir, artistes qu'il veut voir.
|
||||
if rng.random() < 0.4:
|
||||
session.add(
|
||||
VolunteerPositionWish(
|
||||
volunteer_id=volunteer.id,
|
||||
position_id=rng.choice(positions).id,
|
||||
)
|
||||
)
|
||||
n_position_wishes += 1
|
||||
for artist in artists:
|
||||
if rng.random() < 0.15:
|
||||
session.add(
|
||||
VolunteerArtist(volunteer_id=volunteer.id, artist_id=artist.id)
|
||||
)
|
||||
n_artist_wishes += 1
|
||||
# Disponibilités : chaque bénévole déclare, pour ~80 % des jours, une
|
||||
# large fenêtre couvrant la journée et la nuit (06:00 → 04:00 le lendemain).
|
||||
_FULL_DAY = (dt.time(6, 0), dt.time(4, 0))
|
||||
@@ -463,6 +516,9 @@ def build(session: Session, *, rng: random.Random | None = None) -> Summary:
|
||||
shifts=len(shifts),
|
||||
assignments=n_assignments,
|
||||
understaffed_shifts=sum(1 for shift in shifts if fill[shift.id] < shift.min_staff),
|
||||
artists=len(artists),
|
||||
position_wishes=n_position_wishes,
|
||||
artist_wishes=n_artist_wishes,
|
||||
)
|
||||
_verify(session, edition, summary)
|
||||
return summary
|
||||
|
||||
Reference in New Issue
Block a user