@ -609,7 +609,7 @@ class Tournament(BaseModel):
# if now is before the first match, we want to show the summons + group stage or first matches
# change timezone to datetime to avoid the bug RuntimeWarning: DateTimeField Tournament.start_date received a naive datetime (2024-05-16 00:00:00) while time zone support is active.
current_time = timezone . now ( )
current_time = timezone . localtime ( )
tournament_start = self . local_start_date ( )
one_hour_before_start = tournament_start - timedelta ( hours = 1 )
@ -852,17 +852,17 @@ class Tournament(BaseModel):
if self . end_date is not None :
return is_build_and_not_empty
if timezone . now ( ) > = self . local_start_date ( ) :
if self . has_started ( ) :
return is_build_and_not_empty
minimum_publish_date = self . creation_date . replace ( hour = 9 , minute = 0 ) + timedelta ( days = 1 )
return timezone . now ( ) > = timezone . localtime ( minimum_publish_date )
minimum_publish_date = self . creation_date . replace ( hour = 7 , minute = 0 ) + timedelta ( days = 1 )
return timezone . now ( ) > = minimum_publish_date
def display_teams ( self ) :
if self . end_date is not None :
return self . has_team_registrations ( )
if self . publish_teams :
return self . has_team_registrations ( )
if timezone . now ( ) > = self . local_start_date ( ) :
if self . has_started ( ) :
return self . has_team_registrations ( )
return False
@ -874,7 +874,7 @@ class Tournament(BaseModel):
return False
if self . publish_summons :
return self . has_summons ( )
if timezone . now ( ) > = self . local_start_date ( ) :
if self . has_started ( ) :
return self . has_summons ( )
return False
@ -888,7 +888,7 @@ class Tournament(BaseModel):
first_group_stage_start_date = self . group_stage_start_date ( )
if first_group_stage_start_date is None :
return timezone . now ( ) > = self . local_start_date ( )
return self . has_started ( )
else :
return timezone . now ( ) > = first_group_stage_start_date
@ -896,9 +896,7 @@ class Tournament(BaseModel):
group_stages = [ gs for gs in self . group_stages . all ( ) if gs . start_date is not None ]
if len ( group_stages ) == 0 :
return None
timezone = self . timezone ( )
return min ( group_stages , key = lambda gs : gs . start_date ) . start_date . astimezone ( timezone )
return min ( group_stages , key = lambda gs : gs . start_date ) . start_date
def display_matches ( self ) :
if self . end_date is not None :
@ -911,19 +909,22 @@ class Tournament(BaseModel):
first_match_start_date = self . first_match_start_date ( bracket_matches )
if first_match_start_date is None :
return timezone . now ( ) > = self . local_start_date ( )
return self . has_started ( )
bracket_start_date = self . getEightAm ( first_match_start_date )
if bracket_start_date < self . local_ start_date( ) :
bracket_start_date = self . local_ start_date( )
if bracket_start_date < self . start_date :
bracket_start_date = self . start_date
group_stage_start_date = self . group_stage_start_date ( )
now = timezone . now ( )
if group_stage_start_date is not None :
if bracket_start_date < group_stage_start_date :
return timezone . now ( ) > = first_match_start_date
return now > = first_match_start_date
if timezone . now ( ) > = bracket_start_date :
if now > = bracket_start_date :
return True
return False
@ -939,45 +940,59 @@ class Tournament(BaseModel):
matches = [ m for m in bracket_matches if m . start_date is not None ]
if len ( matches ) == 0 :
return None
return min ( matches , key = lambda m : m . start_date ) . local_start_date ( )
return min ( matches , key = lambda m : m . start_date )
def getEightAm ( self , date ) :
return date . replace ( hour = 8 , minute = 0 , second = 0 , microsecond = 0 , tzinfo = date . tzinfo )
def has_started ( self , hour_delta = None ) :
timezoned_datetime = self . local_start_date ( )
now_utc = timezone . now ( )
now = now_utc . astimezone ( self . timezone ( ) )
if hour_delta is not None :
timezoned_datetime - = timedelta ( hours = hour_delta )
return now > = timezoned_datetime
def will_start_soon ( self ) :
return self . has_started ( hour_delta = 2 )
def supposedly_in_progress ( self ) :
# end = self.start_date + timedelta(days=self.day_duration + 1)
# return self.start_date.replace(hour=0, minute=0) <= timezone.now() <= end
timezoned_datetime = self . local_start_date ( )
end = timezoned_datetime + timedelta ( days = self . day_duration + 1 )
now = timezone . now ( )
now_utc = timezone . now ( )
now = now_utc . astimezone ( self . timezone ( ) )
start = timezoned_datetime . replace ( hour = 0 , minute = 0 )
print ( f " timezoned_datetime: { timezoned_datetime } " )
print ( f " tournament end date: { end } " )
print ( f " current time: { now } " )
print ( f " tournament start: { start } " )
print ( f " start <= now <= end: { start < = now < = end } " )
# print(f"timezoned_datetime: {timezoned_datetime}" )
# print(f"tournament end date: {end}" )
# print(f"current time: {now}" )
# print(f"tournament start: {start}" )
# print(f"start <= now <= end: {start <= now <= end}" )
return start < = now < = end
def starts_in_the_future ( self ) :
# tomorrow = datetime.now().date() + timedelta(days=1)
timezoned_datetime = self . local_start_date ( )
start = timezoned_datetime . replace ( hour = 0 , minute = 0 )
now = timezone . now ( )
now_utc = timezone . now ( )
now = now_utc . astimezone ( self . timezone ( ) )
return start > = now
def has_ended ( self ) :
return self . end_date is not None
def should_be_over ( self ) :
if self . end_date is not None :
if self . has_ended ( ) :
return True
timezoned_datetime = self . local_start_date ( )
end = timezoned_datetime + timedelta ( days = self . day_duration + 1 )
now = timezone . now ( )
now_utc = timezone . now ( )
now = now_utc . astimezone ( self . timezone ( ) )
return now > = end and self . is_build_and_not_empty ( ) and self . nearly_over ( )
def nearly_over ( self ) :
@ -1096,6 +1111,33 @@ class Tournament(BaseModel):
else :
return " La sélection se fait par date d ' inscription "
def automatic_waiting_list ( self ) :
"""
Determines if automatic waiting list processing should be applied based on the tournament ' s registration status.
Returns True if automatic waiting list processing should be applied , False otherwise .
"""
if self . enable_time_to_confirm is False :
return False
# Get the current registration status
status = self . get_online_registration_status ( )
# Define which status values should allow automatic waiting list
status_map = {
OnlineRegistrationStatus . OPEN : True ,
OnlineRegistrationStatus . NOT_ENABLED : False ,
OnlineRegistrationStatus . NOT_STARTED : False ,
OnlineRegistrationStatus . ENDED : False ,
OnlineRegistrationStatus . WAITING_LIST_POSSIBLE : False ,
OnlineRegistrationStatus . WAITING_LIST_FULL : True , # Still manage in case spots open up
OnlineRegistrationStatus . IN_PROGRESS : False , # Allow for last-minute changes
OnlineRegistrationStatus . ENDED_WITH_RESULTS : False ,
OnlineRegistrationStatus . CANCELED : False
}
# Return the mapped value or False as default for any unmapped status
return status_map . get ( status , False )
def get_online_registration_status ( self ) :
if self . is_canceled ( ) :
return OnlineRegistrationStatus . CANCELED
@ -1103,7 +1145,7 @@ class Tournament(BaseModel):
return OnlineRegistrationStatus . ENDED_WITH_RESULTS
if self . enable_online_registration is False :
return OnlineRegistrationStatus . NOT_ENABLED
if self . supposedly_in_progress ( ) :
if self . has_started ( ) :
return OnlineRegistrationStatus . ENDED
if self . closed_registration_date is not None :
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
@ -1111,13 +1153,11 @@ class Tournament(BaseModel):
now = timezone . now ( )
if self . opening_registration_date is not None :
timezoned_datetime = timezone . localtime ( self . opening_registration_date )
if now < timezoned_datetime :
if now < self . opening_registration_date :
return OnlineRegistrationStatus . NOT_STARTED
if self . registration_date_limit is not None :
timezoned_datetime = timezone . localtime ( self . registration_date_limit )
if now > timezoned_datetime :
if now > self . registration_date_limit :
return OnlineRegistrationStatus . WAITING_LIST_POSSIBLE
if self . team_sorting == TeamSortingType . RANK :
@ -1163,14 +1203,15 @@ class Tournament(BaseModel):
# Check if registration is closed
if self . registration_date_limit is not None :
if timezone . now ( ) > timezone . localtime ( self . registration_date_limit ) :
if timezone . now ( ) > self . registration_date_limit :
return False
# Otherwise unregistration is allowed
return True
def get_waiting_list_position ( self ) :
current_time = timezone . now ( )
now_utc = timezone . now ( )
current_time = now_utc . astimezone ( self . timezone ( ) )
local_registration_federal_limit = self . local_registration_federal_limit ( )
if self . team_sorting == TeamSortingType . RANK and local_registration_federal_limit is not None :
if current_time < local_registration_federal_limit :
@ -1315,7 +1356,8 @@ class Tournament(BaseModel):
return None
def waiting_list_teams ( self , teams ) :
current_time = timezone . now ( )
now_utc = timezone . now ( )
current_time = now_utc . astimezone ( self . timezone ( ) )
local_registration_federal_limit = self . local_registration_federal_limit ( )
if self . team_sorting == TeamSortingType . RANK and local_registration_federal_limit is not None :
if current_time < local_registration_federal_limit :
@ -1349,6 +1391,7 @@ class Tournament(BaseModel):
and m . court_index is not None
]
now = timezone . now ( )
# Group matches by court
matches_by_court = { }
courts = set ( )
@ -1362,7 +1405,7 @@ class Tournament(BaseModel):
for court in matches_by_court :
matches_by_court [ court ] . sort ( key = lambda m : (
m . start_date is None , # None dates come last
m . start_date if m . start_date else timezone . now ( )
m . start_date if m . start_date else now
) )
# Sort courts and organize them into groups of 4
@ -1535,14 +1578,29 @@ class Tournament(BaseModel):
# 7. Check urgency overrides
apply_business_rules = True
for hours_threshold , override in URGENCY_OVERRIDE [ " thresholds " ] . items ( ) :
if hours_until_tournament < = hours_threshold :
apply_business_rules = False
# Ensure minimum response time
minutes_to_confirm = max ( minutes_to_confirm ,
URGENCY_OVERRIDE [ " minimum_response_time " ] / 10 if getattr ( settings , ' LIVE_TESTING ' , False )
else URGENCY_OVERRIDE [ " minimum_response_time " ] )
break
# Default business hours
business_start_hour = BUSINESS_RULES [ " hours " ] [ " start " ]
business_end_hour = BUSINESS_RULES [ " hours " ] [ " end " ]
# for hours_threshold, override in URGENCY_OVERRIDE["thresholds"].items():
# if hours_until_tournament <= hours_threshold:
# apply_business_rules = False
# # Ensure minimum response time
# minutes_to_confirm = max(minutes_to_confirm,
# URGENCY_OVERRIDE["minimum_response_time"] / 10 if getattr(settings, 'LIVE_TESTING', False)
# else URGENCY_OVERRIDE["minimum_response_time"])
# break
# Adjust business hours based on tournament proximity
if hours_until_tournament < = 24 :
# 24 hours before tournament: 7am - 10pm
business_start_hour = 7
business_end_hour = 22
if hours_until_tournament < = 12 :
# 12 hours before tournament: 6am - 1am (next day)
business_start_hour = 6
business_end_hour = 25 # 1am next day (25 in 24-hour format)
# 8. Calculate raw deadline
raw_deadline = current_time + timezone . timedelta ( minutes = minutes_to_confirm )
@ -1558,23 +1616,14 @@ class Tournament(BaseModel):
# 10. Apply business hours rules if needed
if apply_business_rules and live_testing is False :
# Check if deadline falls outside business hours
is_weekend = raw_deadline . weekday ( ) in BUSINESS_RULES [ " days " ] [ " weekend " ]
before_hours = raw_deadline . hour < BUSINESS_RULES [ " hours " ] [ " start " ]
after_hours = raw_deadline . hour > = BUSINESS_RULES [ " hours " ] [ " end " ]
before_hours = raw_deadline . hour < business_start_hour
after_hours = raw_deadline . hour > = business_end_hour
if is_weekend or before_hours or after_hours :
if before_hours or after_hours :
# Extend to next business day
if after_hours or is_weekend :
if after_hours :
# Move to next day
days_to_add = 1
if is_weekend :
# If Saturday, move to Monday
if raw_deadline . weekday ( ) == 5 : # Saturday
days_to_add = 2
# If Sunday, move to Monday
elif raw_deadline . weekday ( ) == 6 : # Sunday
days_to_add = 1
raw_deadline + = timezone . timedelta ( days = days_to_add )
# Set to business start hour
@ -1584,7 +1633,7 @@ class Tournament(BaseModel):
second = 0 ,
microsecond = 0
)
print ( f " Is weekend: { is_weekend } , Before hours: { before_hours } , After hours: { after_hours } " )
print ( f " Before hours: { before_hours } , After hours: { after_hours } " )
print ( f " Live testing: { live_testing } " )
print ( f " Current time: { current_time } " )