Let a coach schedule a line-up to publish itself later

Publish now still works exactly as before (default action), but a coach
can also pick a future date/time -- events.services.lineup.
schedule_lineup_publish sets Lineup.scheduled_publish_at, and a new
periodic task (events.tasks.publish_scheduled_lineups, every 15 minutes)
publishes it once that time arrives via the same publish_lineup used for a
manual publish, so selection/Attendance/notifications all work identically
either way. A pending schedule can be cancelled or published early from the
same screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
2026-08-22 20:52:22 +02:00
parent 3b634fda22
commit 1d0c2ef299
11 changed files with 288 additions and 16 deletions

View File

@@ -36,9 +36,15 @@ def publish_lineup(lineup):
previously unused anywhere. Every selected member becomes SELECTED; every
other member with an Attendance row for this event who was actually
available (not out/silent, see UNAVAILABLE_STATUSES) becomes
NOT_SELECTED. Notifies only the selected players."""
NOT_SELECTED. Notifies only the selected players.
Clears scheduled_publish_at regardless of whether this run came from a
coach tapping Publish directly or from the scheduled sweep (events.tasks.
publish_scheduled_lineups) catching a due one -- once published, there's
nothing left pending either way."""
lineup.published_at = timezone.now()
lineup.save(update_fields=["published_at"])
lineup.scheduled_publish_at = None
lineup.save(update_fields=["published_at", "scheduled_publish_at"])
selected_member_ids = set(LineupSelection.objects.filter(lineup=lineup).values_list("member_id", flat=True))
@@ -53,6 +59,24 @@ def publish_lineup(lineup):
return lineup
def schedule_lineup_publish(lineup, when):
"""Sets ``lineup`` to publish itself automatically at ``when`` (an aware
datetime in the future) instead of waiting for a manual Publish tap --
events.tasks.publish_scheduled_lineups is the periodic sweep that
actually calls publish_lineup once that time arrives. Overwrites any
previous schedule rather than erroring -- picking a new time is the
whole point of letting a coach come back and adjust it."""
lineup.scheduled_publish_at = when
lineup.save(update_fields=["scheduled_publish_at"])
return lineup
def cancel_scheduled_publish(lineup):
lineup.scheduled_publish_at = None
lineup.save(update_fields=["scheduled_publish_at"])
return lineup
def selected_members_by_position(lineup):
"""Every LineupSelection for ``lineup``, grouped by the member's roster
position for the lineup's own team/season ("category") -- the read side