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.""" """Returns the total number of spots in all group stages."""
return sum(gs.size for gs in self.groupstage_set.all()) 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. Get sorted list of teams for the tournament.
@ -349,7 +349,7 @@ class Tournament(models.Model):
# Process each team registration # Process each team registration
for team_reg in self.teamregistration_set.all(): 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 continue
# Create team item # Create team item
@ -1227,9 +1227,25 @@ class Tournament(models.Model):
if len(teams)<=self.team_count: if len(teams)<=self.team_count:
return None return None
waiting_teams = [team for team in teams if team.stage == "Attente"] 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 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): def broadcasted_prog(self):
# Get matches from broadcasted_matches_and_group_stages # Get matches from broadcasted_matches_and_group_stages

@ -1,6 +1,18 @@
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.utils import timezone from django.utils import timezone
from django.urls import reverse 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: class TournamentEmailService:
@staticmethod @staticmethod
@ -36,15 +48,7 @@ class TournamentEmailService:
tournament_details_str, tournament_details_str,
waiting_list_position waiting_list_position
) )
TournamentEmailService._send_email(request.user.email, email_subject, email_body)
email = EmailMessage(
subject=email_subject,
body=TournamentEmailService._convert_newlines_to_html(email_body),
to=[request.user.email]
)
email.content_subtype = "html"
email.send()
@staticmethod @staticmethod
def _build_email_subject(tournament, tournament_details_str, waiting_list_position): def _build_email_subject(tournament, tournament_details_str, waiting_list_position):
@ -97,68 +101,7 @@ class TournamentEmailService:
other_player other_player
) )
email = EmailMessage( TournamentEmailService._send_email(captain.email, email_subject, email_body)
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()
@staticmethod @staticmethod
def _build_unregistration_email_body(tournament, captain, tournament_details_str, other_player): def _build_unregistration_email_body(tournament, captain, tournament_details_str, other_player):
@ -219,41 +162,277 @@ class TournamentEmailService:
return "".join(body_parts) return "".join(body_parts)
@staticmethod @staticmethod
def send_tournament_cancellation_notification(player, tournament, other_player): def _build_tournament_cancellation_email_body(tournament, player, tournament_details_str, other_player):
tournament_details_str = tournament.build_tournament_details_str() body_parts = [
email_subject = TournamentEmailService.email_subject(tournament, "Annulation du tournoi") "Bonjour,\n\n",
email_body = TournamentEmailService._build_tournament_cancellation_email_body( 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."
tournament, ]
player,
tournament_details_str,
other_player
)
email = EmailMessage( if other_player is not None:
subject=email_subject, body_parts.append(
body=TournamentEmailService._convert_newlines_to_html(email_body), f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
to=[player.email] )
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" body_parts.extend([
email.send() 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 @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 = [ body_parts = [
"Bonjour,\n\n", "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: if other_player is not None:
body_parts.append( body_parts.append(
f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire." f"\nVous étiez inscrit avec {other_player.name()}, n'oubliez pas de prévenir votre partenaire."
) )
body_parts.extend([ 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}", 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." "\n\nCeci est un e-mail automatique, veuillez ne pas y répondre."
]) ])
return "".join(body_parts) 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 random
import string 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.dispatch import receiver
from django.conf import settings from django.conf import settings
from tournaments.models.tournament import Tournament from tournaments.models.tournament import Tournament
from tournaments.models.unregistered_player import UnregisteredPlayer from tournaments.models.unregistered_player import UnregisteredPlayer
from django.utils import timezone 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 import requests
from tournaments.services.email_service import TournamentEmailService from tournaments.services.email_service import TournamentEmailService, TeamEmailType
from tournaments.models import PlayerDataSource from tournaments.models import PlayerDataSource
from shared.discord import send_discord_log_message, send_discord_failed_calls_message from shared.discord import send_discord_log_message, send_discord_failed_calls_message
from datetime import datetime
def generate_unique_code(): def generate_unique_code():
characters = string.ascii_lowercase + string.digits characters = string.ascii_lowercase + string.digits
@ -48,95 +49,91 @@ def notify_object_creation_on_discord(created, instance):
else: else:
send_discord_log_message(message) send_discord_log_message(message)
def notify_team(team, tournament, message_type):
# def send_discord_message(webhook_url, content): if tournament.online_registration is False:
# data = { return
# "content": content TournamentEmailService.notify_team(team, tournament, message_type)
# }
# 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}'
# # )
@receiver(pre_delete, sender=TeamRegistration) @receiver(pre_delete, sender=TeamRegistration)
def unregister_team(sender, instance, **kwargs): def unregister_team(sender, instance, **kwargs):
team_registration = instance if instance.tournament.is_deleted:
tournament = instance.tournament return
if tournament.is_deleted is True: if instance.online_registration is False:
return return
# Create unregistered player records and track captain/other player notify_team(instance, instance.tournament, TeamEmailType.UNREGISTERED)
captain = None first_waiting_list_team = instance.tournament.first_waiting_list_team()
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
if first_waiting_list_team: if first_waiting_list_team:
waiting_captain = None notify_team(first_waiting_list_team, instance.tournament, TeamEmailType.OUT_OF_WAITING_LIST)
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
)
@receiver(post_save, sender=Tournament) @receiver(post_save, sender=Tournament)
def notify_players_of_tournament_cancellation(sender, instance, **kwargs): 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 return
# Get all team registrations for team_registration in instance.teamregistration_set.all():
team_registrations = tournament.teamregistration_set.all() notify_team(team_registration, instance, TeamEmailType.TOURNAMENT_CANCELED)
for team_registration in team_registrations: @receiver(pre_save, sender=Tournament)
captain = None def check_waiting_list(sender, instance, **kwargs):
other_player = None if instance.id is None:
return
# Get players who registered online and have email if instance.online_registration is False:
for player in team_registration.playerregistration_set.all(): return
print(player, player.registered_online)
if player.captain: previous_state = Tournament.objects.get(id=instance.id)
captain = player if previous_state is None:
else: return
other_player = player
teams_out_to_warn = []
# Send email to captain teams_in_to_warn = []
if captain and captain.registered_online and captain.email:
TournamentEmailService.send_tournament_cancellation_notification( if previous_state.team_count > instance.team_count:
captain, teams_to_remove_count = previous_state.team_count - instance.team_count
tournament, sorted_teams = sorted(
other_player [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
# Send email to other player if they exist and registered online ) if previous_state.team_sorting == TeamSortingType.INSCRIPTION_DATE else
if other_player and other_player.registered_online and other_player.email: (t.initial_weight, t.team_registration.id)
TournamentEmailService.send_tournament_cancellation_notification( )
other_player, teams_out_to_warn = sorted_teams[-teams_to_remove_count:]
tournament, elif previous_state.team_count < instance.team_count:
captain 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) tournament = get_object_or_404(Tournament, pk=tournament_id)
teams = tournament.teams(True) teams = tournament.teams(True)
selected_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'] waiting_teams = [team for team in teams if team.stage == "Attente"]
return render(request, 'tournaments/teams.html', { return render(request, 'tournaments/teams.html', {
'tournament': tournament, 'tournament': tournament,

Loading…
Cancel
Save