From afbb3a2272c7d322887fd6d806b926d47592d62c Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 28 Jan 2025 18:04:29 +0100 Subject: [PATCH 1/5] fix event deletion signal --- tournaments/models/tournament.py | 7 ------- tournaments/signals.py | 22 ++++++++++++++++------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index d8ac5a2..d9dfcdf 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -75,7 +75,6 @@ class Tournament(models.Model): minimum_player_per_team = models.IntegerField(default=2) maximum_player_per_team = models.IntegerField(default=2) information = models.CharField(max_length=4000, null=True, blank=True) - _being_deleted = False # Class attribute def __str__(self): if self.name: @@ -1186,12 +1185,6 @@ class Tournament(models.Model): return waiting_teams[0].team_registration return None - - def delete(self, *args, **kwargs): - # Mark this tournament as being deleted - self._being_deleted = True - super().delete(*args, **kwargs) - def broadcasted_prog(self): # Get matches from broadcasted_matches_and_group_stages matches, _ = self.broadcasted_matches_and_group_stages() diff --git a/tournaments/signals.py b/tournaments/signals.py index 1de9963..ac9e8e2 100644 --- a/tournaments/signals.py +++ b/tournaments/signals.py @@ -1,6 +1,6 @@ import random import string -from django.db.models.signals import post_save, pre_delete +from django.db.models.signals import post_save, pre_delete, post_delete from django.dispatch import receiver from django.conf import settings from tournaments.models.tournament import Tournament @@ -12,6 +12,8 @@ import requests from tournaments.services.email_service import TournamentEmailService from tournaments.models import PlayerDataSource +tournament_deletion_in_progress = set() + def generate_unique_code(): characters = string.ascii_lowercase + string.digits while True: @@ -59,13 +61,13 @@ def send_discord_message(webhook_url, content): f'Error sending message to Discord webhook: {response.status_code}, {response.text}' ) -@receiver(pre_delete, sender=TeamRegistration) +@receiver(post_delete, sender=TeamRegistration) def unregister_team(sender, instance, **kwargs): team_registration = instance tournament = instance.tournament - - # Skip creating unregistration records if tournament is being deleted - if not tournament or tournament._being_deleted == True: + global tournament_deletion_in_progress + print('pre_delete TeamRegistration', tournament_deletion_in_progress) + if tournament.id in tournament_deletion_in_progress: return first_waiting_list_team = tournament.first_waiting_list_team() @@ -117,10 +119,18 @@ def unregister_team(sender, instance, **kwargs): other_player ) +@receiver(post_delete, sender=Tournament) +def tournament_deletion(sender, instance, **kwargs): + global tournament_deletion_in_progress + tournament_deletion_in_progress.discard(instance.id) + print('post tournament_deletion_in_progress', tournament_deletion_in_progress) + @receiver(pre_delete, sender=Tournament) def notify_players_of_tournament_cancellation(sender, instance, **kwargs): tournament = instance - + global tournament_deletion_in_progress + tournament_deletion_in_progress.add(instance.id) + print('pre tournament_deletion_in_progress', tournament_deletion_in_progress) # Get all team registrations team_registrations = tournament.teamregistration_set.all() From 7515f15b7f4f222bd93f4276026530f60a4ec32e Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 29 Jan 2025 15:18:12 +0100 Subject: [PATCH 2/5] fix issue with registration email service --- tournaments/models/tournament.py | 18 +++++-- tournaments/services/email_service.py | 33 ++++++------ .../services/tournament_registration.py | 23 +++++---- .../services/tournament_unregistration.py | 18 +++++++ tournaments/signals.py | 50 ++++++++----------- 5 files changed, 82 insertions(+), 60 deletions(-) diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index d9dfcdf..56fde14 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -1090,17 +1090,25 @@ class Tournament(models.Model): # In waiting list with no limit return current_team_count - self.team_count - def build_tournament_details_str(self): + def build_tournament_type_array(self): tournament_details = [] - - name_str = self.build_name_details_str() - if self.federal_level_category > 0: tournament_details.append(self.level()) if self.category(): tournament_details.append(self.category()) - if self.age(): + if self.age() and self.federal_age_category != FederalAgeCategory.SENIOR: tournament_details.append(self.age()) + return tournament_details + + def build_tournament_type_str(self): + tournament_details = self.build_tournament_type_array() + return " ".join(filter(None, tournament_details)) + + def build_tournament_details_str(self): + tournament_details = self.build_tournament_type_array() + + name_str = self.build_name_details_str() + if len(name_str) > 0: tournament_details.append(name_str) diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index d119b40..f019848 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -14,11 +14,17 @@ class TournamentEmailService: """ + @staticmethod + def email_subject(tournament, topic): + base_subject = f"[{tournament.build_tournament_type_str()}] [{tournament.formatted_start_date()}] " + topic + return base_subject + @staticmethod def send_registration_confirmation(request, tournament, team_registration, waiting_list_position): tournament_details_str = tournament.build_tournament_details_str() email_subject = TournamentEmailService._build_email_subject( + tournament, tournament_details_str, waiting_list_position ) @@ -41,11 +47,12 @@ class TournamentEmailService: email.send() @staticmethod - def _build_email_subject(tournament_details_str, waiting_list_position): - base_subject = f"Confirmation d'inscription au tournoi {tournament_details_str}" + def _build_email_subject(tournament, tournament_details_str, waiting_list_position): if waiting_list_position >= 0: - base_subject = f"En liste d'attente du tournoi {tournament_details_str}" - return base_subject + base_subject = "En liste d'attente du tournoi" + else: + base_subject = "Confirmation d'inscription au tournoi" + return TournamentEmailService.email_subject(tournament, base_subject) @staticmethod def _build_email_body(request, tournament, team_registration, tournament_details_str, waiting_list_position): @@ -68,7 +75,7 @@ class TournamentEmailService: body_parts.extend([ f"\nDate d'inscription: {inscription_date}", f"\nÉquipe inscrite: {team_members_str}", - f"\nLe tournoi commencera le {tournament.start_date.strftime('%d/%m/%Y')} au club {tournament.event.club.name}", + f"\nLe tournoi commencera le {tournament.formatted_start_date()} au club {tournament.event.club.name}", f"\nVoir les {absolute_url}", "\nPour toute question, veuillez contacter votre juge-arbitre. Si vous n'êtes pas à l'origine de cette inscription, merci de le contacter rapidement.", f"\n{tournament.event.creator.full_name()}\n{tournament.event.creator.email}", @@ -82,7 +89,7 @@ class TournamentEmailService: def send_unregistration_confirmation(captain, tournament, other_player): tournament_details_str = tournament.build_tournament_details_str() - email_subject = f"Désistement du tournoi {tournament_details_str}" + email_subject = TournamentEmailService.email_subject(tournament, "Désistement du tournoi") email_body = TournamentEmailService._build_unregistration_email_body( tournament, captain, @@ -119,8 +126,7 @@ class TournamentEmailService: @staticmethod def send_out_of_waiting_list_confirmation(captain, tournament, other_player): tournament_details_str = tournament.build_tournament_details_str() - - email_subject = f"Participation au tournoi {tournament_details_str}" + email_subject = TournamentEmailService.email_subject(tournament, "Participation au tournoi") email_body = TournamentEmailService._buil_out_of_waiting_list_email_body( tournament, captain, @@ -158,7 +164,7 @@ class TournamentEmailService: def _build_unregistration_email_body(tournament, captain, tournament_details_str, other_player): body_parts = [ "Bonjour,\n\n", - f"Votre inscription au tournoi {tournament_details_str}, prévu le {tournament.start_date.strftime('%d/%m/%Y')} au club {tournament.event.club.name} a été annulée" + f"Votre inscription au tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été annulée" ] if other_player is not None: @@ -168,7 +174,7 @@ class TournamentEmailService: body_parts.extend([ "\n\nPour toute question, veuillez contacter votre juge-arbitre. " - "Si vous n'êtes pas à l'origine de cette inscription, merci de le contacter rapidement.", + "Si vous n'êtes pas à l'origine de cette désinscription, merci de le contacter rapidement.", f"\n{tournament.event.creator.full_name()}\n{tournament.event.creator.email}", "\n\nCeci est un e-mail automatique, veuillez ne pas y répondre." ]) @@ -179,7 +185,7 @@ class TournamentEmailService: def _buil_out_of_waiting_list_email_body(tournament, captain, tournament_details_str, other_player): body_parts = [ "Bonjour,\n\n", - f"Suite au désistement d'une paire, vous êtes maintenant inscrit au tournoi {tournament_details_str}, prévu le {tournament.start_date.strftime('%d/%m/%Y')} au club {tournament.event.club.name}" + f"Suite au désistement d'une paire, vous êtes maintenant inscrit au tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name}" ] absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" @@ -206,8 +212,7 @@ class TournamentEmailService: @staticmethod def send_tournament_cancellation_notification(player, tournament, other_player): tournament_details_str = tournament.build_tournament_details_str() - - email_subject = f"Annulation du tournoi {tournament_details_str}" + email_subject = TournamentEmailService.email_subject(tournament, "Annulation du tournoi") email_body = TournamentEmailService._build_tournament_cancellation_email_body( tournament, player, @@ -228,7 +233,7 @@ class TournamentEmailService: def _build_tournament_cancellation_email_body(tournament, player, tournament_details_str, other_player): body_parts = [ "Bonjour,\n\n", - f"Le tournoi {tournament_details_str}, prévu le {tournament.start_date.strftime('%d/%m/%Y')} au club {tournament.event.club.name} a été annulé par le juge-arbitre." + f"Le tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été annulé par le juge-arbitre." ] if other_player is not None: diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index 5294434..f7fbb3a 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -22,7 +22,6 @@ class TournamentRegistrationService: 'team_form': None, 'add_player_form': None, 'current_players': self.request.session.get('team_registration', []), - 'user_without_licence': self.request.session.get('user_without_licence', False) } return self.context @@ -54,11 +53,18 @@ class TournamentRegistrationService: if self._is_already_registered(licence_id): return - # Handle player data - if self.context['add_player_form'].names_is_valid(): - self._handle_valid_names(player_data) - else: + if self.request.user.licence_id is None and len(self.context['current_players']) == 0: + # if no licence id for authentificated user and trying to add him as first player of the team, we check his federal data self._handle_invalid_names(licence_id, player_data) + else: + # Handle player data + if self.context['add_player_form'].names_is_valid(): + self._handle_valid_names(player_data) + else: + self._handle_invalid_names(licence_id, player_data) + + if self.request.user.is_authenticated and self.request.user.licence_id is None: + self._update_user_license(player_data.get('licence_id')) def handle_team_registration(self): if not self.context['team_form'].is_valid(): @@ -214,12 +220,6 @@ class TournamentRegistrationService: self._set_default_rank(player_data) self.add_player_to_session(player_data) - - if self.request.user.is_authenticated and self.request.user.licence_id is None: - self._update_user_license(player_data.get('licence_id')) - self.context['add_player_form'].user_without_licence = False - self.request.session.modified = True - self.context['add_player_form'] = AddPlayerForm() self.context['add_player_form'].first_tournament = False @@ -255,6 +255,7 @@ class TournamentRegistrationService: self.request.user.licence_id = validator.computed_licence_id self.request.user.save() self.request.user.refresh_from_db() + self.request.session.modified = True # Reset the form state self.context['add_player_form'] = AddPlayerForm() self.context['add_player_form'].first_tournament = False diff --git a/tournaments/services/tournament_unregistration.py b/tournaments/services/tournament_unregistration.py index 3013186..cfad4c4 100644 --- a/tournaments/services/tournament_unregistration.py +++ b/tournaments/services/tournament_unregistration.py @@ -29,10 +29,28 @@ class TournamentUnregistrationService: "La désincription a échouée. Veuillez contacter le juge-arbitre.") return False + self._unregister_team() self._delete_registered_team() self._cleanup_session() return True + def _unregister_team(self): + # Create unregistered team record + team_registration = self.player_registration.team_registration + + unregistered_team = UnregisteredTeam.objects.create( + tournament=team_registration.tournament, + unregistration_date=timezone.now(), + ) + + for player in team_registration.playerregistration_set.all(): + UnregisteredPlayer.objects.create( + unregistered_team=unregistered_team, + first_name=player.first_name, + last_name=player.last_name, + licence_id=player.licence_id, + ) + def _find_player_registration(self): self.player_registration = PlayerRegistration.objects.filter( licence_id__startswith=self.request.user.licence_id, diff --git a/tournaments/signals.py b/tournaments/signals.py index ac9e8e2..c39a9b7 100644 --- a/tournaments/signals.py +++ b/tournaments/signals.py @@ -61,22 +61,10 @@ def send_discord_message(webhook_url, content): f'Error sending message to Discord webhook: {response.status_code}, {response.text}' ) -@receiver(post_delete, sender=TeamRegistration) +@receiver(pre_delete, sender=TeamRegistration) def unregister_team(sender, instance, **kwargs): team_registration = instance tournament = instance.tournament - global tournament_deletion_in_progress - print('pre_delete TeamRegistration', tournament_deletion_in_progress) - if tournament.id in tournament_deletion_in_progress: - return - - first_waiting_list_team = tournament.first_waiting_list_team() - - # Create unregistered team record - unregistered_team = UnregisteredTeam.objects.create( - tournament=tournament, - unregistration_date=timezone.now(), - ) # Create unregistered player records and track captain/other player captain = None @@ -87,13 +75,25 @@ def unregister_team(sender, instance, **kwargs): else: other_player = player - UnregisteredPlayer.objects.create( - unregistered_team=unregistered_team, - first_name=player.first_name, - last_name=player.last_name, - licence_id=player.licence_id, + # Send unregistration confirmation + if captain and captain.registered_online and captain.email: + TournamentEmailService.send_unregistration_confirmation( + captain, + tournament, + other_player ) +@receiver(post_delete, sender=TeamRegistration) +def handle_waiting_list(sender, instance, **kwargs): + team_registration = instance + tournament = team_registration.tournament + global tournament_deletion_in_progress + print('post_delete TeamRegistration', tournament_deletion_in_progress) + if tournament.id in tournament_deletion_in_progress: + return + + first_waiting_list_team = tournament.first_waiting_list_team() + # Handle waiting list notifications if first_waiting_list_team: waiting_captain = None @@ -111,14 +111,6 @@ def unregister_team(sender, instance, **kwargs): waiting_other_player ) - # Send unregistration confirmation - if captain and captain.registered_online and captain.email: - TournamentEmailService.send_unregistration_confirmation( - captain, - tournament, - other_player - ) - @receiver(post_delete, sender=Tournament) def tournament_deletion(sender, instance, **kwargs): global tournament_deletion_in_progress @@ -140,16 +132,14 @@ def notify_players_of_tournament_cancellation(sender, instance, **kwargs): # Get players who registered online and have email for player in team_registration.playerregistration_set.all(): - if not player.registered_online: - continue - + print(player, player.registered_online) if player.captain: captain = player else: other_player = player # Send email to captain - if captain: + if captain and captain.registered_online and captain.email: TournamentEmailService.send_tournament_cancellation_notification( captain, tournament, From 580c24e91695bece244a36c1f3ef162386ae01f2 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 29 Jan 2025 15:23:42 +0100 Subject: [PATCH 3/5] fix issue with registration email service --- tournaments/services/email_service.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index f019848..e2b0c7c 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -127,7 +127,7 @@ class TournamentEmailService: def send_out_of_waiting_list_confirmation(captain, tournament, other_player): tournament_details_str = tournament.build_tournament_details_str() email_subject = TournamentEmailService.email_subject(tournament, "Participation au tournoi") - email_body = TournamentEmailService._buil_out_of_waiting_list_email_body( + email_body = TournamentEmailService._build_out_of_waiting_list_email_body( tournament, captain, tournament_details_str, @@ -144,7 +144,7 @@ class TournamentEmailService: email.send() if other_player.email is not None: - email_body = TournamentEmailService._buil_out_of_waiting_list_email_body( + email_body = TournamentEmailService._build_out_of_waiting_list_email_body( tournament, other_player, tournament_details_str, @@ -172,6 +172,14 @@ class TournamentEmailService: f"\n\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire." ) + absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" + link_text = "informations sur le tournoi" + absolute_url = f'{link_text}' + + body_parts.append( + f"\n\nVoir les {absolute_url}", + ) + body_parts.extend([ "\n\nPour toute question, veuillez contacter votre juge-arbitre. " "Si vous n'êtes pas à l'origine de cette désinscription, merci de le contacter rapidement.", @@ -182,7 +190,7 @@ class TournamentEmailService: return "".join(body_parts) @staticmethod - def _buil_out_of_waiting_list_email_body(tournament, captain, tournament_details_str, other_player): + def _build_out_of_waiting_list_email_body(tournament, captain, tournament_details_str, other_player): body_parts = [ "Bonjour,\n\n", f"Suite au désistement d'une paire, vous êtes maintenant inscrit au tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name}" From e1e62ea9631ee721274e69e108dbd6b98f7281fd Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 29 Jan 2025 16:10:59 +0100 Subject: [PATCH 4/5] fix issue with registration email service --- tournaments/services/email_service.py | 5 +++-- tournaments/signals.py | 29 ++++++++------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index e2b0c7c..6a3e001 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -197,7 +197,7 @@ class TournamentEmailService: ] absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" - link_text = "informations sur le tournoi" + link_text = "accéder au tournoi" absolute_url = f'{link_text}' if other_player is not None: @@ -206,8 +206,9 @@ class TournamentEmailService: ) body_parts.append( - "\nSi vous n'êtes plus disponible pour participer à ce tournoi, cliquez sur ce lien ou contactez rapidement le juge-arbitre." + "\n\nSi vous n'êtes plus disponible pour participer à ce tournoi, cliquez sur ce lien ou contactez rapidement le juge-arbitre." f"\n{absolute_url}" + "\nPour vous désinscrire en ligne vous devez avoir un compte Padel Club." ) body_parts.extend([ diff --git a/tournaments/signals.py b/tournaments/signals.py index c39a9b7..321fde0 100644 --- a/tournaments/signals.py +++ b/tournaments/signals.py @@ -12,8 +12,6 @@ import requests from tournaments.services.email_service import TournamentEmailService from tournaments.models import PlayerDataSource -tournament_deletion_in_progress = set() - def generate_unique_code(): characters = string.ascii_lowercase + string.digits while True: @@ -65,6 +63,8 @@ def send_discord_message(webhook_url, content): def unregister_team(sender, instance, **kwargs): team_registration = instance tournament = instance.tournament + if tournament.is_deleted is True: + return # Create unregistered player records and track captain/other player captain = None @@ -83,16 +83,8 @@ def unregister_team(sender, instance, **kwargs): other_player ) -@receiver(post_delete, sender=TeamRegistration) -def handle_waiting_list(sender, instance, **kwargs): - team_registration = instance - tournament = team_registration.tournament - global tournament_deletion_in_progress - print('post_delete TeamRegistration', tournament_deletion_in_progress) - if tournament.id in tournament_deletion_in_progress: - return - first_waiting_list_team = tournament.first_waiting_list_team() + print("first_waiting_list_team", first_waiting_list_team) # Handle waiting list notifications if first_waiting_list_team: @@ -111,18 +103,13 @@ def handle_waiting_list(sender, instance, **kwargs): waiting_other_player ) -@receiver(post_delete, sender=Tournament) -def tournament_deletion(sender, instance, **kwargs): - global tournament_deletion_in_progress - tournament_deletion_in_progress.discard(instance.id) - print('post tournament_deletion_in_progress', tournament_deletion_in_progress) - -@receiver(pre_delete, sender=Tournament) +@receiver(post_save, sender=Tournament) def notify_players_of_tournament_cancellation(sender, instance, **kwargs): tournament = instance - global tournament_deletion_in_progress - tournament_deletion_in_progress.add(instance.id) - print('pre tournament_deletion_in_progress', tournament_deletion_in_progress) + + if tournament.is_deleted is False: + return + # Get all team registrations team_registrations = tournament.teamregistration_set.all() From 384060addd21a6ecd27f3cd0895c54ded6727375 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 30 Jan 2025 08:56:36 +0100 Subject: [PATCH 5/5] fix validation error --- tournaments/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tournaments/views.py b/tournaments/views.py index ad08a2c..e1333a5 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -63,6 +63,7 @@ from django.core.mail import EmailMessage from django.views.decorators.csrf import csrf_protect from .services.tournament_registration import TournamentRegistrationService from .services.tournament_unregistration import TournamentUnregistrationService +from django.core.exceptions import ValidationError # Local application imports