fix email signal warning

bracket-feature^2
Razmig Sarkissian 8 months ago
parent 1627265a36
commit 8970f49e6a
  1. 24
      tournaments/models/tournament.py
  2. 357
      tournaments/services/email_service.py
  3. 159
      tournaments/signals.py
  4. 4
      tournaments/views.py

@ -328,7 +328,7 @@ class Tournament(models.Model):
"""Returns the total number of spots in all group stages."""
return sum(gs.size for gs in self.groupstage_set.all())
def teams(self, include_waiting_list):
def teams(self, include_waiting_list, un_walk_out_team=None):
"""
Get sorted list of teams for the tournament.
@ -349,7 +349,7 @@ class Tournament(models.Model):
# Process each team registration
for team_reg in self.teamregistration_set.all():
if team_reg.out_of_tournament():
if team_reg.out_of_tournament() and ((un_walk_out_team and team_reg.id is not un_walk_out_team.id) or un_walk_out_team is None):
continue
# Create team item
@ -1227,9 +1227,25 @@ class Tournament(models.Model):
if len(teams)<=self.team_count:
return None
waiting_teams = [team for team in teams if team.stage == "Attente"]
if waiting_teams:
if len(waiting_teams) > 0:
return waiting_teams[0].team_registration
return None
def first_out_of_list(self, un_walk_out_team):
teams = self.teams(True, un_walk_out_team)
waiting_teams = [team for team in teams if team.stage == "Attente"]
retrieved_teams = [team for team in teams if team.team_registration.id == un_walk_out_team.id]
waiting_teams = [team for team in teams if team.stage == "Attente"]
waiting_team = None
is_in = True
if len(retrieved_teams) > 0:
if retrieved_teams[0].stage == "Attente":
is_in = False
if len(waiting_teams) > 0:
waiting_team = waiting_teams[0].team_registration
return is_in, waiting_team
def broadcasted_prog(self):
# Get matches from broadcasted_matches_and_group_stages

@ -1,6 +1,18 @@
from django.core.mail import EmailMessage
from django.utils import timezone
from django.urls import reverse
from enum import Enum
class TeamEmailType(Enum):
UNREGISTERED = "unregistered"
OUT_OF_WAITING_LIST = "out_of_waiting_list"
TOURNAMENT_CANCELED = "tournament_canceled"
IN_TOURNAMENT_STRUCTURE = "in_tournament_structure"
OUT_OF_TOURNAMENT_STRUCTURE = "out_of_tournament_structure"
OUT_OF_WALKOUT_IS_IN = "out_of_walkout_is_in"
OUT_OF_WALKOUT_WAITING_LIST = "out_of_walkout_waiting_list"
WALKOUT = "walkout"
UNEXPECTED_OUT_OF_TOURNAMENT = 'unexpected_out_of_tournament'
class TournamentEmailService:
@staticmethod
@ -36,15 +48,7 @@ class TournamentEmailService:
tournament_details_str,
waiting_list_position
)
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[request.user.email]
)
email.content_subtype = "html"
email.send()
TournamentEmailService._send_email(request.user.email, email_subject, email_body)
@staticmethod
def _build_email_subject(tournament, tournament_details_str, waiting_list_position):
@ -97,68 +101,7 @@ class TournamentEmailService:
other_player
)
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[captain.email]
)
email.content_subtype = "html"
email.send()
if other_player.email is not None:
email_body = TournamentEmailService._build_unregistration_email_body(
tournament,
other_player,
tournament_details_str,
captain
)
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[other_player.email]
)
email.content_subtype = "html"
email.send()
@staticmethod
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._build_out_of_waiting_list_email_body(
tournament,
captain,
tournament_details_str,
other_player
)
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[captain.email]
)
email.content_subtype = "html"
email.send()
if other_player.email is not None:
email_body = TournamentEmailService._build_out_of_waiting_list_email_body(
tournament,
other_player,
tournament_details_str,
captain
)
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[other_player.email]
)
email.content_subtype = "html"
email.send()
TournamentEmailService._send_email(captain.email, email_subject, email_body)
@staticmethod
def _build_unregistration_email_body(tournament, captain, tournament_details_str, other_player):
@ -219,41 +162,277 @@ class TournamentEmailService:
return "".join(body_parts)
@staticmethod
def send_tournament_cancellation_notification(player, tournament, other_player):
tournament_details_str = tournament.build_tournament_details_str()
email_subject = TournamentEmailService.email_subject(tournament, "Annulation du tournoi")
email_body = TournamentEmailService._build_tournament_cancellation_email_body(
tournament,
player,
tournament_details_str,
other_player
)
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.formatted_start_date()} au club {tournament.event.club.name} a été annulé par le juge-arbitre."
]
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[player.email]
if other_player is not None:
body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre:",
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."
])
return "".join(body_parts)
@staticmethod
def _build_in_tournament_email_body(tournament, captain, tournament_details_str, other_player):
body_parts = [
"Bonjour,\n\n",
f"Suite à une modification de la taille du tournoi, vous pouvez participer 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"
link_text = "accéder au tournoi"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
if other_player is not None:
body_parts.append(
f"\nVoici le partenaire indiqué dans l'inscription : {other_player.name()}, n'oubliez pas de le prévenir."
)
body_parts.append(
"\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."
)
email.content_subtype = "html"
email.send()
body_parts.extend([
f"\n\n{tournament.event.creator.full_name()}\n{tournament.event.creator.email}",
"\n\nCeci est un e-mail automatique, veuillez ne pas y répondre."
])
return "".join(body_parts)
@staticmethod
def _build_tournament_cancellation_email_body(tournament, player, tournament_details_str, other_player):
def _build_out_of_tournament_email_body(tournament, captain, tournament_details_str, other_player):
body_parts = [
"Bonjour,\n\n",
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."
f"Suite à une modification de la taille du tournoi, vous ne faites plus partie des équipes participant 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"
link_text = "informations sur le tournoi"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}")
if other_player is not None:
body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre:",
"\n\nPour toute question, veuillez contacter votre juge-arbitre : ",
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."
])
return "".join(body_parts)
@staticmethod
def _build_walk_out_email_body(tournament, captain, tournament_details_str, other_player):
body_parts = [
"Bonjour,\n\n",
f"Le Juge-Arbitre a annulé votre participation 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"
link_text = "accéder au tournoi"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}")
if other_player is not None:
body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre : ",
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."
])
return "".join(body_parts)
@staticmethod
def _build_out_of_walkout_is_in_email_body(tournament, captain, tournament_details_str, other_player):
body_parts = [
"Bonjour,\n\n",
f"Le juge-arbitre vous a ré-intégré 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"
link_text = "informations sur le tournoi"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}")
if other_player is not None:
body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre : ",
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."
])
return "".join(body_parts)
@staticmethod
def _build_unexpected_out_of_tournament_email_body(tournament, captain, tournament_details_str, other_player):
body_parts = [
"Bonjour,\n\n",
f"En raison d'une décision du juge-arbitre, vous ne faites plus partie des équipes participant 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"
link_text = "informations sur le tournoi"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}")
if other_player is not None:
body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre : ",
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."
])
return "".join(body_parts)
@staticmethod
def _build_out_of_walkout_waiting_list_email_body(tournament, captain, tournament_details_str, other_player):
body_parts = [
"Bonjour,\n\n",
f"Le juge-arbitre vous a ré-intégré au tournoi en liste d'attente {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"
link_text = "informations sur le tournoi"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}")
if other_player is not None:
body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre : ",
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."
])
return "".join(body_parts)
@staticmethod
def notify(captain, other_player, tournament, message_type: TeamEmailType):
if tournament.should_be_over():
return
if tournament.supposedly_in_progress():
return
if not captain or not captain.registered_online or not captain.email:
return
tournament_details_str = tournament.build_tournament_details_str()
email_body = TournamentEmailService._build_email_content(
message_type, captain, tournament, tournament_details_str, other_player
)
if email_body is None:
return
email_subject = TournamentEmailService.email_subject(tournament, "Participation au tournoi")
TournamentEmailService._send_email(captain.email, email_subject, email_body)
@staticmethod
def _build_email_content(message_type, recipient, tournament, tournament_details_str, other_player):
if message_type == TeamEmailType.OUT_OF_WAITING_LIST:
body = TournamentEmailService._build_out_of_waiting_list_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.TOURNAMENT_CANCELED:
body = TournamentEmailService._build_tournament_cancellation_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.WALKOUT:
body = TournamentEmailService._build_walk_out_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.OUT_OF_WALKOUT_IS_IN:
body = TournamentEmailService._build_out_of_walkout_is_in_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.OUT_OF_WALKOUT_WAITING_LIST:
body = TournamentEmailService._build_out_of_walkout_waiting_list_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.UNEXPECTED_OUT_OF_TOURNAMENT:
body = TournamentEmailService._build_unexpected_out_of_tournament_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.OUT_OF_TOURNAMENT_STRUCTURE:
body = TournamentEmailService._build_tournament_cancellation_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.IN_TOURNAMENT_STRUCTURE:
body = TournamentEmailService._build_out_of_tournament_email_body(
tournament, recipient, tournament_details_str, other_player
)
else:
return None
return body
@staticmethod
def _send_email(to, subject, body):
email = EmailMessage(
subject=subject,
body=TournamentEmailService._convert_newlines_to_html(body),
to=[to]
)
email.content_subtype = "html"
email.send()
@staticmethod
def notify_team(team, tournament, message_type: TeamEmailType):
captain = None
other_player = None
for player in team.playerregistration_set.all():
if player.captain:
captain = player
else:
other_player = player
if captain:
TournamentEmailService.notify(captain, other_player, tournament, message_type)
else:
# Notify both players separately if there is no captain or the captain is unavailable
players = list(team.playerregistration_set.all())
if len(players) == 2:
first_player, second_player = players
TournamentEmailService.notify(first_player, second_player, tournament, message_type)
TournamentEmailService.notify(second_player, first_player, tournament, message_type)
elif len(players) == 1:
# If there's only one player, just send them the notification
TournamentEmailService.notify(players[0], None, tournament, message_type)

@ -1,18 +1,19 @@
import random
import string
from django.db.models.signals import post_save, pre_delete, post_delete
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver
from django.conf import settings
from tournaments.models.tournament import Tournament
from tournaments.models.unregistered_player import UnregisteredPlayer
from django.utils import timezone
from .models import Club, FailedApiCall, CustomUser, Log, TeamRegistration, PlayerRegistration, UnregisteredTeam, UnregisteredPlayer
from .models import Club, FailedApiCall, CustomUser, Log, TeamRegistration, PlayerRegistration, UnregisteredTeam, UnregisteredPlayer, TeamSortingType
import requests
from tournaments.services.email_service import TournamentEmailService
from tournaments.services.email_service import TournamentEmailService, TeamEmailType
from tournaments.models import PlayerDataSource
from shared.discord import send_discord_log_message, send_discord_failed_calls_message
from datetime import datetime
def generate_unique_code():
characters = string.ascii_lowercase + string.digits
@ -48,95 +49,91 @@ def notify_object_creation_on_discord(created, instance):
else:
send_discord_log_message(message)
# def send_discord_message(webhook_url, content):
# data = {
# "content": content
# }
# requests.post(webhook_url, json=data)
# # if response.status_code != 204:
# # raise ValueError(
# # f'Error sending message to Discord webhook: {response.status_code}, {response.text}'
# # )
def notify_team(team, tournament, message_type):
if tournament.online_registration is False:
return
TournamentEmailService.notify_team(team, tournament, message_type)
@receiver(pre_delete, sender=TeamRegistration)
def unregister_team(sender, instance, **kwargs):
team_registration = instance
tournament = instance.tournament
if tournament.is_deleted is True:
if instance.tournament.is_deleted:
return
if instance.online_registration is False:
return
# Create unregistered player records and track captain/other player
captain = None
other_player = None
for player in team_registration.playerregistration_set.all():
if player.captain is True:
captain = player
else:
other_player = player
# Send unregistration confirmation
if captain and captain.registered_online and captain.email:
TournamentEmailService.send_unregistration_confirmation(
captain,
tournament,
other_player
)
first_waiting_list_team = tournament.first_waiting_list_team()
print("first_waiting_list_team", first_waiting_list_team)
# Handle waiting list notifications
notify_team(instance, instance.tournament, TeamEmailType.UNREGISTERED)
first_waiting_list_team = instance.tournament.first_waiting_list_team()
if first_waiting_list_team:
waiting_captain = None
waiting_other_player = None
for player in first_waiting_list_team.playerregistration_set.all():
if player.captain is True:
waiting_captain = player
else:
waiting_other_player = player
if waiting_captain and waiting_captain.registered_online and waiting_captain.email:
TournamentEmailService.send_out_of_waiting_list_confirmation(
waiting_captain,
tournament,
waiting_other_player
)
notify_team(first_waiting_list_team, instance.tournament, TeamEmailType.OUT_OF_WAITING_LIST)
@receiver(post_save, sender=Tournament)
def notify_players_of_tournament_cancellation(sender, instance, **kwargs):
tournament = instance
if not instance.is_deleted:
return
if tournament.is_deleted is False:
if instance.online_registration is False:
return
# Get all team registrations
team_registrations = tournament.teamregistration_set.all()
for team_registration in instance.teamregistration_set.all():
notify_team(team_registration, instance, TeamEmailType.TOURNAMENT_CANCELED)
for team_registration in team_registrations:
captain = None
other_player = None
@receiver(pre_save, sender=Tournament)
def check_waiting_list(sender, instance, **kwargs):
if instance.id is None:
return
# Get players who registered online and have email
for player in team_registration.playerregistration_set.all():
print(player, player.registered_online)
if player.captain:
captain = player
else:
other_player = player
# Send email to captain
if captain and captain.registered_online and captain.email:
TournamentEmailService.send_tournament_cancellation_notification(
captain,
tournament,
other_player
)
# Send email to other player if they exist and registered online
if other_player and other_player.registered_online and other_player.email:
TournamentEmailService.send_tournament_cancellation_notification(
other_player,
tournament,
captain
)
if instance.online_registration is False:
return
previous_state = Tournament.objects.get(id=instance.id)
if previous_state is None:
return
teams_out_to_warn = []
teams_in_to_warn = []
if previous_state.team_count > instance.team_count:
teams_to_remove_count = previous_state.team_count - instance.team_count
sorted_teams = sorted(
[team for team in previous_state.teams(True) if team.stage != "Attente" and not (team.wildcard_bracket or team.wildcard_groupstage)],
key=lambda t: (
t.registration_date is None, t.registration_date or datetime.min, t.initial_weight, t.team_registration.id
) if previous_state.team_sorting == TeamSortingType.INSCRIPTION_DATE else
(t.initial_weight, t.team_registration.id)
)
teams_out_to_warn = sorted_teams[-teams_to_remove_count:]
elif previous_state.team_count < instance.team_count:
teams_in_to_warn = previous_state.teams(True)[:(instance.team_count - previous_state.team_count)]
for team in teams_in_to_warn:
notify_team(team, instance, TeamEmailType.IN_TOURNAMENT_STRUCTURE)
for team in teams_out_to_warn:
notify_team(team, instance, TeamEmailType.OUT_OF_TOURNAMENT_STRUCTURE)
@receiver(pre_save, sender=TeamRegistration)
def warn_team_walkout_status_change(sender, instance, **kwargs):
if instance.id is None:
return
if instance.tournament.online_registration is False:
return
previous_instance = TeamRegistration.objects.get(id=instance.id)
if previous_instance is None:
return
if previous_instance.out_of_tournament() and not instance.out_of_tournament():
is_in, first_out_of_list = instance.tournament.first_out_of_list(instance)
if is_in:
notify_team(instance, instance.tournament, TeamEmailType.OUT_OF_WALKOUT_IS_IN)
else:
notify_team(instance, instance.tournament, TeamEmailType.OUT_OF_WALKOUT_WAITING_LIST)
if first_out_of_list:
notify_team(first_out_of_list, instance.tournament, TeamEmailType.UNEXPECTED_OUT_OF_TOURNAMENT)
elif not previous_instance.out_of_tournament() and instance.out_of_tournament():
notify_team(instance, instance.tournament, TeamEmailType.WALKOUT)
first_waiting_list_team = instance.tournament.first_waiting_list_team()
if first_waiting_list_team:
notify_team(first_waiting_list_team, instance.tournament, TeamEmailType.OUT_OF_WAITING_LIST)

@ -271,8 +271,8 @@ def tournament_teams(request, tournament_id):
tournament = get_object_or_404(Tournament, pk=tournament_id)
teams = tournament.teams(True)
selected_teams = [team for team in teams if team.stage != 'Attente']
waiting_teams = [team for team in teams if team.stage == 'Attente']
selected_teams = [team for team in teams if team.stage != "Attente"]
waiting_teams = [team for team in teams if team.stage == "Attente"]
return render(request, 'tournaments/teams.html', {
'tournament': tournament,

Loading…
Cancel
Save