Pointage enregistré et statistiques de présence
ci-cd / build-backend (push) Successful in 29s
ci-cd / build-frontend (push) Successful in 19s
ci-cd / deploy (push) Successful in 12s

Étape 58 : le présent / absent du référent est enregistré sur
l'affectation (Assignment.attendance + heure, migration 0030,
PUT .../assignments/{id}/attendance) au lieu de disparaître au
rechargement.

Étape 59 : section « Présence » de l'écran Statistiques — créneaux en
cours et créneaux terminés, par poste, rafraîchie toutes les 30 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AdamThomas
2026-10-28 08:06:32 +01:00
co-authored by Claude Opus 5.5
parent a37f6c2c41
commit c57c70569c
19 changed files with 600 additions and 22 deletions
+46
View File
@@ -187,3 +187,49 @@ def test_filled_slots_never_count_overstaffing(client, edition, session):
assert position["assigned"] == 3
assert position["filled"] == 2
assert position["ideal_slots"] == 4
def test_attendance_splits_past_and_live_shifts_per_position(
client, edition, session, monkeypatch
):
import app.api.stats as stats_api
from app.models import AttendanceStatus
# Maintenant : le 10 à 20:00 — 14h–18h terminé, 18h–22h en cours,
# 22h–02h à venir (ignoré).
monkeypatch.setattr(stats_api, "local_now", lambda: dt_at(10, 20))
bar = make_position(session, edition, name="Bar")
parking = make_position(session, edition, name="Parking")
past = make_shift(session, edition, bar, start=dt_at(10, 14), end=dt_at(10, 18))
live = make_shift(session, edition, bar, start=dt_at(10, 18), end=dt_at(10, 22))
later = make_shift(session, edition, parking, start=dt_at(10, 22), end=dt_at(11, 2))
def assign(shift, name, attendance=None):
volunteer = make_volunteer(session, edition, first_name=name)
session.add(
Assignment(
edition_id=edition.id,
volunteer_id=volunteer.id,
shift_id=shift.id,
attendance=attendance,
)
)
assign(past, "A", AttendanceStatus.PRESENT)
assign(past, "B", AttendanceStatus.ABSENT)
assign(past, "C")
assign(live, "D", AttendanceStatus.PRESENT)
assign(live, "E")
assign(later, "F", AttendanceStatus.PRESENT)
session.commit()
att = client.get(_url(edition)).json()["attendance"]
assert att["past"] == {"assigned": 3, "present": 1, "absent": 1, "unmarked": 1}
assert att["live"] == {"assigned": 2, "present": 1, "absent": 0, "unmarked": 1}
assert att["live_shifts"] == 1
# Parking n'a encore aucun créneau commencé : absent du détail.
(row,) = att["by_position"]
assert row["name"] == "Bar"
assert row["past"]["assigned"] == 3
assert row["live"]["present"] == 1
assert row["live_shifts"] == 1