"""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_bot 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): """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. 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, ) sent += 1 return sent