diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py index c3f3be6..fa2c263 100644 --- a/tournaments/models/enums.py +++ b/tournaments/models/enums.py @@ -15,6 +15,8 @@ class FederalCategory(models.IntegerChoices): @staticmethod def female_in_male_assimilation_addition(rank: int) -> int: + if rank is None: + return 0 if 1 <= rank <= 10: return 400 elif 11 <= rank <= 30: diff --git a/tournaments/models/group_stage.py b/tournaments/models/group_stage.py index b1a6488..11a4b81 100644 --- a/tournaments/models/group_stage.py +++ b/tournaments/models/group_stage.py @@ -135,7 +135,7 @@ class GroupStage(models.Model): def has_at_least_one_started_match(self): for match in self.match_set.all(): - if match.start_date is not None and match.start_date <= timezone.now(): + if match.start_date is not None and match.start_date <= timezone.now() and match.confirmed is True: return True return False diff --git a/tournaments/models/match.py b/tournaments/models/match.py index baca94b..e69aa67 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -180,37 +180,45 @@ class Match(models.Model): if len(team_scores) == 0: if (self.round and self.round.parent is None and self.round.tournament.round_set.filter(parent__isnull=True, group_stage_loser_bracket=False).count() - 1 == self.round.index): - names = ["Qualifié", ''] + names = ["Qualifié"] team = self.default_live_team(names) teams.append(team) - names = ["Qualifié", ''] + names = ["Qualifié"] team = self.default_live_team(names) teams.append(team) return teams if (self.group_stage): - names = ["Équipe de poule", ''] + names = ["Équipe de poule"] team = self.default_live_team(names) teams.append(team) - names = ["Équipe de poule", ''] + names = ["Équipe de poule"] team = self.default_live_team(names) teams.append(team) return teams elif self.round and self.round.parent: if loser_top_match: - names = [f"Perdant {loser_top_match.computed_name()}", ''] + names = [f"Perdant {loser_top_match.computed_name()}"] team = self.default_live_team(names) teams.append(team) if loser_bottom_match: - names = [f"Perdant {loser_bottom_match.computed_name()}", ''] + names = [f"Perdant {loser_bottom_match.computed_name()}"] + team = self.default_live_team(names) + teams.append(team) + if previous_top_match: + names = [f"Gagnant {previous_top_match.computed_name()}"] + team = self.default_live_team(names) + teams.append(team) + if previous_bottom_match: + names = [f"Gagnant {previous_bottom_match.computed_name()}"] team = self.default_live_team(names) teams.append(team) elif self.round and self.round.parent is None: if previous_top_match: - names = [f"Gagnant {previous_top_match.computed_name()}", ''] + names = [f"Gagnant {previous_top_match.computed_name()}"] team = self.default_live_team(names) teams.append(team) if previous_bottom_match: - names = [f"Gagnant {previous_bottom_match.computed_name()}", ''] + names = [f"Gagnant {previous_bottom_match.computed_name()}"] team = self.default_live_team(names) teams.append(team) elif len(team_scores) == 1: @@ -218,33 +226,33 @@ class Match(models.Model): existing_team = team_scores[0].live_team(self) if (self.group_stage): teams.append(existing_team) - names = ["Équipe de poule", ''] + names = ["Équipe de poule"] team = self.default_live_team(names) teams.append(team) elif self.round: if loser_top_match and loser_top_match.disabled == False and loser_top_match.end_date is None: - names = [f"Perdant {loser_top_match.computed_name()}", ''] + names = [f"Perdant {loser_top_match.computed_name()}"] team = self.default_live_team(names) teams.append(team) teams.append(existing_team) elif loser_bottom_match: - names = [f"Perdant {loser_bottom_match.computed_name()}", ''] + names = [f"Perdant {loser_bottom_match.computed_name()}"] team = self.default_live_team(names) teams.append(existing_team) teams.append(team) elif previous_top_match and previous_top_match.disabled == False and previous_top_match.end_date is None: - names = [f"Gagnant {previous_top_match.computed_name()}", ''] + names = [f"Gagnant {previous_top_match.computed_name()}"] team = self.default_live_team(names) teams.append(team) teams.append(existing_team) elif previous_bottom_match: - names = [f"Gagnant {previous_bottom_match.computed_name()}", ''] + names = [f"Gagnant {previous_bottom_match.computed_name()}"] team = self.default_live_team(names) teams.append(existing_team) teams.append(team) elif (self.round.parent is None and self.round.tournament.round_set.filter(parent__isnull=True, group_stage_loser_bracket=False).count() - 1 == self.round.index): match_index_within_round = self.index - (int(2 ** self.round.index) - 1) - names = ["Qualifié", ''] + names = ["Qualifié"] team = self.default_live_team(names) if match_index_within_round < int(2 ** self.round.index) / 2: teams.append(existing_team) diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 846b593..6fcca5b 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -38,22 +38,29 @@ class TeamRegistration(models.Model): return self.player_names() def player_names_as_list(self): - return [pr.name() for pr in self.playerregistration_set.all()] - + players = list(self.playerregistration_set.all()) + if len(players) == 0: + return [] + elif len(players) == 1: + return [players[0].name()] + else: + return [pr.name() for pr in players] def team_names(self): if self.name: - return [self.name] + return [self.name] #add an empty line if it's a team name else: return self.player_names_as_list() def shortened_team_names(self): if self.name: - return [self.name] + return [self.name] #add an empty line if it's a team name else: players = list(self.playerregistration_set.all()) - if len(players) == 1: - return [players[0].shortened_name(), ''] + if len(players) == 0: + return [] + elif len(players) == 1: + return [players[0].shortened_name()] else: return [pr.shortened_name() for pr in players] diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 1ae1c3f..617926c 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -678,6 +678,12 @@ class Tournament(models.Model): if previous_round: # print('previous_round') matches.extend(previous_round.get_matches_recursive(True)) + + previous_previous_round = self.round_for_index(current_round.index + 2) + if previous_previous_round: + previous_previous_matches = previous_previous_round.get_matches_recursive(True) + previous_previous_matches = [m for m in previous_previous_matches if m.end_date is None] + matches.extend(previous_previous_matches) else: # print('group_stages') group_stages = [gs.live_group_stages() for gs in self.last_group_stage_step()] @@ -722,6 +728,10 @@ class Tournament(models.Model): matches = [m for m in self.all_matches(False) if m.start_date and m.end_date is None] # print(f'first_unfinished_match > match len: {len(matches)}') matches.sort(key=lambda m: m.start_date) + main_bracket_matches = [m for m in matches if m.round and m.round.parent is None] + if main_bracket_matches: + return main_bracket_matches[0] + if matches: return matches[0] else: @@ -782,7 +792,16 @@ class Tournament(models.Model): group_stages.sort(key=lambda gs: (gs.index, gs.start_date is None, gs.start_date)) for group_stage in group_stages: matches.extend(group_stage.match_set.all()) - matches = [m for m in matches if m.should_appear()] + + if len(matches) > 16: + # if more than 16 groupstage matches + now = timezone.now() + future_threshold = now + timedelta(hours=1) + past_threshold = now - timedelta(hours=1) + matches = [m for m in matches if m.should_appear() and + (m.start_date is None or m.start_date <= future_threshold) and # Not starting in more than 1h + (m.end_date is None or m.end_date >= past_threshold)] # Not finished for more than 1h + matches = matches[:16] matches.sort(key=lambda m: (m.start_date is None, m.end_date is not None, m.start_date, m.index)) group_stage_loser_bracket = list(self.round_set.filter(parent=None, group_stage_loser_bracket=True).all()) diff --git a/tournaments/repositories.py b/tournaments/repositories.py index d33690f..cd394bd 100644 --- a/tournaments/repositories.py +++ b/tournaments/repositories.py @@ -23,7 +23,7 @@ class TournamentRegistrationRepository: is_captain = False player_licence_id = player_data['licence_id'] if player_licence_id and stripped_license: - if player_licence_id.startswith(stripped_license): + if stripped_license.lower() in player_licence_id.lower(): is_captain = True sex, rank, computed_rank = TournamentRegistrationRepository._compute_rank_and_sex( @@ -31,6 +31,7 @@ class TournamentRegistrationRepository: player_data ) + print("create_player_registrations", player_data.get('last_name'), sex, rank, computed_rank) data_source = None if player_data.get('found_in_french_federation', False) == True: data_source = PlayerDataSource.FRENCH_FEDERATION @@ -66,14 +67,18 @@ class TournamentRegistrationRepository: @staticmethod def _compute_rank_and_sex(tournament, player_data): is_woman = player_data.get('is_woman', False) - rank = player_data.get('rank', 0) - computed_rank = rank - sex = PlayerSexType.MALE + rank = player_data.get('rank', None) + if rank is None: + computed_rank = 100000 + else: + computed_rank = rank + sex = PlayerSexType.MALE if is_woman: sex = PlayerSexType.FEMALE if tournament.federal_category == FederalCategory.MEN: computed_rank = str(int(computed_rank) + FederalCategory.female_in_male_assimilation_addition(int(rank))) + print("_compute_rank_and_sex", sex, rank, computed_rank) return sex, rank, computed_rank diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index 90492ba..8f033f0 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -31,9 +31,18 @@ class TournamentRegistrationService: if 'add_player' in self.request.POST: self.handle_add_player() + if 'remove_player' in self.request.POST: + self.handle_remove_player() elif 'register_team' in self.request.POST: self.handle_team_registration() + def handle_remove_player(self): + team_registration = self.request.session.get('team_registration', []) + if team_registration: # Check if list is not empty + team_registration.pop() # Remove last element + self.request.session['team_registration'] = team_registration + self.context['current_players'] = team_registration + def handle_add_player(self): if not self.context['add_player_form'].is_valid(): return @@ -106,6 +115,7 @@ class TournamentRegistrationService: self.initialize_session_data() def add_player_to_session(self, player_data): + print("add_player_to_session", player_data) if not self.request.session.get('team_registration'): self.request.session['team_registration'] = [] @@ -216,6 +226,7 @@ class TournamentRegistrationService: return False def _handle_valid_names(self, player_data): + print("_handle_valid_names", player_data) if player_data.get('rank') is None: self._set_default_rank(player_data) @@ -224,18 +235,19 @@ class TournamentRegistrationService: self.context['add_player_form'].first_tournament = False def _handle_invalid_names(self, licence_id, player_data): - if not self.context['add_player_form'].first_tournament: - data, found = get_player_name_from_csv(self.tournament.federal_category, licence_id) - if found and data: - self._update_player_data_from_csv(player_data, data) - player_check = self._player_check(player_data) - if player_check == True: - self.add_player_to_session(player_data) - self.context['add_player_form'] = AddPlayerForm() - else: - return + data, found = get_player_name_from_csv(self.tournament.federal_category, licence_id) + print("_handle_invalid_names get_player_name_from_csv", data, found) + if found and data: + self._update_player_data_from_csv(player_data, data) + player_check = self._player_check(player_data) + if player_check == True: + self.add_player_to_session(player_data) + self.context['add_player_form'] = AddPlayerForm() else: - self._handle_first_tournament_case(data) + return + else: + print("_handle_first_tournament_case") + self._handle_first_tournament_case(data) def _set_default_rank(self, player_data): if self.request.session.get('last_rank') is None: @@ -245,7 +257,7 @@ class TournamentRegistrationService: self.request.session['is_woman'] = data['is_woman'] self.request.session.modified = True - player_data['rank'] = self.request.session.get('last_rank', 0) + player_data['rank'] = self.request.session.get('last_rank', None) player_data['is_woman'] = self.request.session.get('is_woman', False) def _update_user_license(self, licence_id): @@ -261,6 +273,7 @@ class TournamentRegistrationService: self.context['add_player_form'].first_tournament = False def _update_player_data_from_csv(self, player_data, csv_data): + print("_update_player_data_from_csv", player_data, csv_data) player_data.update({ 'first_name': csv_data['first_name'], 'last_name': csv_data['last_name'], @@ -278,6 +291,7 @@ class TournamentRegistrationService: }) def _handle_first_tournament_case(self, data): + print("_handle_first_tournament_case", data) if data: self.request.session['last_rank'] = data['rank'] self.request.session['is_woman'] = data['is_woman'] @@ -307,6 +321,6 @@ class TournamentRegistrationService: def _license_already_registered(self, stripped_license): return PlayerRegistration.objects.filter( team_registration__tournament=self.tournament, - licence_id__startswith=stripped_license, + licence_id__icontains=stripped_license, team_registration__walk_out=False ).exists() diff --git a/tournaments/services/tournament_unregistration.py b/tournaments/services/tournament_unregistration.py index cfad4c4..aa932e0 100644 --- a/tournaments/services/tournament_unregistration.py +++ b/tournaments/services/tournament_unregistration.py @@ -53,7 +53,7 @@ class TournamentUnregistrationService: def _find_player_registration(self): self.player_registration = PlayerRegistration.objects.filter( - licence_id__startswith=self.request.user.licence_id, + licence_id__icontains=self.request.user.licence_id, team_registration__tournament_id=self.tournament.id, ).first() diff --git a/tournaments/static/tournaments/css/broadcast.css b/tournaments/static/tournaments/css/broadcast.css index ffd1f78..5171712 100644 --- a/tournaments/static/tournaments/css/broadcast.css +++ b/tournaments/static/tournaments/css/broadcast.css @@ -25,3 +25,57 @@ body { .bold { font-family: "Montserrat-Bold"; } + +.player { + position: relative; + flex: 1; + display: flex; + flex-direction: column; + min-height: 2.8em; /* This ensures minimum height for 2 lines */ + justify-content: center; + overflow: hidden; +} + +/* Add this if you want empty lines to take up space */ +.player div { + min-height: 1.4em; /* Height for single line */ +} + +/* For single player teams */ +.player.single-player .bold { + max-height: 2.8em; /* Adjust based on your needs */ + line-height: 1.4em; + overflow: hidden; + position: relative; + text-overflow: ellipsis; +} + +/* For two player teams */ +.player.two-players .bold { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.flex { + display: flex; + align-items: center; +} + +.flex-left { + flex: 1; + text-align: left; + justify-content: center; + min-height: 4em; + padding-right: 5px; +} + +.flex-right { + flex: initial; + text-align: right; + justify-content: center; +} + +.center { + align-items: center; +} diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index 035357a..80bcd9c 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -323,9 +323,34 @@ tr { } .player { + position: relative; flex: 1; display: flex; flex-direction: column; + min-height: 2.8em; /* This ensures minimum height for 2 lines */ + justify-content: center; + overflow: hidden; +} + +/* Add this if you want empty lines to take up space */ +.player div { + min-height: 1.4em; /* Height for single line */ +} + +/* For single player teams */ +.player.single-player .semibold { + max-height: 2.8em; /* Adjust based on your needs */ + line-height: 1.4em; + overflow: hidden; + position: relative; + text-overflow: ellipsis; +} + +/* For two player teams */ +.player.two-players .semibold { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .scores { @@ -344,7 +369,6 @@ tr { font-size: 1.3em; vertical-align: middle; text-align: center; - padding: 0px 5px; /* width: 30px; */ } @@ -718,14 +742,15 @@ h-margin { .flex-left { flex: 1; text-align: left; - padding: 5px 0px; + justify-content: center; + min-height: 4em; + padding-right: 5px; } .flex-right { flex: initial; text-align: right; - vertical-align: middle; - padding: 5px 0px; + justify-content: center; } #header { @@ -838,10 +863,6 @@ h-margin { background-color: #90ee90; /* Light green color */ } -.player { - position: relative; /* Ensures the overlay is positioned within this block */ -} - .overlay-text { position: absolute; top: 50%; diff --git a/tournaments/templates/register_tournament.html b/tournaments/templates/register_tournament.html index 5d5a262..0210214 100644 --- a/tournaments/templates/register_tournament.html +++ b/tournaments/templates/register_tournament.html @@ -23,7 +23,7 @@ {% if registration_successful %}

Merci, l'inscription a bien été envoyée au juge-arbitre.

- Un email de confirmation a été envoyé à {{ user.email }} pour confirmer votre inscription. Pensez à vérifier vos spams si vous ne recevez pas l'email. En cas de problème, contactez le juge-arbitre. + Un email de confirmation a été envoyé à l'adresse associée à votre compte Padel Club ({{ user.email }}). Pensez à vérifier vos spams si vous ne recevez pas l'email. En cas de problème, contactez le juge-arbitre.

{% else %}
@@ -48,7 +48,24 @@

{% endif %} @@ -73,6 +90,9 @@ {% endif %} {% if add_player_form.first_tournament or add_player_form.user_without_licence or tournament.license_is_required is False %} {% if not add_player_form.user_without_licence and tournament.license_is_required is True %} +
+ Padel Club n'a pas trouvé votre partenaire, il se peut qu'il s'agisse de son premier tournoi. Contacter le juge-arbitre après l'inscription si ce n'est pas le cas. +
Précisez les informations du joueur :
diff --git a/tournaments/templates/tournaments/broadcast/broadcasted_auto.html b/tournaments/templates/tournaments/broadcast/broadcasted_auto.html index a20e5fb..39545ef 100644 --- a/tournaments/templates/tournaments/broadcast/broadcasted_auto.html +++ b/tournaments/templates/tournaments/broadcast/broadcasted_auto.html @@ -38,6 +38,7 @@ paginatedSummons: null, paginatedRankings: null, active: 1, + hide_weight: {{ tournament.hide_weight|lower }}, prefixTitle: '', retrieveData() { fetch('/tournament/{{ tournament.id }}/broadcast/json/') diff --git a/tournaments/templates/tournaments/broadcast/broadcasted_auto_event.html b/tournaments/templates/tournaments/broadcast/broadcasted_auto_event.html index 2d63558..61153e4 100644 --- a/tournaments/templates/tournaments/broadcast/broadcasted_auto_event.html +++ b/tournaments/templates/tournaments/broadcast/broadcasted_auto_event.html @@ -39,6 +39,7 @@ paginatedSummons: null, paginatedRankings: null, active: 1, + hide_weight: {{ tournament.hide_weight|lower }}, prefixTitle: '', eventTitle: '', title: '', diff --git a/tournaments/templates/tournaments/broadcast/broadcasted_group_stage.html b/tournaments/templates/tournaments/broadcast/broadcasted_group_stage.html index 8c21269..0d35253 100644 --- a/tournaments/templates/tournaments/broadcast/broadcasted_group_stage.html +++ b/tournaments/templates/tournaments/broadcast/broadcasted_group_stage.html @@ -8,7 +8,10 @@
-
+