diff --git a/tournaments/models/match.py b/tournaments/models/match.py index 9caada4..3bc686a 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -52,7 +52,8 @@ class Match(models.Model): items = [] if self.round: items.append(self.round.name()) - items.append(f" #{self.index_in_round() + 1}") + if self.round.index > 0: + items.append(f" #{self.index_in_round() + 1}") elif self.group_stage: items.append(self.group_stage.name()) items.append(f"Match #{self.index + 1}") @@ -133,7 +134,7 @@ class Match(models.Model): is_winner = False scores = [] walk_out = None - team = Team(image, names, scores, weight, is_winner, walk_out) + team = Team(None, image, names, scores, weight, is_winner, walk_out) return team def live_teams(self): @@ -347,8 +348,9 @@ class Match(models.Model): # return sort_score class Team: - def __init__(self, image, names, scores, weight, is_winner, walk_out): + def __init__(self, id, image, names, scores, weight, is_winner, walk_out): # print(f"image = {image}, names= {names}, scores ={scores}, weight={weight}, win={is_winner}") + self.id = id self.image = image self.names = names self.scores = scores @@ -358,6 +360,7 @@ class Team: def to_dict(self): return { + "id": self.id, "image": self.image, "names": self.names, "scores": self.scores, diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index b35badb..32fd930 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -1,6 +1,7 @@ from django.db import models from . import TeamRegistration, PlayerSexType, PlayerDataSource, PlayerPaymentType import uuid +from django.utils import timezone class PlayerRegistration(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) @@ -48,3 +49,30 @@ class PlayerRegistration(models.Model): name_parts = self.last_name.split(" ") name = f"{self.first_name[0]}. {name_parts[0]}" return name + + def clean_club_name(self): + if self.club_name: + return self.club_name.split(' (')[0] + return "Non renseigné" + + def calculate_age(self): + if self.birthdate: + try: + birth_year = int(self.birthdate[-4:]) # Assumes birthdate ends with YYYY + current_year = timezone.now().year + return current_year - birth_year + except (ValueError, TypeError, IndexError): + return None + return None + + def format_ordinal(self): + if self.rank is None: + if self.sex == PlayerSexType.FEMALE: + return "Non Classée" + return "Non Classé" + + if self.rank == 1: + if self.sex == PlayerSexType.FEMALE: + return "1ère" + return "1er" + return f"{self.rank}ème" diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 272163d..d1c4545 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -3,6 +3,7 @@ from django.db.models.sql.query import Q from . import Tournament, GroupStage, Match import uuid from django.utils import timezone +from django.db.models import Count class TeamRegistration(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) @@ -61,6 +62,15 @@ class TeamRegistration(models.Model): else: return "no players" + def formatted_team_names(self): + if self.name: + return self.name + names = [pr.last_name for pr in self.playerregistration_set.all()][:2] # Take max first 2 + joined_names = " / ".join(names) + if joined_names: + return f"Paire {joined_names}" + return "Détail de l'équipe" + def next_match(self): all_matches = [ts.match for ts in self.teamscore_set.all()] now = timezone.now() @@ -102,3 +112,81 @@ class TeamRegistration(models.Model): else: # print("no date") return None + + def get_matches(self): + matches = Match.objects.filter(team_scores__team_registration=self).distinct() + print(f"All matches for team {self.id}: {matches.count()}") + for match in matches: + print(f"Match {match.id}: start_date={match.start_date}, end_date={match.end_date}") + return matches + + def get_upcoming_matches(self): + matches = self.get_matches() + upcoming = matches.filter(end_date__isnull=True).order_by('start_date') + print(f"Upcoming matches count: {upcoming.count()}") + return [match.live_match() for match in upcoming] + + def get_completed_matches(self): + matches = self.get_matches() + completed = matches.filter(end_date__isnull=False).order_by('-end_date') + print(f"Completed matches count: {completed.count()}") + return [match.live_match() for match in completed] + + def get_statistics(self): + + stats = { + 'final_ranking': self.get_final_ranking(), + 'points_earned': self.get_points_earned(), + 'initial_stage': self.get_initial_stage(), + 'matches_played': self.count_matches_played(), + 'victory_ratio': self.calculate_victory_ratio() + } + return stats + + def get_initial_stage(self): + matches = self.get_matches().order_by('start_date') + first_match = matches.first() + if first_match: + if first_match.group_stage: + return "Poule" + elif first_match.round: + return first_match.round.name() + return None + + + def get_final_ranking(self): + if self.final_ranking: + if self.final_ranking == 1: + return "1er" + return f"{self.final_ranking}e" + return None + + def get_points_earned(self): + return self.points_earned + + def calculate_total_duration(self): + total_seconds = 0 + for match in self.get_matches().filter(end_date__isnull=False): + if match.start_date and match.end_date: + duration = (match.end_date - match.start_date).total_seconds() + total_seconds += duration + + if total_seconds > 0: + hours = int(total_seconds // 3600) + minutes = int((total_seconds % 3600) // 60) + if hours > 0: + return f"{hours}h{minutes:02d}" + return f"{minutes}min" + return None + + def count_matches_played(self): + return self.get_matches().filter(end_date__isnull=False).count() + + def calculate_victory_ratio(self): + matches = self.get_matches().filter(end_date__isnull=False) + total_matches = matches.count() + if total_matches > 0: + wins = matches.filter(winning_team_id=self.id).count() + ratio = (wins / total_matches) * 100 + return f"{wins}/{total_matches}" + return None diff --git a/tournaments/models/team_score.py b/tournaments/models/team_score.py index 3c510b2..fdbde13 100644 --- a/tournaments/models/team_score.py +++ b/tournaments/models/team_score.py @@ -71,10 +71,12 @@ class TeamScore(models.Model): def live_team(self, match): if self.team_registration: + id = self.team_registration.id image = self.team_registration.logo weight = self.team_registration.weight is_winner = self.team_registration.id == match.winning_team_id else: + id = None image = None weight= None is_winner = False @@ -82,5 +84,5 @@ class TeamScore(models.Model): 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) + team = Team(id, image, names, scores, weight, is_winner, walk_out) return team diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index a0123b2..64f91fd 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -239,7 +239,7 @@ class Tournament(models.Model): names = team.names stage = team.stage weight = team.weight - summon = TeamSummon(names, team.date, weight, stage, "", team.image, self.day_duration) + summon = TeamSummon(team.team_registration.id, names, team.date, weight, stage, "", team.image, self.day_duration) summons.append(summon) else: print('>>> team_summons') @@ -250,7 +250,7 @@ class Tournament(models.Model): names = team_registration.team_names() stage = next_match.summon_stage_name() weight = team_registration.weight - summon = TeamSummon(names, next_match.local_start_date(), weight, stage, next_match.court_name(next_match.court_index), team_registration.logo, self.day_duration) + summon = TeamSummon(team_registration.id, names, next_match.local_start_date(), weight, stage, next_match.court_name(next_match.court_index), team_registration.logo, self.day_duration) summons.append(summon) summons.sort(key=lambda s: (s.date is None, s.date or datetime.min)) @@ -271,7 +271,7 @@ class Tournament(models.Model): names = team_registration.team_names() ranking = team_registration.final_ranking points = team_registration.points_earned - team = TeamRanking(names, ranking, points, team_registration.logo) + team = TeamRanking(team_registration.id, names, ranking, points, team_registration.logo) rankings.append(team) rankings.sort(key=lambda r: r.ranking) @@ -750,9 +750,8 @@ class Tournament(models.Model): return group_stages def display_rankings(self): - if self.publish_rankings is True and self.end_date is not None: - return True - return False + return True + def display_tournament(self): if self.publish_tournament: @@ -910,7 +909,8 @@ class MatchGroup: self.matches = matches class TeamSummon: - def __init__(self, names, date, weight, stage, court, image, day_duration): + def __init__(self, id, names, date, weight, stage, court, image, day_duration): + self.id = id self.names = names self.date = date self.weight = weight @@ -930,6 +930,7 @@ class TeamSummon: def to_dict(self): return { + "id": self.id, "names": self.names, "date": self.formatted_date(), "weight": self.weight, @@ -968,7 +969,8 @@ class TeamItem: } class TeamRanking: - def __init__(self, names, ranking, points, image): + def __init__(self, id, names, ranking, points, image): + self.id = id self.names = names self.ranking = ranking self.formatted_ranking = self.ordinal(ranking) @@ -999,6 +1001,7 @@ class TeamRanking: def to_dict(self): return { + "id": self.id, "names": self.names, "ranking": self.ranking, "formatted_ranking": self.formatted_ranking, diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index 7f0e7a1..9c1b320 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -714,3 +714,22 @@ h-margin { .right-content { margin-left: auto; } + +.match-result a { + text-decoration: none; + color: inherit; + display: block; + padding: 2px 8px; + border-radius: 6px; +} + +.match-result a:hover { + background-color: #fae7ce; + color: #707070; +} + +.single-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/tournaments/templates/tournaments/match_cell.html b/tournaments/templates/tournaments/match_cell.html index cbed073..bcbeb05 100644 --- a/tournaments/templates/tournaments/match_cell.html +++ b/tournaments/templates/tournaments/match_cell.html @@ -11,9 +11,11 @@
{% for team in match.teams %} -
{% if match.should_show_scores %} diff --git a/tournaments/templates/tournaments/player_row.html b/tournaments/templates/tournaments/player_row.html new file mode 100644 index 0000000..6b74622 --- /dev/null +++ b/tournaments/templates/tournaments/player_row.html @@ -0,0 +1,61 @@ +
+
+ + +
+
+
+
+ {{ player.clean_club_name }} +
+
+
+ +
+
+
+ Classement +
+
+
+ {{ player.format_ordinal }} +
+
+ +
+
+
+ Age +
+
+
+ + {{ player.calculate_age|default:"?" }} ans + +
+
+ +
+
+
+ Points +
+
+
+ {{ player.points|default:"0" }} pts +
+
+ +
+
+
+ Tournois joués +
+
+
+ {{ player.tournament_played|default:"0" }} +
+
+
+
+
diff --git a/tournaments/templates/tournaments/ranking_row.html b/tournaments/templates/tournaments/ranking_row.html index c32684c..89a9fac 100644 --- a/tournaments/templates/tournaments/ranking_row.html +++ b/tournaments/templates/tournaments/ranking_row.html @@ -4,9 +4,19 @@
{{ ranking.formatted_ranking }}
{% if tournament.display_points_earned %} diff --git a/tournaments/templates/tournaments/rankings.html b/tournaments/templates/tournaments/rankings.html index 1883a87..aa8fe71 100644 --- a/tournaments/templates/tournaments/rankings.html +++ b/tournaments/templates/tournaments/rankings.html @@ -12,26 +12,29 @@ {% include 'tournaments/navigation_tournament.html' %} - {% if rankings %} +
+
+
-
- -
-
- + {% if rankings %} + {% for ranking in rankings %} - - {% include 'tournaments/ranking_row.html' %} - + {% include 'tournaments/ranking_row.html' %} {% endfor %} + {% else %} +
+
+
+ Aucun classement disponible +
+
+
+ {% endif %} -
-
- - {% endif %} +
{% endblock %} {% endif %} diff --git a/tournaments/templates/tournaments/summon_row.html b/tournaments/templates/tournaments/summon_row.html index 86139c9..a8ebd53 100644 --- a/tournaments/templates/tournaments/summon_row.html +++ b/tournaments/templates/tournaments/summon_row.html @@ -4,9 +4,19 @@
diff --git a/tournaments/templates/tournaments/team_details.html b/tournaments/templates/tournaments/team_details.html new file mode 100644 index 0000000..4198121 --- /dev/null +++ b/tournaments/templates/tournaments/team_details.html @@ -0,0 +1,54 @@ +{% extends 'tournaments/base.html' %} + +{% block head_title %}Équipes du {{ tournament.display_name }}{% endblock %} +{% block first_title %}{{ tournament.event.display_name }}{% endblock %} +{% block second_title %}{{ tournament.display_name }}{% endblock %} + +{% block content %} +
+ +
+

{{ team.formatted_team_names }}

+
+ {% for player in team.playerregistration_set.all %} + {% include 'tournaments/player_row.html' with player=player %} + {% endfor %} + {% include 'tournaments/team_stats.html' %} +
+
+
+ +
+ {% with upcoming_matches=team.get_upcoming_matches %} + {% if upcoming_matches %} + +
+

Prochains matchs

+
+ {% for match in upcoming_matches %} + {% include 'tournaments/match_cell.html' %} + {% endfor %} +
+
+ {% endif %} + {% endwith %} + + {% with completed_matches=team.get_completed_matches %} + {% if completed_matches %} + +
+

Matchs terminés

+
+ {% for match in completed_matches %} + {% include 'tournaments/match_cell.html' %} + {% endfor %} +
+
+ {% endif %} + {% endwith %} +
+{% endblock %} diff --git a/tournaments/templates/tournaments/team_row.html b/tournaments/templates/tournaments/team_row.html index a9c71ef..197b3c0 100644 --- a/tournaments/templates/tournaments/team_row.html +++ b/tournaments/templates/tournaments/team_row.html @@ -8,9 +8,18 @@ {% endif %} {% if team.names %} {% else %}
diff --git a/tournaments/templates/tournaments/team_stats.html b/tournaments/templates/tournaments/team_stats.html new file mode 100644 index 0000000..42d44ae --- /dev/null +++ b/tournaments/templates/tournaments/team_stats.html @@ -0,0 +1,72 @@ +
+
+ + +
+ {% with stats=team.get_statistics %} + {% if stats.final_ranking %} +
+
+
+ Classement final +
+
+
+ {{ stats.final_ranking }} +
+
+ {% endif %} + + {% if stats.points_earned %} +
+
+
+ Points gagnés +
+
+
+ {{ stats.points_earned }} pts +
+
+ {% endif %} + + {% if stats.initial_stage %} +
+
+
+ Départ +
+
+
+ {{ stats.initial_stage }} +
+
+ {% endif %} + +
+
+
+ Matchs joués +
+
+
+ {{ stats.matches_played }} +
+
+ + {% if stats.victory_ratio %} +
+
+
+ Ratio victoires +
+
+
+ {{ stats.victory_ratio }} +
+
+ {% endif %} + {% endwith %} +
+
+
diff --git a/tournaments/urls.py b/tournaments/urls.py index 5670262..b42bd03 100644 --- a/tournaments/urls.py +++ b/tournaments/urls.py @@ -28,6 +28,7 @@ urlpatterns = [ path('rankings/', views.tournament_rankings, name='tournament-rankings'), path('rankings/json/', views.tournament_rankings_json, name='tournament-rankings-json'), path('broadcast/rankings/', views.tournament_broadcast_rankings, name='broadcasted-rankings'), + path('team//', views.team_details, name='team-details'), ]) ), path("event//broadcast/auto/", views.automatic_broadcast_event, name='automatic-broadcast-event'), @@ -39,4 +40,5 @@ urlpatterns = [ path('utils/xls-to-csv/', views.xls_to_csv, name='xls-to-csv'), path('mail-test/', views.simple_form_view, name='mail-test'), path('admin/tournament-import/', views.tournament_import_view, name='tournament_import'), + ] diff --git a/tournaments/views.py b/tournaments/views.py index b088cdd..6538b45 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -576,3 +576,21 @@ def get_file_data(zip_file, file_path): except json.JSONDecodeError as e: print(f"JSON Error for {file_path}: {str(e)}") raise Exception(f"Invalid JSON in file {file_path}") + +def team_details(request, tournament_id, team_id): + tournament = get_object_or_404(Tournament, id=tournament_id) + team = get_object_or_404(TeamRegistration, id=team_id) + print(f"Processing team {team_id} in tournament {tournament_id}") + + # Get all matches for this team + all_matches = team.get_matches() + print(f"Total matches found: {all_matches.count()}") + print("Match details:") + for match in all_matches: + print(f"- Match {match.id}: start={match.start_date}, end={match.end_date}") + + return render(request, 'tournaments/team_details.html', { + 'tournament': tournament, + 'team': team, + 'debug': True # Set to False in production + })