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)