From 29b43d29ed89359c708714e3aee8117dcb8f5e93 Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 1 Nov 2024 01:59:19 +1100 Subject: [PATCH 1/5] Fix issue with call date not timezoned + renamed TeamList into TeamItem --- tournaments/models/__init__.py | 2 +- tournaments/models/team_registration.py | 7 +++++++ tournaments/models/tournament.py | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tournaments/models/__init__.py b/tournaments/models/__init__.py index ced18b9..ff8321d 100644 --- a/tournaments/models/__init__.py +++ b/tournaments/models/__init__.py @@ -5,7 +5,7 @@ from .date_interval import DateInterval from .enums import TournamentPayment, FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory from .player_enums import PlayerSexType, PlayerDataSource, PlayerPaymentType from .event import Event -from .tournament import Tournament, TeamSummon, TeamSortingType, TeamList +from .tournament import Tournament, TeamSummon, TeamSortingType, TeamItem from .group_stage import GroupStage from .round import Round from .match import Match, LiveMatch diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 7736713..51b7d3f 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -90,3 +90,10 @@ class TeamRegistration(models.Model): return self.weight else: return self.locked_weight + + def local_call_date(self): + timezone = self.tournament.timezone() + if self.call_date: + return self.call_date.astimezone(timezone) + else: + return None diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 2fde8c6..a46b033 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -299,7 +299,7 @@ class Tournament(models.Model): # print(f"Is valid: {is_valid}") if team_registration.walk_out is False: - team = TeamList(team_registration) + team = TeamItem(team_registration) # print(f"Created team: {team}") if team_registration.group_stage_position is not None: team.set_stage("Poule") @@ -880,10 +880,10 @@ class TeamSummon: "image": self.image, } -class TeamList: +class TeamItem: def __init__(self, team_registration): self.names = team_registration.team_names - self.date = team_registration.call_date + self.date = team_registration.local_call_date() self.weight = team_registration.weight self.initial_weight = team_registration.initial_weight() self.image = team_registration.logo From 255c86a8b4dc994e437d9f739aa1bf053a4136aa Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 31 Oct 2024 18:22:45 +0100 Subject: [PATCH 2/5] add winner from previous match title, handle one player in team label, fix magic duration, fix round sorting in broadcast content --- tournaments/models/match.py | 127 +++++++++++++++--- tournaments/models/round.py | 7 + tournaments/models/team_registration.py | 6 +- tournaments/models/team_score.py | 21 ++- tournaments/models/tournament.py | 8 +- .../broadcast/broadcasted_match.html | 2 +- .../templates/tournaments/match_cell.html | 8 +- 7 files changed, 156 insertions(+), 23 deletions(-) diff --git a/tournaments/models/match.py b/tournaments/models/match.py index cb42958..8a9123e 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -52,10 +52,10 @@ class Match(models.Model): items = [] if self.round: items.append(self.round.name()) - items.append(f" #{self.index}") + items.append(f" #{self.index_in_round() + 1}") elif self.group_stage: items.append(self.group_stage.name()) - items.append(f"Match #{self.index}") + items.append(f"Match #{self.index + 1}") return " ".join(items) def summon_stage_name(self): @@ -76,9 +76,116 @@ class Match(models.Model): else: return '--' + def get_previous_round(self): + # Calculate the next index + if self.round is None: + return None + + previous_index = self.round.index + 1 + + # Check if the next index is within the bounds of the rounds array + if previous_index < len(self.round.tournament.round_set.all()): + return self.round.tournament.round_set.filter(index=previous_index, parent = None).first() + + # Return None or an appropriate value if the index is out of bounds + return None + + def precedent_match(self, top): + previous_round = self.get_previous_round() + #print(previous_round) + match = None + if previous_round: + matches = previous_round.match_set.all() # Retrieve the QuerySet + match_index = self.index * 2 + 1 + if top == False: + match_index += 1 + match = matches.filter(index=match_index).first() + return match + + def computed_name(self): + if self.round and self.round.parent is None: + return self.backup_name() + title = self.name if self.name else self.backup_name() + return title + + def index_in_round(self): + if self.round is not None: + matches = sorted( + self.round.match_set.filter(disabled=False), + key=lambda match: match.index + ) + try: + index_of_self = matches.index(self) + return index_of_self + except ValueError: + # `self` is not in `matches`, return self.index as a fallback + return self.index + + return self.index + + def player_names(self): return map(lambda ts: ts.player_names(), self.team_scores.all()) + def default_live_team(self, names): + image = None + weight= None + is_winner = False + scores = [] + walk_out = None + team = Team(image, names, scores, weight, is_winner, walk_out) + return team + + def live_teams(self): + #print('player names from match') + ##return map(lambda ts: ts.player_names(), self.team_scores.all()) + # List to hold the names of the teams + teams = [] + + # Check if team scores exist + team_scores = list(self.team_scores.all()) + previous_top_match = self.precedent_match(True) + previous_bottom_match = self.precedent_match(False) + if len(team_scores) == 0: + if (self.round and len(self.round.tournament.round_set.all()) == self.round.index -1): + return teams + if (self.group_stage): + return teams + + if self.round and self.round.parent: + return teams + + # No team scores at all + if previous_top_match: + names = [f"Gagnants du {previous_top_match.computed_name()}", ''] + team = self.default_live_team(names) + teams.append(team) + if previous_bottom_match: + names = [f"Gagnants du {previous_bottom_match.computed_name()}", ''] + team = self.default_live_team(names) + teams.append(team) + elif len(team_scores) == 1: + # Only one team score, handle missing one + existing_team = team_scores[0].live_team(self) + + if previous_top_match and previous_top_match.disabled == False and previous_top_match.end_date is None: + names = [f"Gagnants du {previous_top_match.computed_name()}", ''] + team = self.default_live_team(names) + teams.append(team) + teams.append(existing_team) + elif previous_bottom_match: + names = [f"Gagnants du {previous_bottom_match.computed_name()}", ''] + team = self.default_live_team(names) + teams.append(existing_team) + teams.append(team) + else: + teams.append(existing_team) + else: + # Both team scores present + teams.extend([team_score.live_team(self) for team_score in team_scores]) + + return teams + def local_start_date(self): timezone = self.tournament().timezone() return self.start_date.astimezone(timezone) @@ -194,7 +301,7 @@ class Match(models.Model): # return f"{_hours:02d}h{_minutes:02d}min" def live_match(self): - title = self.name if self.name else self.backup_name() + title = self.computed_name() date = self.formatted_start_date() time_indication = self.time_indication() court = self.court_name(self.court_index) @@ -205,19 +312,7 @@ class Match(models.Model): ended = self.end_date is not None livematch = LiveMatch(title, date, time_indication, court, self.started(), ended, group_stage_name) - for team_score in self.sorted_team_scores(): - if team_score.team_registration: - image = team_score.team_registration.logo - weight = team_score.team_registration.weight - is_winner = team_score.team_registration.id == self.winning_team_id - else: - image = None - weight= None - is_winner = False - names = team_score.shortened_team_names() - scores = team_score.scores_array() - walk_out = team_score.walk_out - team = Team(image, names, scores, weight, is_winner, walk_out) + for team in self.live_teams(): livematch.add_team(team) return livematch diff --git a/tournaments/models/round.py b/tournaments/models/round.py index 4f5a348..feca1d0 100644 --- a/tournaments/models/round.py +++ b/tournaments/models/round.py @@ -77,3 +77,10 @@ class Round(models.Model): return self else: return self.parent.root_round() + + def all_matches_are_over(self): + for match in self.match_set.all(): + if match.end_date is None and match.disabled is False: + return False + + return True diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 7736713..7f2b5d4 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -47,7 +47,11 @@ class TeamRegistration(models.Model): if self.name: return [self.name] else: - return [pr.shortened_name() for pr in self.playerregistration_set.all()] + players = list(self.playerregistration_set.all()) + if len(players) == 1: + return [players[0].shortened_name(), ''] + else: + return [pr.shortened_name() for pr in players] def player_names(self): names = [pr.name() for pr in self.playerregistration_set.all()] diff --git a/tournaments/models/team_score.py b/tournaments/models/team_score.py index 41852f0..3c510b2 100644 --- a/tournaments/models/team_score.py +++ b/tournaments/models/team_score.py @@ -56,12 +56,31 @@ class TeamScore(models.Model): games = 0.0 if self.match.format: format = self.match.format + if format == FederalMatchCategory.TWO_SETS_OF_SUPER_TIE: + games = sum(scores) / 4 + return games if FederalMatchCategory.last_set_is_tie_break(format) and len(scores) == FederalMatchCategory.max_number_of_sets(format): points = scores.pop() - games += points / 7 # we take 7 points in average for a game + games += points / 4 # we take 4 points in average for a game games += sum(scores) return games def number_of_games(self): scores = self.scores() return sum(scores) + + def live_team(self, match): + if self.team_registration: + image = self.team_registration.logo + weight = self.team_registration.weight + is_winner = self.team_registration.id == match.winning_team_id + else: + image = None + weight= None + is_winner = False + names = self.shortened_team_names() + scores = self.scores_array() + walk_out = self.walk_out + from .match import Team # Import Team only when needed + team = Team(image, names, scores, weight, is_winner, walk_out) + return team diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 2fde8c6..5f78852 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -585,19 +585,23 @@ class Tournament(models.Model): else: current_round = self.round_to_show() if current_round: + all_upper_matches_are_over = current_round.all_matches_are_over() + if all_upper_matches_are_over is False: + matches.extend(current_round.get_matches_recursive(True)) # Add full matches from the next rounds next_round = self.round_for_index(current_round.index - 1) if next_round: matches.extend(next_round.get_matches_recursive(True)) + if all_upper_matches_are_over is True: + matches.extend(current_round.get_matches_recursive(True)) + # Add matches from the previous round or group_stages previous_round = self.round_for_index(current_round.index + 1) if previous_round: - matches.extend(current_round.get_matches_recursive(True)) matches.extend(previous_round.get_matches_recursive(True)) else: - matches.extend(current_round.all_matches(True)) group_stages = [gs.live_group_stages() for gs in self.last_group_stage_step()] return matches, group_stages diff --git a/tournaments/templates/tournaments/broadcast/broadcasted_match.html b/tournaments/templates/tournaments/broadcast/broadcasted_match.html index 295b181..1878786 100644 --- a/tournaments/templates/tournaments/broadcast/broadcasted_match.html +++ b/tournaments/templates/tournaments/broadcast/broadcasted_match.html @@ -15,7 +15,7 @@
diff --git a/tournaments/templates/tournaments/match_cell.html b/tournaments/templates/tournaments/match_cell.html index 984bd81..cbed073 100644 --- a/tournaments/templates/tournaments/match_cell.html +++ b/tournaments/templates/tournaments/match_cell.html @@ -16,7 +16,11 @@
{% for name in team.names %}
- {{ name }} + {% if name|length > 0 %} + {{ name }} + {% else %} +   + {% endif %}
{% endfor %}
@@ -31,7 +35,7 @@ {% if team.walk_out %}WO{% endif %} - {% elif not tournament.hide_weight %} + {% elif not tournament.hide_weight and team.weight %} {{ team.weight }} {% endif %} From dc683b0762d8e2792a4afaee4af1b5ef58a45390 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 31 Oct 2024 18:32:37 +0100 Subject: [PATCH 3/5] fix LB sorting --- tournaments/models/round.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tournaments/models/round.py b/tournaments/models/round.py index feca1d0..f1b47f0 100644 --- a/tournaments/models/round.py +++ b/tournaments/models/round.py @@ -67,11 +67,22 @@ class Round(models.Model): matches.sort(key=lambda m: m.index) # Recursively fetch matches from child rounds - for child_round in self.children.all(): + # Sort children by depth in ascending order (deeper rounds last) + sorted_children = sorted(self.children.all(), key=lambda child: child.get_depth()) + + for child_round in sorted_children: matches.extend(child_round.get_matches_recursive(hide_empty_matches)) return matches + def get_depth(self): + depth = 0 + current_round = self + while current_round.parent: + depth += 1 + current_round = current_round.parent + return depth + def root_round(self): if self.parent is None: return self From ab37b7b1896f8fcd081431a459a318e28084e77f Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 31 Oct 2024 18:37:25 +0100 Subject: [PATCH 4/5] fix LB sorting --- tournaments/models/round.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tournaments/models/round.py b/tournaments/models/round.py index f1b47f0..a7dee08 100644 --- a/tournaments/models/round.py +++ b/tournaments/models/round.py @@ -68,7 +68,7 @@ class Round(models.Model): # Recursively fetch matches from child rounds # Sort children by depth in ascending order (deeper rounds last) - sorted_children = sorted(self.children.all(), key=lambda child: child.get_depth()) + sorted_children = sorted(self.children.all(), key=lambda child: (child.index, child.get_depth())) for child_round in sorted_children: matches.extend(child_round.get_matches_recursive(hide_empty_matches)) From e797135ee5d9a16f653817192551d83d1e8a2a0c Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 1 Nov 2024 04:58:10 +0100 Subject: [PATCH 5/5] fix LB sorting --- tournaments/models/round.py | 5 +---- tournaments/models/tournament.py | 8 +++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tournaments/models/round.py b/tournaments/models/round.py index a7dee08..befe57a 100644 --- a/tournaments/models/round.py +++ b/tournaments/models/round.py @@ -67,10 +67,7 @@ class Round(models.Model): matches.sort(key=lambda m: m.index) # Recursively fetch matches from child rounds - # Sort children by depth in ascending order (deeper rounds last) - sorted_children = sorted(self.children.all(), key=lambda child: (child.index, child.get_depth())) - - for child_round in sorted_children: + for child_round in self.children.all(): matches.extend(child_round.get_matches_recursive(hide_empty_matches)) return matches diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index ff4d12b..d8782a0 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -469,7 +469,13 @@ class Tournament(models.Model): ranking_matches = [m for m in ranking_matches if m.disabled is False] if len(ranking_matches) > 0: - ranking_matches.sort(key=lambda m: m.index) + ranking_matches.sort( + key=lambda m: ( + m.round.index, # Sort by Round index first + m.round.get_depth(), + m.name, # Then by Round depth + ) + ) group = self.create_match_group('Matchs de classement', ranking_matches) groups.append(group)