@ -1290,12 +1290,20 @@ class Tournament(BaseModel):
def min_player_rank ( self ) :
return FederalLevelCategory . min_player_rank ( self . federal_level_category , self . federal_category , self . federal_age_category )
def first_ waiting_list_team( self , teams ) :
def waiting_list_teams ( self , teams ) :
if len ( teams ) < = self . team_count :
return None
waiting_teams = [ team for team in teams if team . stage == " Attente " ]
if len ( waiting_teams ) > 0 :
return waiting_teams [ 0 ] . team_registration
return waiting_teams
def first_waiting_list_team ( self , teams ) :
waiting_list_team = self . waiting_list_teams ( teams )
if waiting_list_team is None :
return None
if len ( waiting_list_team ) > 0 :
return waiting_list_team [ 0 ] . team_registration
else :
return None
def broadcasted_prog ( self ) :
# Get matches from broadcasted_matches_and_group_stages
@ -1434,6 +1442,153 @@ class Tournament(BaseModel):
return self . umpire_custom_phone
return self . event . creator . phone
def calculate_time_to_confirm ( self , waiting_list_count ) :
"""
Calculate the time a team has to confirm their registration
based on tournament proximity , waiting list pressure , and business hours .
Args :
tournament : The Tournament instance
waiting_list_count : Waiting List count
Returns :
datetime : The confirmation deadline datetime
"""
# Skip if feature not enabled
# if not tournament.has_time_to_confirm:
# return None
if waiting_list_count < = 1 :
return None
# Configuration rules
TIME_PROXIMITY_RULES = {
48 : 30 , # within 48h → 30 min
24 : 20 , # within 24h → 20 min
72 : 60 , # within 72h → 60 min
" default " : 120
}
WAITING_LIST_RULES = {
15 : 15 , # 15+ teams → 15 min
10 : 30 , # 10+ teams → 30 min
5 : 60 , # 5+ teams → 60 min
" default " : 120
}
BUSINESS_RULES = {
" hours " : {
" start " : 8 , # 8:00
" end " : 20 , # 20:00
" default_confirmation_hour " : 10 # When extending to next day
} ,
" days " : {
" working_days " : [ 0 , 1 , 2 , 3 , 4 , 5 , 6 ] , # Monday = 0, Friday = 4
" weekend " : [ ] # Saturday = 5, Sunday = 6
}
}
URGENCY_OVERRIDE = {
" thresholds " : {
24 : True , # If ≤ 24h until tournament: ignore business hours
12 : True # If ≤ 12h until tournament: ignore all restrictions
} ,
" minimum_response_time " : 15 # minutes
}
# 1. Get current time in tournament's timezone
current_time = timezone . now ( )
current_time = current_time . astimezone ( self . timezone ( ) )
tournament_start_date = self . local_start_date ( )
# 2. Calculate tournament proximity (hours until tournament starts)
hours_until_tournament = ( tournament_start_date - current_time ) . total_seconds ( ) / 3600
# 3. Calculate waiting list pressure
# teams = self.teams(True)
# waiting_teams = self.waiting_list_team(teams)
# if waiting_teams is None:
# return None
# waiting_list_count = len(waiting_teams)
# 4. Determine base minutes to confirm based on time proximity
time_based_minutes = TIME_PROXIMITY_RULES [ " default " ]
for hours_threshold , minutes in TIME_PROXIMITY_RULES . items ( ) :
if hours_threshold != " default " and hours_until_tournament < = hours_threshold :
time_based_minutes = minutes
break
# 5. Determine waiting list based minutes
waitlist_based_minutes = WAITING_LIST_RULES [ " default " ]
for teams_threshold , minutes in WAITING_LIST_RULES . items ( ) :
if teams_threshold != " default " and waiting_list_count > = teams_threshold :
waitlist_based_minutes = minutes
break
# 6. Use the more restrictive rule (smaller time window)
minutes_to_confirm = min ( time_based_minutes , waitlist_based_minutes )
# 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 " ] )
break
# 8. Calculate raw deadline
raw_deadline = current_time + timezone . timedelta ( minutes = minutes_to_confirm )
# 9. Round up to next 30-minute mark
minute = raw_deadline . minute
if minute % 30 != 0 :
# Minutes to next 30-minute mark
minutes_to_add = 30 - ( minute % 30 )
raw_deadline + = timezone . timedelta ( minutes = minutes_to_add )
# 10. Apply business hours rules if needed
if apply_business_rules :
# 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 " ]
if is_weekend or before_hours or after_hours :
# Extend to next business day
if after_hours or is_weekend :
# 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
raw_deadline = raw_deadline . replace (
hour = BUSINESS_RULES [ " hours " ] [ " default_confirmation_hour " ] ,
minute = 0 ,
second = 0 ,
microsecond = 0
)
print ( f " Current time: { current_time } " )
print ( f " Minutes to confirm: { minutes_to_confirm } " )
print ( f " Raw deadline before rounding: { current_time + timezone . timedelta ( minutes = minutes_to_confirm ) } " )
print ( f " Raw deadline after rounding: { raw_deadline } " )
print ( f " Is weekend: { is_weekend } , Before hours: { before_hours } , After hours: { after_hours } " )
print ( f " Apply business rules: { apply_business_rules } " )
return raw_deadline
class MatchGroup :
def __init__ ( self , name , matches , formatted_schedule , round_id = None ) :