Line-up: show each player's turnout rate, notify on changes after publish
A "yes" to this game doesn't say how reliably a player actually shows up -- each row on the line-up screen now shows this season's turnout rate (events.services.attendance.player_attendance_rankings, one query for the whole team), color-coded, omitted for anyone with too little history to mean anything. Also: editing a published line-up and hitting "Save" never notified anyone (it only ever wrote LineupSelection, never touched Attendance.status) -- "Publish"/"Publish changes" now stays reachable after the first publish, and publish_lineup diffs against who was SELECTED before this run so only players whose status actually changed get notified, not everyone currently selected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,12 +36,21 @@ 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.
|
||||
|
||||
Also doubles as "publish changes" for an already-published lineup a
|
||||
coach keeps editing (CoachLineupView's own "Save line-up" only writes
|
||||
LineupSelection -- Attendance.status, and any notification, only ever
|
||||
updates here). Notifies by comparing against who was SELECTED *before*
|
||||
this run, not by re-notifying everyone currently selected -- a player
|
||||
unaffected by the edit shouldn't get a second "you're in" ping, and one
|
||||
dropped since the last publish should hear about that specifically.
|
||||
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."""
|
||||
previously_selected_ids = set(Attendance.objects.filter(event=lineup.event, status=Attendance.AttendanceStatus.SELECTED).values_list("member_id", flat=True))
|
||||
|
||||
lineup.published_at = timezone.now()
|
||||
lineup.scheduled_publish_at = None
|
||||
lineup.save(update_fields=["published_at", "scheduled_publish_at"])
|
||||
@@ -51,10 +60,15 @@ def publish_lineup(lineup):
|
||||
Attendance.objects.filter(event=lineup.event, member_id__in=selected_member_ids).update(status=Attendance.AttendanceStatus.SELECTED)
|
||||
Attendance.objects.filter(event=lineup.event).exclude(member_id__in=selected_member_ids).exclude(status__in=UNAVAILABLE_STATUSES).update(status=Attendance.AttendanceStatus.NOT_SELECTED)
|
||||
|
||||
selected_members = Member.objects.filter(pk__in=selected_member_ids)
|
||||
if selected_members:
|
||||
newly_selected = Member.objects.filter(pk__in=selected_member_ids - previously_selected_ids)
|
||||
if newly_selected:
|
||||
body = _("You're in the line-up for %(event)s.") % {"event": lineup.event.title}
|
||||
notify_members(selected_members, club=lineup.event.club, title=_("Line-up published"), body=body, source=lineup.event)
|
||||
notify_members(newly_selected, club=lineup.event.club, title=_("Line-up published"), body=body, source=lineup.event)
|
||||
|
||||
newly_dropped = Member.objects.filter(pk__in=previously_selected_ids - selected_member_ids)
|
||||
if newly_dropped:
|
||||
body = _("The line-up for %(event)s has changed -- you're not in it this time.") % {"event": lineup.event.title}
|
||||
notify_members(newly_dropped, club=lineup.event.club, title=_("Line-up updated"), body=body, source=lineup.event)
|
||||
|
||||
return lineup
|
||||
|
||||
|
||||
@@ -573,6 +573,43 @@ class LineupServiceTests(EventsTestBase):
|
||||
notified_member_ids = set(Notification.objects.filter(member__in=[self.alice, self.bob]).values_list("member_id", flat=True))
|
||||
self.assertEqual(notified_member_ids, {self.alice.pk})
|
||||
|
||||
def test_republish_does_not_renotify_a_player_still_selected(self):
|
||||
event, lineup = self.make_game_with_lineup()
|
||||
LineupSelection.objects.create(lineup=lineup, member=self.alice)
|
||||
Attendance.objects.update_or_create(event=event, member=self.alice, defaults={"status": Attendance.AttendanceStatus.PRESENT})
|
||||
publish_lineup(lineup)
|
||||
Notification.objects.filter(member=self.alice).delete()
|
||||
|
||||
publish_lineup(lineup)
|
||||
|
||||
self.assertFalse(Notification.objects.filter(member=self.alice).exists())
|
||||
|
||||
def test_republish_notifies_a_newly_added_player(self):
|
||||
event, lineup = self.make_game_with_lineup()
|
||||
Attendance.objects.update_or_create(event=event, member=self.alice, defaults={"status": Attendance.AttendanceStatus.PRESENT})
|
||||
publish_lineup(lineup)
|
||||
Notification.objects.filter(member=self.alice).delete()
|
||||
|
||||
LineupSelection.objects.create(lineup=lineup, member=self.alice)
|
||||
publish_lineup(lineup)
|
||||
|
||||
notification = Notification.objects.get(member=self.alice)
|
||||
self.assertIn("in the line-up", notification.body)
|
||||
|
||||
def test_republish_notifies_a_dropped_player_with_a_different_message(self):
|
||||
event, lineup = self.make_game_with_lineup()
|
||||
LineupSelection.objects.create(lineup=lineup, member=self.alice)
|
||||
Attendance.objects.update_or_create(event=event, member=self.alice, defaults={"status": Attendance.AttendanceStatus.PRESENT})
|
||||
publish_lineup(lineup)
|
||||
Notification.objects.filter(member=self.alice).delete()
|
||||
|
||||
LineupSelection.objects.filter(lineup=lineup, member=self.alice).delete()
|
||||
publish_lineup(lineup)
|
||||
|
||||
notification = Notification.objects.get(member=self.alice)
|
||||
self.assertIn("not in it this time", notification.body)
|
||||
self.assertEqual(Attendance.objects.get(event=event, member=self.alice).status, Attendance.AttendanceStatus.NOT_SELECTED)
|
||||
|
||||
def test_notify_dropout_notifies_the_teams_managers(self):
|
||||
event, _lineup = self.make_game_with_lineup()
|
||||
manager = Member.objects.create(first_name="Cara", last_name="Coach")
|
||||
|
||||
Reference in New Issue
Block a user