Grille : ordonner les créneaux de nuit après minuit selon la vraie journée

Un créneau type commençant avant la frontière du jour (le plus petit
`order`, ex. « Nuit » 00 h–03 h après « Soirée » 19 h–00 h) était généré
sur la même date calendaire que la soirée elle-même au lieu du lendemain,
et s'affichait donc avant 19 h/22 h/23 h dans la grille des besoins au
lieu d'après 23 h. Corrigé dans la génération des créneaux
(_shiftgen.py, même frontière que validation.engine._day_boundary) et
dans le regroupement/tri de la grille (planning.ts : `dayBoundary`
calculé depuis les créneaux types).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
AdamThomas
2026-09-17 22:59:17 +02:00
co-authored by Claude Sonnet 5
parent f565feb3bc
commit 4bcdafca96
6 changed files with 128 additions and 24 deletions
+18 -5
View File
@@ -12,10 +12,22 @@ def _dates(edition: Edition) -> list[dt.date]:
return [edition.start_date + dt.timedelta(days=i) for i in range(span + 1)]
def _window(day: dt.date, part: DayPart) -> tuple[dt.datetime, dt.datetime]:
start = dt.datetime.combine(day, part.start_time)
def _day_boundary(day_parts: list[DayPart]) -> dt.time:
"""Début du premier créneau type du jour (le plus petit `order`) — même
frontière que `validation.engine._day_boundary`. Un créneau type dont
l'heure de début tombe avant cette frontière est la suite de la nuit du
jour précédent (ex. `Nuit3` 00 h–01 h après `Soirée` 19 h–00 h) : il doit
être généré sur le jour *suivant*, pas sur `day` lui-même."""
if not day_parts:
return dt.time(0, 0)
return min(day_parts, key=lambda part: part.order).start_time
def _window(day: dt.date, part: DayPart, boundary: dt.time) -> tuple[dt.datetime, dt.datetime]:
actual_day = day + dt.timedelta(days=1) if part.start_time < boundary else day
start = dt.datetime.combine(actual_day, part.start_time)
end_day = (
day if part.end_time > part.start_time else day + dt.timedelta(days=1)
actual_day if part.end_time > part.start_time else actual_day + dt.timedelta(days=1)
)
return start, dt.datetime.combine(end_day, part.end_time)
@@ -28,6 +40,7 @@ def generate_missing_shifts(
day_parts = list(
session.exec(select(DayPart).where(DayPart.edition_id == edition.id))
)
boundary = _day_boundary(day_parts)
existing = {
(s.day_part_id, s.starts_at.date())
for s in session.exec(
@@ -37,9 +50,9 @@ def generate_missing_shifts(
created = 0
for day in _dates(edition):
for part in day_parts:
if (part.id, day) in existing:
start, end = _window(day, part, boundary)
if (part.id, start.date()) in existing:
continue
start, end = _window(day, part)
session.add(
Shift(
edition_id=edition.id,