From 3f9547706627cb53b96bca3f2a86dc9f1e245579 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 18 Apr 2025 08:39:18 +0200 Subject: [PATCH] fixes --- tournaments/models/__init__.py | 2 +- tournaments/models/team_registration.py | 92 +++++++++++++++++++ tournaments/models/tournament.py | 52 ++++++++++- tournaments/tasks.py | 89 +++--------------- .../tournaments/tournament_info.html | 36 +++----- tournaments/views.py | 11 +++ 6 files changed, 179 insertions(+), 103 deletions(-) diff --git a/tournaments/models/__init__.py b/tournaments/models/__init__.py index 6e3d720..8d1530e 100644 --- a/tournaments/models/__init__.py +++ b/tournaments/models/__init__.py @@ -4,7 +4,7 @@ from .custom_user import CustomUser from .club import Club from .court import Court from .date_interval import DateInterval -from .enums import UserOrigin, TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory, OnlineRegistrationStatus, ModelOperation +from .enums import UserOrigin, TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory, OnlineRegistrationStatus, RegistrationStatus, ModelOperation from .player_enums import PlayerSexType, PlayerDataSource, PlayerPaymentType from .event import Event from .tournament import Tournament, TeamSummon, TeamSortingType, TeamItem diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 66d0276..5833210 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -4,6 +4,7 @@ import uuid from django.utils import timezone from .enums import RegistrationStatus from .player_enums import PlayerPaymentType +from ..services.email_service import TournamentEmailService, TeamEmailType class TeamRegistration(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) @@ -407,3 +408,94 @@ class TeamRegistration(SideStoreModel): return self.tournament.team_fee() elif status == 'MIXED': return self.tournament.player_fee() + + def is_confirmation_expired(self): + """ + Check if the confirmation deadline has expired. + Returns: + bool: True if expired, False if still valid or no deadline exists + """ + deadline = self.get_confirmation_deadline() + if not deadline: + return False + current_time = timezone.now() + + return deadline < current_time + + def format_confirmation_deadline(self): + """ + Format the confirmation deadline in a human-readable format. + Returns: + str: Formatted deadline, or None if no deadline exists + """ + deadline = self.get_confirmation_deadline() + if not deadline: + return None + + if self.tournament and self.tournament.timezone(): + deadline = deadline.astimezone(self.tournament.timezone()) + + return deadline.strftime("%d/%m/%Y à %H:%M") + + def check_confirmation_deadline(self, tournament_context=None): + """ + Check if the confirmation deadline for this team has expired and perform necessary actions. + + Args: + tournament_context (dict, optional): Pre-calculated tournament context to avoid redundant calls. + If None, will calculate on-demand. + + """ + now = timezone.now() + tournament = self.tournament + + if not tournament: + return + + # Use provided context or calculate if not provided + if tournament_context is None: + teams = tournament.teams(True) + waiting_list_teams = tournament.waiting_list_teams(teams) + ttc = tournament.calculate_time_to_confirm(len(waiting_list_teams)) if waiting_list_teams is not None else None + first_waiting_list_team = tournament.first_waiting_list_team(teams) + is_online_registration_irrevelant = tournament.is_online_registration_irrevelant() + else: + ttc = tournament_context.get('ttc') + first_waiting_list_team = tournament_context.get('first_waiting_list_team') + is_online_registration_irrevelant = tournament_context.get('is_online_registration_irrevelant', False) + + # Get all players in this team + team_players = self.player_registrations.filter(registered_online=True) + + should_update_team = False + should_send_mail = False + + for team_player in team_players: + if is_online_registration_irrevelant: + team_player.registration_status = RegistrationStatus.CANCELED + team_player.save() + elif team_player.time_to_confirm is None and first_waiting_list_team is not None: + self.set_time_to_confirm(ttc) + should_send_mail = True + print(team_player, "team_player.time_to_confirm is None and", ttc) + elif team_player.time_to_confirm is not None and now > team_player.time_to_confirm: + if first_waiting_list_team is not None: + team_player.registration_status = RegistrationStatus.CANCELED + self.registration_date = now + should_update_team = True + + team_player.time_to_confirm = None + team_player.save() + print(team_player, "time_to_confirm = ", team_player.time_to_confirm) + + if should_update_team: + self.save() + print(f"Team {self} confirmation expired in tournament {tournament.id}") + + + if should_send_mail: + TournamentEmailService.notify_team( + self, + tournament, + TeamEmailType.REQUIRES_TIME_CONFIRMATION + ) diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index f7fe3f0..cce3013 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -1,6 +1,7 @@ from zoneinfo import ZoneInfo from django.db import models -from . import BaseModel, Event, TournamentPayment, FederalMatchCategory, FederalCategory, FederalLevelCategory, FederalAgeCategory, OnlineRegistrationStatus +from . import BaseModel, Event, TournamentPayment, FederalMatchCategory, FederalCategory, FederalLevelCategory, FederalAgeCategory, OnlineRegistrationStatus, RegistrationStatus + import uuid from django.utils import timezone, formats from datetime import datetime, timedelta, time @@ -1812,6 +1813,55 @@ class Tournament(BaseModel): """Get the commission rate for this tournament, falling back to the umpire default if not set""" return 1.00 # Fallback default + def check_all_confirmation_deadlines(self): + """ + Check all confirmation deadlines for teams in this tournament. + Send notification emails as needed. + + Returns: + int: Number of teams processed + """ + + # Calculate these values once for the tournament + teams = self.teams(True) + waiting_list_teams = self.waiting_list_teams(teams) + ttc = self.calculate_time_to_confirm(len(waiting_list_teams)) if waiting_list_teams is not None else None + first_waiting_list_team = self.first_waiting_list_team(teams) + + # Tournament context dict to pass to each team check + tournament_context = { + 'ttc': ttc, + 'first_waiting_list_team': first_waiting_list_team, + 'is_online_registration_irrevelant': self.is_online_registration_irrevelant() + } + + # Find players with expired confirmation deadlines in this tournament + PlayerRegistration = apps.get_model('tournaments', 'PlayerRegistration') + expired_confirmations = PlayerRegistration.objects.filter( + registration_status=RegistrationStatus.PENDING, + registered_online=True, + team_registration__tournament=self + ).select_related('team_registration') + + processed_teams = set() # To avoid processing the same team multiple times + teams_processed = 0 + + for player in expired_confirmations: + team_registration = player.team_registration + + # Skip if we've already processed this team + if team_registration.id in processed_teams: + continue + + processed_teams.add(team_registration.id) + teams_processed += 1 + + # Process in a transaction to ensure atomic operations + team_registration.check_confirmation_deadline(tournament_context) + + return teams_processed + + class MatchGroup: def __init__(self, name, matches, formatted_schedule, round_id=None): diff --git a/tournaments/tasks.py b/tournaments/tasks.py index f14116b..66836a6 100644 --- a/tournaments/tasks.py +++ b/tournaments/tasks.py @@ -3,9 +3,8 @@ from django.utils import timezone from django.db import transaction from django.conf import settings -from .models import PlayerRegistration +from .models import Tournament from .models.enums import RegistrationStatus -from .services.email_service import TournamentEmailService, TeamEmailType @background(schedule=settings.BACKGROUND_SCHEDULED_TASK_INTERVAL * 60) # Run every 30 minutes (30*60 seconds) def background_task_check_confirmation_deadlines(): @@ -21,81 +20,15 @@ def check_confirmation_deadlines(): now = timezone.now() print(f"[{now}] Running confirmation deadline check...") - # Find players with expired confirmation deadlines - expired_confirmations = PlayerRegistration.objects.filter( - registration_status=RegistrationStatus.PENDING, - registered_online=True, - team_registration__isnull=False - ).select_related('team_registration', 'team_registration__tournament') + # Get all tournaments with online registrations + tournaments = Tournament.objects.filter( + team_registrations__player_registrations__registration_status=RegistrationStatus.PENDING, + team_registrations__player_registrations__registered_online=True + ).distinct() - print(f"Found {expired_confirmations.count()} expired confirmations") + total_processed = 0 + for tournament in tournaments: + processed = tournament.check_all_confirmation_deadlines() + total_processed += processed - # Process each expired confirmation - processed_teams = set() # To avoid processing the same team multiple times - - for player in expired_confirmations: - team_registration = player.team_registration - - # Skip if we've already processed this team - if team_registration.id in processed_teams: - continue - - processed_teams.add(team_registration.id) - tournament = team_registration.tournament - - if not tournament: - continue - - teams = tournament.teams(True) - waiting_list_teams = tournament.waiting_list_teams(teams) - if waiting_list_teams is not None: - ttc = tournament.calculate_time_to_confirm(len(waiting_list_teams)) - else: - ttc = None - first_waiting_list_team = tournament.first_waiting_list_team(teams) - - # Process in a transaction to ensure atomic operations - with transaction.atomic(): - # Get all players in this team and mark them as expired - team_players = PlayerRegistration.objects.filter( - team_registration=team_registration, - registered_online=True - ) - - should_update_team = False - should_send_mail = False - is_online_registration_irrevelant = team_registration.tournament.is_online_registration_irrevelant() - for team_player in team_players: - if is_online_registration_irrevelant: - team_player.registration_status = RegistrationStatus.CANCELED - team_player.save() - elif team_player.time_to_confirm is None and first_waiting_list_team is not None: - team_registration.set_time_to_confirm(ttc) - team_player.save() - should_send_mail = True - print(team_player, "team_player.time_to_confirm is None and", ttc) - elif team_player.time_to_confirm is not None and now > team_player.time_to_confirm: - if first_waiting_list_team is not None: - team_player.registration_status = RegistrationStatus.CANCELED - team_registration.registration_date = now - should_update_team = True - - team_player.time_to_confirm = None - team_player.save() - print(team_player, "time_to_confirm = ", team_player.time_to_confirm) - # elif team_player.time_to_confirm is not None and team_player.time_to_confirm > ttc: - # team_player.registration_status = RegistrationStatus.PENDING - # team_player.time_to_confirm = ttc - # team_player.save() - # should_update_team = True - - if should_send_mail: - TournamentEmailService.notify_team( - team_registration, - tournament, - TeamEmailType.REQUIRES_TIME_CONFIRMATION - ) - - if should_update_team: - team_registration.save() - print(f"Team {team_registration} confirmation expired in tournament {tournament.id}") + print(f"Processed confirmation deadlines for {total_processed} teams") diff --git a/tournaments/templates/tournaments/tournament_info.html b/tournaments/templates/tournaments/tournament_info.html index 5a81e3e..b19b2f3 100644 --- a/tournaments/templates/tournaments/tournament_info.html +++ b/tournaments/templates/tournaments/tournament_info.html @@ -51,30 +51,20 @@

{% if team.needs_confirmation %} -

Confirmation requise

-
Une place dans le tournoi a été libérée !
- {% if tournament.should_request_payment and tournament.online_payment_is_mandatory %} -
Vous devez payer votre inscription pour confirmer votre participation.
- {% endif %} - {% if team.get_confirmation_deadline %} - {% timezone tournament.timezone %} - {% with time_to_confirm=team.get_confirmation_deadline %} - {% now "Y-m-d H:i:s" as current_time_str %} - {% if time_to_confirm %} - {% if time_to_confirm|date:"Y-m-d H:i:s" > current_time_str %} -
Vous devez confirmer votre participation avant le :
-

{{ time_to_confirm|date:"d/m/Y à H:i" }}

- -

Temps restant: {{ time_to_confirm|timeuntil }}

- {% else %} -

- Le délai de confirmation est expiré. Votre place a été proposée à une autre équipe. -

- {% endif %} - {% endif %} - {% endwith %} - {% endtimezone %} +

Confirmation requise

+ {% if tournament.should_request_payment and tournament.online_payment_is_mandatory %} +
Vous devez payer votre inscription pour confirmer votre participation.
+ {% endif %} + {% if team.get_confirmation_deadline %} + {% if team.is_confirmation_expired %} +

+ Le délai de confirmation est expiré. Votre place a été proposée à une autre équipe. +

+ {% else %} +
Vous devez confirmer votre participation avant le :
+

{{ team.format_confirmation_deadline }}

{% endif %} + {% endif %} {% endif %} {% if team.call_date %} diff --git a/tournaments/views.py b/tournaments/views.py index ebe8c2d..b256e2b 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -1076,6 +1076,12 @@ def confirm_tournament_registration(request, tournament_id): return redirect('tournament-info', tournament_id=tournament_id) team_registration = team_registrations.first() + + if team_registration.is_confirmation_expired(): + team_registration.check_confirmation_deadline() + messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus confirmer votre participation.") + return redirect('tournament-info', tournament_id=tournament_id) + # Confirm registration team_registration.confirm_registration() @@ -1094,6 +1100,11 @@ def proceed_to_payment(request, tournament_id): messages.error(request, "Aucune inscription trouvée pour ce tournoi.") return redirect('tournament-info', tournament_id=tournament_id) + if team.is_confirmation_expired(): + team.check_confirmation_deadline() + messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus régler votre participation.") + return redirect('tournament-info', tournament_id=tournament_id) + if team.is_paid(): messages.info(request, "Votre inscription est déjà payée.") return redirect('tournament-info', tournament_id=tournament_id)