102 lines
3.6 KiB
Python
102 lines
3.6 KiB
Python
"""Announcing a scheduled match to the people who have to be there.
|
|
|
|
The same twenty lines appeared three times — matches.create_match,
|
|
matches.edit_match and team_matches.create_match — each formatting the date
|
|
and time itself, then walking two parallel lists in lockstep to pair a
|
|
player with the participant row that a Discord reaction has to find again
|
|
(ARCH-003).
|
|
|
|
Three copies meant three chances to drift, and they had:
|
|
create_match read the times from local variables it had just parsed, while
|
|
edit_match re-derived them from the saved row and substituted the start
|
|
time for a missing end time. The rule kept here is the more careful of the
|
|
two.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from app.discord import send_schedule_notification
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
#: What the bot shows when a match has no usable time.
|
|
TIME_UNKNOWN = 'TBD'
|
|
|
|
|
|
def format_event_time(start_time, end_time):
|
|
"""Render a match's time range the way the Discord message expects.
|
|
|
|
Args:
|
|
start_time: A time, or None.
|
|
end_time: A time, or None. Falls back to start_time, so a match with
|
|
only a start still announces something useful.
|
|
|
|
Returns:
|
|
str: 'HH:MM AM - HH:MM PM', or TIME_UNKNOWN.
|
|
"""
|
|
if not start_time:
|
|
return TIME_UNKNOWN
|
|
finish = end_time or start_time
|
|
return f'{start_time.strftime("%I:%M %p")} - {finish.strftime("%I:%M %p")}'
|
|
|
|
|
|
def zip_participants(player_ids, participant_ids):
|
|
"""Pair each player with the participant row that was created for them.
|
|
|
|
The routes build these as two parallel lists, appended in step. Pairing
|
|
them by index is what the original code did, and it is only correct as
|
|
long as they stay in step — hence one place to look at rather than
|
|
three. A short participant list yields None, which
|
|
:func:`notify_participants` turns into the fallback reference.
|
|
|
|
Args:
|
|
player_ids: Player primary keys, in creation order.
|
|
participant_ids: Participant row ids, in the same order.
|
|
|
|
Yields:
|
|
tuple[int, int | None]: (player_id, participant_id).
|
|
"""
|
|
for index, player_id in enumerate(player_ids):
|
|
yield player_id, participant_ids[index] if index < len(participant_ids) else None
|
|
|
|
|
|
def notify_participants(
|
|
*, title, date, start_time, end_time, participants, fallback_id, change_kind='scheduled'
|
|
):
|
|
"""Tell each participant that a match has been scheduled or changed.
|
|
|
|
Args:
|
|
title: Match title, shown in the message.
|
|
date: The match date.
|
|
start_time: Start time, or None.
|
|
end_time: End time, or None.
|
|
participants: Iterable of (player_id, participant_id) pairs.
|
|
participant_id is what a Discord reaction resolves back to, so
|
|
attendance lands on the right row.
|
|
fallback_id: Reference to use when a participant row has no id —
|
|
the match's own, which the bot can still act on.
|
|
change_kind: 'scheduled' (new match / new player added) or
|
|
'modified' (date/time changed for existing players).
|
|
|
|
Returns:
|
|
int: How many notifications were handed to the bot.
|
|
"""
|
|
event_date = date.strftime('%Y-%m-%d')
|
|
event_time = format_event_time(start_time, end_time)
|
|
|
|
sent = 0
|
|
for player_id, participant_id in participants:
|
|
if not player_id:
|
|
continue
|
|
send_schedule_notification(
|
|
user_id=player_id,
|
|
event_type='match',
|
|
event_title=title,
|
|
event_date=event_date,
|
|
event_time=event_time,
|
|
reference_id=participant_id or fallback_id,
|
|
change_kind=change_kind,
|
|
)
|
|
sent += 1
|
|
return sent
|