from django.core.mail import EmailMessage
from enum import Enum
from ..models.player_registration import RegistrationStatus
from ..models.tournament import TeamSortingType
class TeamEmailType(Enum):
REGISTERED = "registered"
WAITING_LIST = "waiting_list"
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'
def email_subject(self) -> str:
subjects = {
self.REGISTERED: "Participation confirmée",
self.WAITING_LIST: "Liste d'attente",
self.UNREGISTERED: "Désistement",
self.OUT_OF_WAITING_LIST: "Participation confirmée",
self.TOURNAMENT_CANCELED: "Tournoi annulé",
self.IN_TOURNAMENT_STRUCTURE: "Participation confirmée",
self.OUT_OF_TOURNAMENT_STRUCTURE: "Participation annulée",
self.OUT_OF_WALKOUT_IS_IN: "Participation confirmée",
self.OUT_OF_WALKOUT_WAITING_LIST: "Liste d'attente",
self.WALKOUT: "Participation annulée",
self.UNEXPECTED_OUT_OF_TOURNAMENT: "Participation annulée",
}
return subjects.get(self, "Tournament Notification")
class TournamentEmailService:
@staticmethod
def _convert_newlines_to_html(text):
html_content = text.replace('\n', '
')
return f"""
{html_content}
"""
@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):
if waiting_list_position >= 0:
TournamentEmailService.notify_team(team_registration, tournament, TeamEmailType.WAITING_LIST)
else:
TournamentEmailService.notify_team(team_registration, tournament, TeamEmailType.REGISTERED)
@staticmethod
def _build_registration_confirmation_email_body(tournament, captain, tournament_details_str, other_player):
return TournamentEmailService._build_registration_email_body(tournament, captain, tournament_details_str, other_player, False)
@staticmethod
def _build_waiting_list_confirmation_email_body(tournament, captain, tournament_details_str, other_player):
return TournamentEmailService._build_registration_email_body(tournament, captain, tournament_details_str, other_player, True)
@staticmethod
def _build_registration_email_body(tournament, captain, tournament_details_str, other_player, waiting_list):
inscription_date = captain.team_registration.local_registration_date().strftime("%d/%m/%Y à %H:%M")
body_parts = []
body_parts.append("Bonjour,\n")
if waiting_list:
body_parts.append(f"Votre inscription en liste d'attente du tournoi {tournament_details_str} est confirmée.")
else:
body_parts.append(f"Votre inscription au tournoi {tournament_details_str} est confirmée.")
if tournament.team_sorting == TeamSortingType.RANK:
cloture_date = tournament.local_registration_federal_limit().strftime("%d/%m/%Y à %H:%M")
loc = ""
if cloture_date is not None:
loc = f", prévu le {cloture_date}"
body_parts.append(f"Attention, la sélection définitive se fera par poids d'équipe à la clôture des inscriptions{loc}.")
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi"
absolute_url = f'{link_text}'
body_parts.extend([
f"\nDate d'inscription: {inscription_date}",
f"\nÉquipe inscrite: {captain.name()} et {other_player.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{TournamentEmailService._format_umpire_contact(tournament)}",
"\nCeci est un e-mail automatique, veuillez ne pas y répondre.",
"\nCordialement,\n\nPadel Club"
])
return "\n".join(body_parts)
@staticmethod
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.formatted_start_date()} au club {tournament.event.club.name} a été annulée"
]
if other_player is not None:
body_parts.append(
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.",
f"\n{TournamentEmailService._format_umpire_contact(tournament)}",
"\n\nCeci est un e-mail automatique, veuillez ne pas y répondre."
])
return "".join(body_parts)
@staticmethod
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}"
]
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "accéder au tournoi"
absolute_url = f'{link_text}'
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."
)
confirmation_message = TournamentEmailService._build_confirmation_message(captain, tournament, absolute_url)
body_parts.append(confirmation_message)
body_parts.extend([
f"\n\n{TournamentEmailService._format_umpire_contact(tournament)}",
"\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):
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."
]
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{TournamentEmailService._format_umpire_contact(tournament)}",
"\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'{link_text}'
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."
)
confirmation_message = TournamentEmailService._build_confirmation_message(captain, tournament, absolute_url)
body_parts.append(confirmation_message)
body_parts.extend([
f"\n\n{TournamentEmailService._format_umpire_contact(tournament)}",
"\n\nCeci est un e-mail automatique, veuillez ne pas y répondre."
])
return "".join(body_parts)
@staticmethod
def _build_out_of_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 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'{link_text}'
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{TournamentEmailService._format_umpire_contact(tournament)}",
"\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'{link_text}'
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{TournamentEmailService._format_umpire_contact(tournament)}",
"\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'{link_text}'
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."
)
confirmation_message = TournamentEmailService._build_confirmation_message(captain, tournament, absolute_url)
body_parts.append(confirmation_message)
body_parts.extend([
"\n\nPour toute question, veuillez contacter votre juge-arbitre : ",
f"\n{TournamentEmailService._format_umpire_contact(tournament)}",
"\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'{link_text}'
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{TournamentEmailService._format_umpire_contact(tournament)}",
"\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'{link_text}'
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{TournamentEmailService._format_umpire_contact(tournament)}",
"\n\nCeci est un e-mail automatique, veuillez ne pas y répondre."
])
return "".join(body_parts)
@staticmethod
def _format_umpire_contact(tournament):
contact_parts = []
creator_full_name = tournament.umpire_contact()
if creator_full_name:
contact_parts.append(creator_full_name)
if not tournament.hide_umpire_mail:
creator_email = tournament.umpire_mail()
if creator_email:
contact_parts.append(creator_email)
if not tournament.hide_umpire_phone:
creator_phone = tournament.umpire_phone()
if creator_phone:
contact_parts.append(creator_phone)
return "\n".join(contact_parts)
@staticmethod
def _build_confirmation_message(captain, tournament, absolute_url):
"""
Build a standardized confirmation message for emails.
Args:
captain: The player (captain) receiving the email
tournament: The tournament
absolute_url: The URL for confirmation/unregistration
Returns:
str: Formatted confirmation message
"""
time_to_confirm = getattr(captain, 'time_to_confirm', None)
# Common URL and account info text
account_info = "\nVous devez avoir un compte Padel Club."
url_info = f"\n{absolute_url}"
# Base message varies based on whether confirmation is needed
if time_to_confirm is not None:
# Format the deadline time with proper timezone
deadline_str = time_to_confirm.astimezone(tournament.timezone()).strftime("%d/%m/%Y à %H:%M (%Z)")
# Confirmation required message
action_text = "Pour confirmer votre participation au tournoi, cliquez sur ce lien ou contactez rapidement le juge-arbitre."
warning_text = f"⚠️ ATTENTION : Vous avez jusqu'au {deadline_str} pour confirmer votre participation. Passé ce délai, votre place sera automatiquement proposée à l'équipe suivante sur liste d'attente.\n\n"
elif captain.registration_status == RegistrationStatus.PENDING:
action_text = "Pour confirmer votre participation au tournoi, cliquez sur ce lien ou contactez rapidement le juge-arbitre."
warning_text = ""
else:
# Standard message for teams already confirmed
action_text = "Si vous n'êtes plus disponible pour participer à ce tournoi, cliquez sur ce lien ou contactez rapidement le juge-arbitre."
warning_text = ""
# Construct the complete message
return f"\n\n{warning_text}{action_text}{url_info}{account_info}"
@staticmethod
def notify(captain, other_player, tournament, message_type: TeamEmailType):
print("TournamentEmailService.notify", captain.email, captain.registered_online, tournament, message_type)
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
topic = message_type.email_subject()
email_subject = TournamentEmailService.email_subject(tournament, topic)
TournamentEmailService._send_email(captain.email, email_subject, email_body)
@staticmethod
def _build_email_content(message_type, recipient, tournament, tournament_details_str, other_player, request=None, waiting_list_position=None):
if message_type == TeamEmailType.REGISTERED:
body = TournamentEmailService._build_registration_confirmation_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.WAITING_LIST:
body = TournamentEmailService._build_waiting_list_confirmation_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif 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.UNREGISTERED:
body = TournamentEmailService._build_unregistration_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_out_of_tournament_email_body(
tournament, recipient, tournament_details_str, other_player
)
elif message_type == TeamEmailType.IN_TOURNAMENT_STRUCTURE:
body = TournamentEmailService._build_in_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()
print("TournamentEmailService._send_email", to, subject)
@staticmethod
def notify_team(team, tournament, message_type: TeamEmailType):
# Notify both players separately if there is no captain or the captain is unavailable
players = list(team.player_registrations.all())
if len(players) == 2:
print("TournamentEmailService.notify_team 2p", team)
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:
print("TournamentEmailService.notify_team 1p", team)
# If there's only one player, just send them the notification
TournamentEmailService.notify(players[0], None, tournament, message_type)