diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py index 666aca2..c3f3be6 100644 --- a/tournaments/models/enums.py +++ b/tournaments/models/enums.py @@ -96,6 +96,26 @@ class FederalMatchCategory(models.IntegerChoices): SINGLE_SET_OF_FOUR_GAMES = 13, 'Single set of four games' SINGLE_SET_OF_FOUR_GAMES_DECISIVE_POINT = 14, 'Single set of four games with decisive point' + @property + def format_label_short(self): + format_mapping = { + self.TWO_SETS: "A1", + self.TWO_SETS_SUPER_TIE: "B1", + self.TWO_SETS_FOUR_GAME: "C1", + self.NINE_GAMES: "D1", + self.SUPER_TIE: "E", + self.TWO_SETS_OF_SUPER_TIE: "G", + self.MEGA_TIE: "F", + self.SINGLE_SET: "H1", + self.SINGLE_SET_DECISIVE_POINT: "H2", + self.TWO_SETS_DECISIVE_POINT: "A2", + self.TWO_SETS_DECISIVE_POINT_SUPER_TIE: "B2", + self.TWO_SETS_FOUR_GAME_DECISIVE_POINT: "C2", + self.NINE_GAMES_DECISIVE_POINT: "D2", + self.SINGLE_SET_OF_FOUR_GAMES: "I1" + } + return format_mapping.get(self, "") + def last_set_is_tie_break(value): if value == FederalMatchCategory.TWO_SETS_FOUR_GAME or value == FederalMatchCategory.TWO_SETS_FOUR_GAME_DECISIVE_POINT or value == FederalMatchCategory.TWO_SETS_SUPER_TIE or value == FederalMatchCategory.SUPER_TIE or value == FederalMatchCategory.MEGA_TIE or value == FederalMatchCategory.TWO_SETS_DECISIVE_POINT_SUPER_TIE: return True diff --git a/tournaments/models/match.py b/tournaments/models/match.py index acdf519..b991533 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -318,7 +318,9 @@ class Match(models.Model): group_stage_name = self.group_stage.display_name() ended = self.end_date is not None - livematch = LiveMatch(title, date, time_indication, court, self.started(), ended, group_stage_name) + live_format = "Format " + FederalMatchCategory(self.format).format_label_short + + livematch = LiveMatch(title, date, time_indication, court, self.started(), ended, group_stage_name, live_format) for team in self.live_teams(): livematch.add_team(team) @@ -369,7 +371,7 @@ class Team: } class LiveMatch: - def __init__(self, title, date, time_indication, court, started, ended, group_stage_name): + def __init__(self, title, date, time_indication, court, started, ended, group_stage_name, format): self.title = title self.date = date self.teams = [] @@ -379,6 +381,7 @@ class LiveMatch: self.ended = ended self.has_walk_out = False self.group_stage_name = group_stage_name + self.format = format def add_team(self, team): self.teams.append(team) @@ -396,6 +399,7 @@ class LiveMatch: "ended": self.ended, "has_walk_out": self.has_walk_out, "group_stage_name": self.group_stage_name, + "format": self.format } def show_time_indication(self): diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 1586883..656572f 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -172,10 +172,32 @@ class TeamRegistration(models.Model): 'points_earned': self.get_points_earned(), 'initial_stage': self.get_initial_stage(), 'matches_played': self.count_matches_played(), - 'victory_ratio': self.calculate_victory_ratio() + 'victory_ratio': self.calculate_victory_ratio(), + 'team_rank': self.team_rank_label(), + 'total_teams': self.total_teams(), } return stats + def team_rank(self): + teams = self.tournament.teams(False) # Get list of TeamItem objects + try: + # Find the TeamItem that corresponds to this TeamRegistration + team_index = next(i for i, team in enumerate(teams) + if team.team_registration.id == self.id) + return team_index + 1 + except (StopIteration, ValueError): + return None + + def team_rank_label(self): + team_rank = self.team_rank() + if team_rank is None: + return "--" + return f"{team_rank}" + + def total_teams(self): + teams = self.tournament.teams(False) + return f"{len(teams)}" + def get_initial_stage(self): matches = self.get_matches().order_by('start_date') first_match = matches.first() @@ -190,10 +212,22 @@ class TeamRegistration(models.Model): def get_final_ranking(self): if self.final_ranking: if self.final_ranking == 1: - return "1er" - return f"{self.final_ranking}e" + return "1er" + self.ranking_delta() + return f"{self.final_ranking}ème" + self.ranking_delta() return None + def ranking_delta(self): + team_rank = self.team_rank() + if team_rank is None or self.final_ranking is None: + return "" + + sign = "-" + if team_rank > self.final_ranking: + sign = "+" + if team_rank == self.final_ranking: + sign = "" + return f" ({sign}"+f"{abs(self.final_ranking - team_rank)})" + def get_points_earned(self): return self.points_earned @@ -220,6 +254,6 @@ class TeamRegistration(models.Model): total_matches = matches.count() if total_matches > 0: wins = matches.filter(winning_team_id=self.id).count() - ratio = (wins / total_matches) * 100 + # ratio = (wins / total_matches) * 100 return f"{wins}/{total_matches}" return None diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 8bed207..448bc8c 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -324,126 +324,94 @@ class Tournament(models.Model): print("else", index, self.team_count) return -1 - def teams(self, includeWaitingList): - # print("Starting teams method") - bracket_teams = [] - group_stage_teams = [] - waiting_teams = [] - teams = [] - wildcard_bracket = [] - wildcard_group_stage = [] - complete_teams = [] - closed_registration_date = self.closed_registration_date - # print(f"Closed registration date: {closed_registration_date}") - - for team_registration in self.teamregistration_set.all(): - # print(f"Processing team registration: {team_registration}") - is_valid = False - if closed_registration_date is not None and team_registration.registration_date is not None and team_registration.registration_date <= closed_registration_date: - is_valid = True - if closed_registration_date is None: - is_valid = True - if team_registration.registration_date is None: - is_valid = True - # print(f"Is valid: {is_valid}") - if team_registration.out_of_tournament() is False: - team = TeamItem(team_registration) - # print(f"Created team: {team}") - if team_registration.group_stage_position is not None: - team.set_stage("Poule") - elif team_registration.bracket_position is not None: - team.set_stage("Tableau") - else: - team.set_stage("Attente") - # print(f"Team stage: {team.stage}") - - teams.append(team) - if team_registration.wild_card_bracket: - wildcard_bracket.append(team) - elif team_registration.wild_card_group_stage: - wildcard_group_stage.append(team) - elif is_valid is True: - complete_teams.append(team) - else: - waiting_teams.append(team) - - # print(f"Total teams: {len(teams)}") - # print(f"Wildcard bracket: {len(wildcard_bracket)}") - # print(f"Wildcard group stage: {len(wildcard_group_stage)}") - # print(f"Complete teams: {len(complete_teams)}") - # print(f"Waiting teams: {len(waiting_teams)}") - - if len(teams) < self.team_count: - teams.sort(key=lambda s: (s.initial_weight, s.team_registration.id)) - return teams - - seeds_count = min(self.team_count, len(teams)) - self.group_stage_count * self.teams_per_group_stage - len(wildcard_bracket) - group_stage_members_count = self.group_stage_count * self.teams_per_group_stage - len(wildcard_group_stage) - if group_stage_members_count < 0: - group_stage_members_count = 0 - if seeds_count < 0: - seeds_count = 0 - # print(f"Seeds count: {seeds_count}") - # print(f"Group stage members count: {group_stage_members_count}") - - if self.team_sorting == TeamSortingType.INSCRIPTION_DATE: - complete_teams.sort(key=lambda s: (s.registration_date is None, s.registration_date or datetime.min, s.initial_weight, s.team_registration.id)) - else: - complete_teams.sort(key=lambda s: (s.initial_weight, s.team_registration.id)) + def teams(self, include_waiting_list): + """ + Get sorted list of teams for the tournament. - selected_teams = complete_teams[:self.team_count] - selected_teams.sort(key=lambda s: (s.initial_weight, s.team_registration.id)) + Args: + include_waiting_list (bool): Whether to include teams in waiting list - if seeds_count > 0: - bracket_teams = selected_teams[:seeds_count] + wildcard_bracket - else: - bracket_teams = [] - # print(f"Bracket teams: {len(bracket_teams)}") + Returns: + list: List of TeamItem objects sorted according to tournament rules + """ + # Initialize team categories + complete_teams = [] + wildcard_bracket = [] + wildcard_group_stage = [] + waiting_teams = [] - if group_stage_members_count: - group_stage_end = seeds_count + group_stage_members_count - group_stage_teams = selected_teams[seeds_count:group_stage_end] + wildcard_group_stage - else: - group_stage_teams = [] - # print(f"Group stage teams: {len(group_stage_teams)}") - - waiting_list_count = len(teams) - self.team_count - if waiting_list_count < 0: - waiting_list_count = 0 - # print(f"Waiting list count: {waiting_list_count}") - - if waiting_list_count > 0 or len(waiting_teams) > 0: - if waiting_list_count > 0: - waiting_teams = waiting_teams + complete_teams[-waiting_list_count:] - if self.team_sorting == TeamSortingType.INSCRIPTION_DATE: - waiting_teams.sort(key=lambda s: (s.registration_date is None, s.registration_date or datetime.min, s.initial_weight, s.team_registration.id)) + # Get registration cutoff date + closed_date = self.closed_registration_date + + # Process each team registration + for team_reg in self.teamregistration_set.all(): + if team_reg.out_of_tournament(): + continue + + # Create team item + team = TeamItem(team_reg) + + # Determine if registration is valid based on date + is_valid = ( + closed_date is None or + team_reg.registration_date is None or + (team_reg.registration_date and team_reg.registration_date <= closed_date) + ) + + # Categorize team + if team_reg.wild_card_bracket: + wildcard_bracket.append(team) + elif team_reg.wild_card_group_stage: + wildcard_group_stage.append(team) + elif is_valid: + complete_teams.append(team) else: - waiting_teams.sort(key=lambda s: (s.initial_weight, s.team_registration.id)) - else: - waiting_teams = [] - # print(f"Final waiting teams: {len(waiting_teams)}") - - bracket_teams.sort(key=lambda s: (s.weight, s.team_registration.id)) - group_stage_teams.sort(key=lambda s: (s.weight, s.team_registration.id)) - - for team in bracket_teams: - if team.stage == "Attente": - team.set_stage("Tableau") + waiting_teams.append(team) - for team in group_stage_teams: - if team.stage == "Attente": + # Set initial stage + if team_reg.group_stage_position is not None: team.set_stage("Poule") + elif team_reg.bracket_position is not None: + team.set_stage("Tableau") + else: + team.set_stage("Attente") - for team in waiting_teams: - team.set_stage("Attente") - - if includeWaitingList is True: - final_teams = bracket_teams + group_stage_teams + waiting_teams - # print(f"Final teams with waiting list: {len(final_teams)}") + # Sort teams based on tournament rules + if self.team_sorting == TeamSortingType.INSCRIPTION_DATE: + complete_teams.sort(key=lambda t: ( + t.registration_date is None, + t.registration_date or datetime.min, + t.initial_weight, + t.team_registration.id + )) + waiting_teams.sort(key=lambda t: ( + t.registration_date is None, + t.registration_date or datetime.min, + t.initial_weight, + t.team_registration.id + )) else: - final_teams = bracket_teams + group_stage_teams - # print(f"Final teams without waiting list: {len(final_teams)}") - return final_teams + complete_teams.sort(key=lambda t: (t.initial_weight, t.team_registration.id)) + waiting_teams.sort(key=lambda t: (t.initial_weight, t.team_registration.id)) + + # Determine final team list based on tournament settings + if len(complete_teams) <= self.team_count: + all_teams = wildcard_bracket + wildcard_group_stage + complete_teams + if include_waiting_list: + all_teams.extend(waiting_teams) + return all_teams + + # Split teams into main bracket and waiting list + qualified_teams = complete_teams[:self.team_count] + excess_teams = complete_teams[self.team_count:] + + # Combine all waiting list teams + waiting_list = excess_teams + waiting_teams + + # Return final sorted list + if include_waiting_list: + return wildcard_bracket + wildcard_group_stage + qualified_teams + waiting_list + return wildcard_bracket + wildcard_group_stage + qualified_teams def match_groups(self, broadcasted, group_stage_id, round_id): diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index bbb3d49..6b28ce4 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -246,6 +246,7 @@ tr { font-family: "Anybody-ExtraBold"; font-size: 1.2em; color: #1b223a; + line-height: 24px; /* Match the height of flex-row */ } .title { @@ -816,3 +817,12 @@ h-margin { .group-stage-link:hover { color: #f39200; /* Or whatever hover color you prefer */ } + +.tournament-info a { + color: #f39200; + text-decoration: underline; + font-weight: bold; +} +.tournament-info a:hover { + color: #f39200; +} diff --git a/tournaments/templates/tournaments/broadcast/broadcasted_match.html b/tournaments/templates/tournaments/broadcast/broadcasted_match.html index ef51dd8..9e3d0cc 100644 --- a/tournaments/templates/tournaments/broadcast/broadcasted_match.html +++ b/tournaments/templates/tournaments/broadcast/broadcasted_match.html @@ -2,9 +2,13 @@
- +