@ -1,19 +1,15 @@
from time import daylight
from zoneinfo import ZoneInfo
from zoneinfo import ZoneInfo
from django . db import models
from django . db import models
from typing import TYPE_CHECKING
if TYPE_CHECKING :
from tournaments . models import group_stage
from . import BaseModel , Event , TournamentPayment , FederalMatchCategory , FederalCategory , FederalLevelCategory , FederalAgeCategory , OnlineRegistrationStatus
from . import BaseModel , Event , TournamentPayment , FederalMatchCategory , FederalCategory , FederalLevelCategory , FederalAgeCategory , OnlineRegistrationStatus
import uuid
import uuid
from django . utils import timezone , formats
from django . utils import timezone , formats
from datetime import datetime , timedelta
from datetime import datetime , timedelta , time
from zoneinfo import ZoneInfo
from tournaments . utils . player_search import get_player_name_from_csv
from tournaments . utils . player_search import get_player_name_from_csv
from shared . cryptography import encryption_util
from shared . cryptography import encryption_util
from . . utils . extensions import plural_format
from . . utils . extensions import plural_format
from django . utils . formats import date_format
from . . utils . licence_validator import LicenseValidator
from django . apps import apps
class TeamSortingType ( models . IntegerChoices ) :
class TeamSortingType ( models . IntegerChoices ) :
RANK = 1 , ' Rank '
RANK = 1 , ' Rank '
@ -180,6 +176,8 @@ class Tournament(BaseModel):
def level ( self ) :
def level ( self ) :
if self . federal_level_category == 0 :
if self . federal_level_category == 0 :
return " Anim. "
return " Anim. "
if self . federal_level_category == 1 :
return " CHPT "
return self . get_federal_level_category_display ( )
return self . get_federal_level_category_display ( )
def category ( self ) :
def category ( self ) :
@ -231,36 +229,12 @@ class Tournament(BaseModel):
else :
else :
return None
return None
def tournament_status_display ( self ) :
def get_tournament_status ( self ) :
if self . is_canceled ( ) is True :
return self . get_online_registration_status ( ) . status_localized ( )
return " Annulé "
def get_tournament_status_team_count ( self ) :
teams = self . teams ( True )
active_teams_count = self . team_registrations . filter ( walk_out = False ) . count ( )
if self . supposedly_in_progress ( ) or self . end_date is not None or self . should_be_over ( ) :
return min ( active_teams_count , self . team_count )
teams = [ t for t in teams if t . stage != " Attente " ]
if teams is not None and len ( teams ) > 0 :
word = " équipe "
if len ( teams ) > 1 :
word = word + " s "
return f " { len ( teams ) } { word } "
else :
return None
registration_status = None
if self . enable_online_registration == True :
registration_status = self . get_online_registration_status ( ) . status_localized ( )
if teams is not None and len ( teams ) > 0 :
word = " inscription "
if len ( teams ) > 1 :
word = word + " s "
if registration_status is not None :
return f " { registration_status } \n { len ( teams ) } { word } "
else :
return f " { len ( teams ) } { word } "
else :
if registration_status is not None :
return f " { registration_status } "
return None
def name_and_event ( self ) :
def name_and_event ( self ) :
event_name = None
event_name = None
@ -333,9 +307,9 @@ class Tournament(BaseModel):
index = i
index = i
# Check if team_count exists
# Check if team_count exists
if self . team_count :
if self . team_count_limit == True :
# Team is not in list
# Team is not in list
if index < self . team_count :
if index < 0 :
print ( " Team is not in list " , index , self . team_count )
print ( " Team is not in list " , index , self . team_count )
return - 1
return - 1
# Return position in waiting list relative to target count
# Return position in waiting list relative to target count
@ -1091,81 +1065,64 @@ class Tournament(BaseModel):
return options
return options
def online_register_is_enabled ( self ) :
def get_selection_status_localized ( self ) :
if self . team_sorting == TeamSortingType . RANK :
return " La sélection se fait par le poids de l ' équipe "
else :
return " La sélection se fait par date d ' inscription "
def get_online_registration_status ( self ) :
if self . is_canceled ( ) :
return OnlineRegistrationStatus . CANCELED
if self . end_date is not None :
return OnlineRegistrationStatus . ENDED_WITH_RESULTS
if self . enable_online_registration is False :
return OnlineRegistrationStatus . NOT_ENABLED
if self . supposedly_in_progress ( ) :
if self . supposedly_in_progress ( ) :
return False
return OnlineRegistrationStatus . ENDED
if self . closed_registration_date is not None :
if self . closed_registration_date is not None :
return False
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
if self . end_date is not None :
return False
now = timezone . now ( )
now = timezone . now ( )
# Check if online registration is enabled
if not self . enable_online_registration :
return False
# Check opening registration date
if self . opening_registration_date is not None :
if self . opening_registration_date is not None :
timezoned_datetime = timezone . localtime ( self . opening_registration_date )
timezoned_datetime = timezone . localtime ( self . opening_registration_date )
if now < timezoned_datetime :
if now < timezoned_datetime :
return False
return OnlineRegistrationStatus . NOT_STARTED
# Check registration date limit
if self . registration_date_limit is not None :
if self . registration_date_limit is not None :
timezoned_datetime = timezone . localtime ( self . registration_date_limit )
timezoned_datetime = timezone . localtime ( self . registration_date_limit )
if now > timezoned_datetime :
if now > timezoned_datetime :
return False
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
if self . team_sorting == TeamSortingType . RANK :
return OnlineRegistrationStatus . OPEN
# Check target team count and waiting list limit
if self . team_count_limit is True :
if self . team_count is not None :
# Get all team registrations excluding walk_outs
current_team_count = self . team_registrations . exclude ( walk_out = True ) . count ( )
current_team_count = self . team_registrations . exclude ( walk_out = True ) . count ( )
if current_team_count > = self . team_count :
if current_team_count > = self . team_count :
if self . waiting_list_limit is not None :
if self . waiting_list_limit is not None :
waiting_list_count = current_team_count - self . team_count
waiting_list_count = current_team_count - self . team_count
if waiting_list_count > = self . waiting_list_limit :
if waiting_list_count > = self . waiting_list_limit :
return False
return OnlineRegistrationStatus . WAITING_LIST_FULL
return True
def get_selection_status_localized ( self ) :
if self . team_sorting == TeamSortingType . RANK :
return " La sélection se fait par le poids de l ' équipe "
else :
return " La sélection se fait par date d ' inscription "
def get_online_registration_status ( self ) :
if self . supposedly_in_progress ( ) :
return OnlineRegistrationStatus . ENDED
if self . closed_registration_date is not None :
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
if self . end_date is not None :
return OnlineRegistrationStatus . OPEN
return OnlineRegistrationStatus . ENDED_WITH_RESULTS
now = timezone . now ( )
def get_registration_status_short_label ( self ) :
""" Returns a short label for the registration status """
status = self . get_online_registration_status ( )
return status . short_label ( )
if self . opening_registration_date is not None :
def get_registration_status_class ( self ) :
timezoned_datetime = timezone . localtime ( self . opening_registration_date )
""" Returns the CSS class for the registration status box """
if now < timezoned_datetime :
status = self . get_online_registration_status ( )
return OnlineRegistrationStatus . NOT_STARTED
return status . box_class ( )
if self . registration_date_limit is not None :
def should_display_status_box ( self ) :
timezoned_datetime = timezone . localtime ( self . registration_date_limit )
""" Returns whether the registration status box should be displayed """
if now > timezoned_datetime :
status = self . get_online_registration_status ( )
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
return status . display_box ( )
if self . team_sorting == TeamSortingType . RANK :
return OnlineRegistrationStatus . OPEN
if self . team_count_limit is True :
# Get all team registrations excluding walk_outs
current_team_count = self . team_registrations . exclude ( walk_out = True ) . count ( )
if current_team_count > = self . team_count :
if self . waiting_list_limit is not None :
waiting_list_count = current_team_count - self . team_count
if waiting_list_count > = self . waiting_list_limit :
return OnlineRegistrationStatus . WAITING_LIST_FULL
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
return OnlineRegistrationStatus . OPEN
def is_unregistration_possible ( self ) :
def is_unregistration_possible ( self ) :
# Check if tournament has started
# Check if tournament has started
@ -1638,6 +1595,86 @@ class Tournament(BaseModel):
@property
def week_day ( self ) :
""" Return the weekday name (e.g., ' Monday ' ) """
date = self . local_start_date ( )
return date_format ( date , format = ' D ' ) + ' . ' # 'l' gives full weekday name
@property
def day ( self ) :
""" Return the day of the month """
date = self . local_start_date ( )
return date . day
@property
def month ( self ) :
"""
Return the month name in lowercase :
- If full month name is 4 letters or fewer , return as is
- If more than 4 letters , return first 4 letters with a dot
"""
date = self . local_start_date ( )
# Get full month name and convert to lowercase
full_month = date_format ( date , format = ' F ' ) . lower ( )
# Check if the month name is 5 letters or fewer
if len ( full_month ) < = 5 :
return full_month
else :
# Truncate to 5 letters and add a dot
return f " { full_month [ : 5 ] } . "
@property
def year ( self ) :
""" Return the year """
date = self . local_start_date ( )
return date . year
@property
def localized_day_duration ( self ) :
"""
Return localized day duration in French :
- If multiple days : ' 2 jours ' , ' 3 jours ' , etc .
- If 1 day and starts after 18 : 00 : ' soirée '
- If 1 day and starts before 18 : 00 : ' journée '
"""
# Assuming day_duration is a property or field that returns the number of days
days = self . day_duration
if days > 1 :
return f " { days } jours "
else :
# For single day events, check the starting hour
start_time = self . local_start_date ( ) . time ( )
evening_threshold = time ( 18 , 0 ) # 18:00 (6 PM)
if start_time > = evening_threshold :
return " soirée "
else :
return " journée "
def get_player_registration_status_by_licence ( self , user ) :
licence_id = user . licence_id
if not licence_id :
return None
validator = LicenseValidator ( licence_id )
if validator . validate_license ( ) :
stripped_license = validator . stripped_license
# Check if there is a PlayerRegistration for this user in this tournament
PlayerRegistration = apps . get_model ( ' tournaments ' , ' PlayerRegistration ' )
user_player = PlayerRegistration . objects . filter (
licence_id__icontains = stripped_license ,
team_registration__tournament = self ,
) . first ( )
if user_player :
return user_player . get_registration_status ( )
return None
class MatchGroup :
class MatchGroup :
def __init__ ( self , name , matches , formatted_schedule , round_id = None ) :
def __init__ ( self , name , matches , formatted_schedule , round_id = None ) :
self . name = name
self . name = name