add the ability to register animation

sync_v2
Raz 7 months ago
parent 0470d80379
commit 260692da75
  1. 35
      tournaments/migrations/0117_playerregistration_user_teamregistration_user_and_more.py
  2. 38
      tournaments/models/enums.py
  3. 3
      tournaments/models/player_registration.py
  4. 3
      tournaments/models/team_registration.py
  5. 89
      tournaments/models/tournament.py
  6. 3
      tournaments/models/unregistered_player.py
  7. 3
      tournaments/models/unregistered_team.py
  8. 158
      tournaments/services/email_service.py
  9. 19
      tournaments/services/tournament_registration.py
  10. 25
      tournaments/services/tournament_unregistration.py
  11. 2
      tournaments/templates/tournaments/tournament_info.html
  12. 5
      tournaments/templates/tournaments/tournament_row.html
  13. 120
      tournaments/views.py

@ -0,0 +1,35 @@
# Generated by Django 5.1 on 2025-04-25 07:48
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tournaments', '0116_customuser_disable_ranking_federal_ruling_and_more'),
]
operations = [
migrations.AddField(
model_name='playerregistration',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='player_registrations', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='teamregistration',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_registrations', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='unregisteredplayer',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unregistered_players', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='unregisteredteam',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='unregistered_teams', to=settings.AUTH_USER_MODEL),
),
]

@ -49,6 +49,44 @@ class FederalLevelCategory(models.IntegerChoices):
P2000 = 2000, 'P2000' P2000 = 2000, 'P2000'
CHPT = 1, 'Championnat' CHPT = 1, 'Championnat'
def localized_word(self):
if self == FederalLevelCategory.UNLISTED:
return "animation"
elif self == FederalLevelCategory.CHPT:
return "championnat"
else:
return "tournoi"
def is_feminine_word(self):
if self == FederalLevelCategory.UNLISTED:
return True
else:
return False
def localized_prefix_at(self):
if self == FederalLevelCategory.UNLISTED:
return "à l'"
else:
return "au "
def localized_prefix_of(self):
if self == FederalLevelCategory.UNLISTED:
return "de l'"
else:
return "du "
def localized_prefix_this(self):
if self == FederalLevelCategory.UNLISTED:
return "cette"
else:
return "ce "
def localized_prefix_that(self):
if self == FederalLevelCategory.UNLISTED:
return "l'"
else:
return "le "
@staticmethod @staticmethod
def min_player_rank(level=None, category=None, age_category=None) -> int: def min_player_rank(level=None, category=None, age_category=None) -> int:
if level == FederalLevelCategory.P25: if level == FederalLevelCategory.P25:

@ -1,5 +1,5 @@
from django.db import models from django.db import models
from . import SideStoreModel, TeamRegistration, PlayerSexType, PlayerDataSource, PlayerPaymentType, OnlineRegistrationStatus from . import SideStoreModel, TeamRegistration, PlayerSexType, PlayerDataSource, PlayerPaymentType, OnlineRegistrationStatus, CustomUser
from .enums import RegistrationStatus from .enums import RegistrationStatus
import uuid import uuid
from django.utils import timezone from django.utils import timezone
@ -7,6 +7,7 @@ from django.utils import timezone
class PlayerRegistration(SideStoreModel): class PlayerRegistration(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
team_registration = models.ForeignKey(TeamRegistration, on_delete=models.SET_NULL, related_name='player_registrations', null=True) team_registration = models.ForeignKey(TeamRegistration, on_delete=models.SET_NULL, related_name='player_registrations', null=True)
user = models.ForeignKey(CustomUser, null=True, blank=True, on_delete=models.SET_NULL, related_name='player_registrations')
first_name = models.CharField(max_length=50, blank=True) first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True) last_name = models.CharField(max_length=50, blank=True)
licence_id = models.CharField(max_length=50, null=True, blank=True) licence_id = models.CharField(max_length=50, null=True, blank=True)

@ -1,5 +1,5 @@
from django.db import models from django.db import models
from . import SideStoreModel, Tournament, GroupStage, Match from . import SideStoreModel, Tournament, GroupStage, Match, CustomUser
import uuid import uuid
from django.utils import timezone from django.utils import timezone
from .enums import RegistrationStatus from .enums import RegistrationStatus
@ -10,6 +10,7 @@ class TeamRegistration(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='team_registrations', null=True) tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='team_registrations', null=True)
group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL, related_name='team_registrations') group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL, related_name='team_registrations')
user = models.ForeignKey(CustomUser, null=True, blank=True, on_delete=models.SET_NULL, related_name='team_registrations')
registration_date = models.DateTimeField(null=True, blank=True) registration_date = models.DateTimeField(null=True, blank=True)
call_date = models.DateTimeField(null=True, blank=True) call_date = models.DateTimeField(null=True, blank=True)
bracket_position = models.IntegerField(null=True, blank=True) bracket_position = models.IntegerField(null=True, blank=True)

@ -131,16 +131,12 @@ class Tournament(BaseModel):
def display_name(self): def display_name(self):
if self.name: if self.name:
if self.federal_level_category == FederalLevelCategory.UNLISTED: return self.short_base_name() + " " + self.name
return self.name
return self.base_name() + " " + self.name
else: else:
return self.base_name() return self.base_name()
def broadcast_display_name(self): def broadcast_display_name(self):
if self.name: if self.name:
if self.federal_level_category == FederalLevelCategory.UNLISTED:
return self.name
return self.short_base_name() + " " + self.name return self.short_base_name() + " " + self.name
else: else:
return self.base_name() return self.base_name()
@ -154,12 +150,17 @@ class Tournament(BaseModel):
def base_name(self): def base_name(self):
return f"{self.level()} {self.category()}" return f"{self.level()} {self.category()}"
def full_name(self):
if self.name:
return f"{self.level()} {self.name} {self.category()} {self.age()}"
return f"{self.level()} {self.category()} {self.age()}"
def short_base_name(self): def short_base_name(self):
category = self.category() category = self.category()
if len(category) > 0: if len(category) > 0 and self.federal_level_category > 1:
return f"{self.level()}{category[0]}" return f"{self.short_level()}{category[0]}"
else: else:
return self.level() return self.short_level()
def filter_name(self): def filter_name(self):
components = [self.formatted_start_date(), self.short_base_name()] components = [self.formatted_start_date(), self.short_base_name()]
@ -185,6 +186,9 @@ class Tournament(BaseModel):
return formats.date_format(self.local_start_date(), format='l j F Y H:i').capitalize() return formats.date_format(self.local_start_date(), format='l j F Y H:i').capitalize()
def level(self): def level(self):
return self.get_federal_level_category_display()
def short_level(self):
if self.federal_level_category == 0: if self.federal_level_category == 0:
return "Anim." return "Anim."
if self.federal_level_category == 1: if self.federal_level_category == 1:
@ -192,6 +196,11 @@ class Tournament(BaseModel):
return self.get_federal_level_category_display() return self.get_federal_level_category_display()
def category(self): def category(self):
if self.federal_age_category > 100 and self.federal_age_category < 200:
if self.federal_category == 0:
return "Garçon"
if self.federal_category == 1:
return "Fille"
return self.get_federal_category_display() return self.get_federal_category_display()
def age(self): def age(self):
@ -1250,24 +1259,25 @@ class Tournament(BaseModel):
# In waiting list with no limit # In waiting list with no limit
return current_team_count - self.team_count return current_team_count - self.team_count
def build_tournament_type_array(self): def build_tournament_type_str(self):
tournament_details = [] tournament_details = []
if self.federal_level_category > 0:
tournament_details.append(self.level()) tournament_details.append(self.level())
if self.category(): if self.category():
tournament_details.append(self.category()) tournament_details.append(self.category())
if self.age() and self.federal_age_category != FederalAgeCategory.SENIOR: if self.age() and self.federal_age_category != FederalAgeCategory.SENIOR:
tournament_details.append(self.age()) 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)) return " ".join(filter(None, tournament_details))
def build_tournament_details_str(self): def build_tournament_details_str(self):
tournament_details = self.build_tournament_type_array()
name_str = self.build_name_details_str() name_str = self.build_name_details_str()
tournament_details = []
if self.federal_level_category > 0:
tournament_details.append(self.level())
if self.category():
tournament_details.append(self.category())
if self.age() and self.federal_age_category != FederalAgeCategory.SENIOR:
tournament_details.append(self.age())
if len(name_str) > 0: if len(name_str) > 0:
tournament_details.append(name_str) tournament_details.append(name_str)
@ -1313,13 +1323,13 @@ class Tournament(BaseModel):
# Check age category restrictions # Check age category restrictions
if self.federal_age_category == FederalAgeCategory.A11_12 and user_age > 12: if self.federal_age_category == FederalAgeCategory.A11_12 and user_age > 12:
reasons.append("Ce tournoi est réservé aux -12 ans") reasons.append("Ce tournoi est réservé aux 12 ans et moins")
if self.federal_age_category == FederalAgeCategory.A13_14 and user_age > 14: if self.federal_age_category == FederalAgeCategory.A13_14 and user_age > 14:
reasons.append("Ce tournoi est réservé aux -14 ans") reasons.append("Ce tournoi est réservé aux 14 ans et moins")
if self.federal_age_category == FederalAgeCategory.A15_16 and user_age > 16: if self.federal_age_category == FederalAgeCategory.A15_16 and user_age > 16:
reasons.append("Ce tournoi est réservé aux -16 ans") reasons.append("Ce tournoi est réservé aux 16 ans et moins")
if self.federal_age_category == FederalAgeCategory.A17_18 and user_age > 18: if self.federal_age_category == FederalAgeCategory.A17_18 and user_age > 18:
reasons.append("Ce tournoi est réservé aux -18 ans") reasons.append("Ce tournoi est réservé aux 18 ans et moins")
if self.federal_age_category == FederalAgeCategory.SENIOR and user_age < 11: if self.federal_age_category == FederalAgeCategory.SENIOR and user_age < 11:
reasons.append("Ce tournoi est réservé aux 11 ans et plus") reasons.append("Ce tournoi est réservé aux 11 ans et plus")
if self.federal_age_category == FederalAgeCategory.A45 and user_age < 45: if self.federal_age_category == FederalAgeCategory.A45 and user_age < 45:
@ -1728,26 +1738,29 @@ class Tournament(BaseModel):
return "journée" return "journée"
def get_player_registration_status_by_licence(self, user): def get_player_registration_status_by_licence(self, user):
licence_id = user.licence_id user_player = self.get_user_registered(user)
if not licence_id: if user_player:
return user_player.get_registration_status()
return None
def get_user_registered(self, user):
if not user.is_authenticated:
return None return None
validator = LicenseValidator(licence_id)
if validator.validate_license():
stripped_license = validator.stripped_license
# Check if there is a PlayerRegistration for this user in this tournament
PlayerRegistration = apps.get_model('tournaments', 'PlayerRegistration') PlayerRegistration = apps.get_model('tournaments', 'PlayerRegistration')
user_player = PlayerRegistration.objects.filter(
licence_id__icontains=stripped_license, # First, try to find a registration directly linked to the user
direct_registration = PlayerRegistration.objects.filter(
team_registration__tournament=self, team_registration__tournament=self,
user=user,
team_registration__walk_out=False
).first() ).first()
if user_player:
return user_player.get_registration_status()
return None if direct_registration:
return direct_registration
def get_user_registered(self, user): # If no direct registration found and user has no license, return None
if not user.is_authenticated or not user.licence_id: if not user.licence_id:
return None return None
# Validate the license format # Validate the license format
@ -1757,10 +1770,8 @@ class Tournament(BaseModel):
# Get the stripped license (without check letter) # Get the stripped license (without check letter)
stripped_license = validator.stripped_license stripped_license = validator.stripped_license
PlayerRegistration = apps.get_model('tournaments', 'PlayerRegistration')
# Check if there's a player registration with this license in the tournament # Fall back to checking by license ID
# that hasn't walked out
return PlayerRegistration.objects.filter( return PlayerRegistration.objects.filter(
team_registration__tournament=self, team_registration__tournament=self,
licence_id__icontains=stripped_license, licence_id__icontains=stripped_license,
@ -1809,6 +1820,14 @@ class Tournament(BaseModel):
else: else:
return 0 return 0
def is_free(self):
if self.entry_fee is not None and self.entry_fee == 0:
return True
elif self.entry_fee is None:
return True
else:
return False
def effective_commission_rate(self): def effective_commission_rate(self):
"""Get the commission rate for this tournament, falling back to the umpire default if not set""" """Get the commission rate for this tournament, falling back to the umpire default if not set"""
return 1.00 # Fallback default return 1.00 # Fallback default

@ -1,11 +1,12 @@
from django.db import models from django.db import models
from . import UnregisteredTeam from . import UnregisteredTeam, CustomUser
from .player_enums import PlayerPaymentType from .player_enums import PlayerPaymentType
import uuid import uuid
class UnregisteredPlayer(models.Model): class UnregisteredPlayer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
unregistered_team = models.ForeignKey(UnregisteredTeam, on_delete=models.SET_NULL, related_name='unregistered_players', null=True, blank=True) unregistered_team = models.ForeignKey(UnregisteredTeam, on_delete=models.SET_NULL, related_name='unregistered_players', null=True, blank=True)
user = models.ForeignKey(CustomUser, null=True, blank=True, on_delete=models.SET_NULL, related_name='unregistered_players')
first_name = models.CharField(max_length=50, blank=True) first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True) last_name = models.CharField(max_length=50, blank=True)
licence_id = models.CharField(max_length=50, null=True, blank=True) licence_id = models.CharField(max_length=50, null=True, blank=True)

@ -1,9 +1,10 @@
from django.db import models from django.db import models
from . import Tournament from . import Tournament, CustomUser
import uuid import uuid
class UnregisteredTeam(models.Model): class UnregisteredTeam(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
user = models.ForeignKey(CustomUser, null=True, blank=True, on_delete=models.SET_NULL, related_name='unregistered_teams')
tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='unregistered_teams', null=True, blank=True) tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='unregistered_teams', null=True, blank=True)
unregistration_date = models.DateTimeField(null=True, blank=True) unregistration_date = models.DateTimeField(null=True, blank=True)

@ -1,6 +1,6 @@
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from enum import Enum from enum import Enum
from ..models.enums import RegistrationStatus from ..models.enums import RegistrationStatus, FederalLevelCategory
from ..models.tournament import TeamSortingType from ..models.tournament import TeamSortingType
from django.utils import timezone from django.utils import timezone
@ -18,7 +18,7 @@ class TeamEmailType(Enum):
UNEXPECTED_OUT_OF_TOURNAMENT = 'unexpected_out_of_tournament' UNEXPECTED_OUT_OF_TOURNAMENT = 'unexpected_out_of_tournament'
REQUIRES_TIME_CONFIRMATION = 'requires_time_confirmation' REQUIRES_TIME_CONFIRMATION = 'requires_time_confirmation'
def email_subject(self, time_to_confirm=None) -> str: def email_topic(self, category=None, time_to_confirm=None) -> str:
confirmation_types = [ confirmation_types = [
self.REGISTERED, self.REGISTERED,
self.OUT_OF_WAITING_LIST, self.OUT_OF_WAITING_LIST,
@ -26,6 +26,12 @@ class TeamEmailType(Enum):
self.OUT_OF_WALKOUT_IS_IN, self.OUT_OF_WALKOUT_IS_IN,
self.REQUIRES_TIME_CONFIRMATION, self.REQUIRES_TIME_CONFIRMATION,
] ]
word = "Tournoi"
grammar = ''
if category is not None:
federal_category = FederalLevelCategory(category)
word = federal_category.localized_word().capitalize()
grammar = 'e' if federal_category.is_feminine_word() else ''
if time_to_confirm and self in confirmation_types: if time_to_confirm and self in confirmation_types:
return "Participation en attente de confirmation" return "Participation en attente de confirmation"
@ -36,7 +42,7 @@ class TeamEmailType(Enum):
self.UNREGISTERED: "Désistement", self.UNREGISTERED: "Désistement",
self.OUT_OF_WAITING_LIST: "Participation confirmée", self.OUT_OF_WAITING_LIST: "Participation confirmée",
self.REQUIRES_TIME_CONFIRMATION: "Participation en attente de confirmation", self.REQUIRES_TIME_CONFIRMATION: "Participation en attente de confirmation",
self.TOURNAMENT_CANCELED: "Tournoi annulé", self.TOURNAMENT_CANCELED: f"{word} annulé{grammar}",
self.IN_TOURNAMENT_STRUCTURE: "Participation confirmée", self.IN_TOURNAMENT_STRUCTURE: "Participation confirmée",
self.OUT_OF_TOURNAMENT_STRUCTURE: "Participation annulée", self.OUT_OF_TOURNAMENT_STRUCTURE: "Participation annulée",
self.OUT_OF_WALKOUT_IS_IN: "Participation confirmée", self.OUT_OF_WALKOUT_IS_IN: "Participation confirmée",
@ -81,34 +87,39 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_registration_email_body(tournament, captain, tournament_details_str, other_player, waiting_list): 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") inscription_date = captain.team_registration.local_registration_date().strftime("%d/%m/%Y à %H:%M")
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_of = federal_level_category.localized_prefix_of()
tournament_prefix_that = federal_level_category.localized_prefix_that()
body_parts = [] body_parts = []
body_parts.append("Bonjour,\n") body_parts.append("Bonjour,\n")
if waiting_list: if waiting_list:
body_parts.append(f"Votre inscription en liste d'attente du tournoi {tournament_details_str} est confirmée.") body_parts.append(f"Votre inscription en liste d'attente {tournament_prefix_of}{tournament_word} {tournament_details_str} prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} est confirmée.")
else: else:
body_parts.append(f"Votre inscription au tournoi {tournament_details_str} est confirmée.") body_parts.append(f"Votre inscription {tournament_prefix_at}{tournament_word} {tournament_details_str} prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} est confirmée.")
if tournament.team_sorting == TeamSortingType.RANK: if tournament.team_sorting == TeamSortingType.RANK:
cloture_date = tournament.local_registration_federal_limit().strftime("%d/%m/%Y à %H:%M") cloture_date = tournament.local_registration_federal_limit()
loc = "" loc = ""
if cloture_date is not None: if cloture_date is not None:
loc = f", prévu le {cloture_date}" loc = f", prévu le {cloture_date.strftime("%d/%m/%Y à %H:%M")}"
body_parts.append(f"Attention, la sélection définitive se fera par poids d'équipe à la clôture des inscriptions{loc}.") 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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.extend([ body_parts.extend([
f"\nDate d'inscription: {inscription_date}", f"\nDate d'inscription: {inscription_date}",
f"\nÉquipe inscrite: {captain.name()} et {other_player.name()}", 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}" f"\nVoir les {absolute_url}"
]) ])
# Add payment information if applicable # Add payment information if applicable
if tournament.should_request_payment: if tournament.should_request_payment():
payment_info = TournamentEmailService._build_payment_info(tournament, captain.team_registration) payment_info = TournamentEmailService._build_payment_info(tournament, captain.team_registration)
body_parts.append(payment_info) body_parts.append(payment_info)
@ -123,9 +134,14 @@ class TournamentEmailService:
@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):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_that = federal_level_category.localized_prefix_that()
body_parts = [ body_parts = [
"Bonjour,\n\n", "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" f"Votre inscription {tournament_prefix_at}{tournament_word} {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: if other_player is not None:
@ -134,7 +150,7 @@ class TournamentEmailService:
) )
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append( body_parts.append(
@ -152,13 +168,17 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_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):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
body_parts = [ body_parts = [
"Bonjour,\n\n", "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}" f"Suite au désistement d'une paire, vous êtes maintenant inscrit {tournament_prefix_at}{tournament_word} {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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "accéder au tournoi" link_text = f"accéder {tournament_prefix_at}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
if other_player is not None: if other_player is not None:
@ -178,13 +198,17 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_requires_confirmation_email_body(tournament, captain, tournament_details_str, other_player): def _build_requires_confirmation_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
body_parts = [ body_parts = [
"Bonjour,\n\n", "Bonjour,\n\n",
f"Vous n'avez toujours pas confirmé votre participation au tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name}" f"Vous n'avez toujours pas confirmé votre participation {tournament_prefix_at}{tournament_word} {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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "accéder au tournoi" link_text = f"accéder {tournament_prefix_at}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
if other_player is not None: if other_player is not None:
@ -204,9 +228,13 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _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):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_that = federal_level_category.localized_prefix_that()
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"{tournament_prefix_that.capitalize()}{tournament_word} {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: if other_player is not None:
@ -224,13 +252,18 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_in_tournament_email_body(tournament, captain, tournament_details_str, other_player): def _build_in_tournament_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_of = federal_level_category.localized_prefix_of()
body_parts = [ body_parts = [
"Bonjour,\n\n", "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}" f"Suite à une modification de la taille {tournament_prefix_of}{tournament_word}, vous pouvez participer {tournament_prefix_at}{tournament_word} {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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "accéder au tournoi" link_text = f"accéder {tournament_prefix_at}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
if other_player is not None: if other_player is not None:
@ -250,13 +283,19 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_out_of_tournament_email_body(tournament, captain, tournament_details_str, other_player): def _build_out_of_tournament_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_of = federal_level_category.localized_prefix_of()
tournament_prefix_that = federal_level_category.localized_prefix_that()
body_parts = [ body_parts = [
"Bonjour,\n\n", "Bonjour,\n\n",
f"Suite à une modification de la taille du tournoi, vous avez été placé en liste d'attente. Votre participation au tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} n'est plus confirmée." f"Suite à une modification de la taille {tournament_prefix_of}{tournament_word}, vous avez été placé en liste d'attente. Votre participation {tournament_prefix_at}{tournament_word} {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} n'est plus confirmée."
] ]
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")
@ -276,13 +315,17 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_walk_out_email_body(tournament, captain, tournament_details_str, other_player): def _build_walk_out_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
body_parts = [ body_parts = [
"Bonjour,\n\n", "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}" f"Le juge-arbitre a annulé votre participation {tournament_prefix_at}{tournament_word} {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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "accéder au tournoi" link_text = f"accéder {tournament_prefix_at}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")
@ -303,13 +346,18 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_out_of_walkout_is_in_email_body(tournament, captain, tournament_details_str, other_player): def _build_out_of_walkout_is_in_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_that = federal_level_category.localized_prefix_that()
body_parts = [ body_parts = [
"Bonjour,\n\n", "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}" f"Le juge-arbitre vous a ré-intégré {tournament_prefix_at}{tournament_word} {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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")
@ -332,13 +380,18 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_unexpected_out_of_tournament_email_body(tournament, captain, tournament_details_str, other_player): def _build_unexpected_out_of_tournament_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_that = federal_level_category.localized_prefix_that()
body_parts = [ body_parts = [
"Bonjour,\n\n", "Bonjour,\n\n",
f"En raison d'une décision du juge-arbitre, vous avez été placé en liste d'attente. Votre participation au tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} n'est plus confirmée." f"En raison d'une décision du juge-arbitre, vous avez été placé en liste d'attente. Votre participation {tournament_prefix_at}{tournament_word} {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} n'est plus confirmée."
] ]
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")
@ -358,17 +411,22 @@ class TournamentEmailService:
@staticmethod @staticmethod
def _build_out_of_walkout_waiting_list_email_body(tournament, captain, tournament_details_str, other_player): def _build_out_of_walkout_waiting_list_email_body(tournament, captain, tournament_details_str, other_player):
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_of = federal_level_category.localized_prefix_of()
tournament_prefix_that = federal_level_category.localized_prefix_that()
body_parts = [ body_parts = [
"Bonjour,\n\n", "Bonjour,\n\n",
] ]
if captain.registration_status == RegistrationStatus.CANCELED: if captain.registration_status == RegistrationStatus.CANCELED:
body_parts.append("Le temps accordé pour confirmer votre inscription s'est écoulé.") body_parts.append("Le temps accordé pour confirmer votre inscription s'est écoulé.")
body_parts.append(f"Vous avez été replacé en liste d'attente du tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name}") body_parts.append(f"Vous avez été replacé en liste d'attente {tournament_prefix_of}{tournament_word} {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name}")
else: else:
body_parts.append(f"Le juge-arbitre vous a placé en liste d'attente du tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name}") body_parts.append(f"Le juge-arbitre vous a placé en liste d'attente {tournament_prefix_of}{tournament_word} {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" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")
@ -424,20 +482,25 @@ class TournamentEmailService:
account_info = "\nVous devez avoir un compte Padel Club." account_info = "\nVous devez avoir un compte Padel Club."
url_info = f"\n{absolute_url}" url_info = f"\n{absolute_url}"
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_at = federal_level_category.localized_prefix_at()
tournament_prefix_this = federal_level_category.localized_prefix_this()
# Base message varies based on whether confirmation is needed # Base message varies based on whether confirmation is needed
if time_to_confirm is not None: if time_to_confirm is not None:
# Format the deadline time with proper timezone # Format the deadline time with proper timezone
deadline_str = time_to_confirm.astimezone(tournament.timezone()).strftime("%d/%m/%Y à %H:%M (%Z)") deadline_str = time_to_confirm.astimezone(tournament.timezone()).strftime("%d/%m/%Y à %H:%M (%Z)")
# Confirmation required message # Confirmation required message
action_text = f"Pour confirmer votre participation au tournoi, cliquez sur ce lien pour {url_info} ou contactez rapidement le juge-arbitre." action_text = f"Pour confirmer votre participation {tournament_prefix_at}{tournament_word}, cliquez sur ce lien pour {url_info} 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" 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: elif captain.registration_status == RegistrationStatus.PENDING:
action_text = f"Pour confirmer votre participation au tournoi, cliquez sur ce lien pour {url_info} ou contactez rapidement le juge-arbitre." action_text = f"Pour confirmer votre participation {tournament_prefix_at}{tournament_word}, cliquez sur ce lien pour {url_info} ou contactez rapidement le juge-arbitre."
warning_text = " ATTENTION : Actuellement, il n'y a pas de liste d'attente pour ce tournoi. Dès qu'une liste d'attente se formera, vous recevrez un email avec un délai précis pour confirmer votre participation.\n\n" warning_text = f" ATTENTION : Actuellement, il n'y a pas de liste d'attente pour {tournament_prefix_this}{tournament_word}. Dès qu'une liste d'attente se formera, vous recevrez un email avec un délai précis pour confirmer votre participation.\n\n"
else: else:
# Standard message for teams already confirmed # Standard message for teams already confirmed
action_text = f"Si vous n'êtes plus disponible pour participer à ce tournoi, cliquez sur ce lien pour {url_info} ou contactez rapidement le juge-arbitre." action_text = f"Si vous n'êtes plus disponible pour participer à {tournament_prefix_this}{tournament_word}, cliquez sur ce lien pour {url_info} ou contactez rapidement le juge-arbitre."
warning_text = "" warning_text = ""
# Construct the complete message # Construct the complete message
@ -458,7 +521,7 @@ class TournamentEmailService:
if email_body is None: if email_body is None:
return return
topic = message_type.email_subject(captain.time_to_confirm) topic = message_type.email_topic(tournament.federal_level_category, captain.time_to_confirm)
email_subject = TournamentEmailService.email_subject(tournament, topic) email_subject = TournamentEmailService.email_subject(tournament, topic)
TournamentEmailService._send_email(captain.email, email_subject, email_body) TournamentEmailService._send_email(captain.email, email_subject, email_body)
@ -536,6 +599,7 @@ class TournamentEmailService:
print("TournamentEmailService.notify_team 2p", team) print("TournamentEmailService.notify_team 2p", team)
first_player, second_player = players first_player, second_player = players
TournamentEmailService.notify(first_player, second_player, tournament, message_type) TournamentEmailService.notify(first_player, second_player, tournament, message_type)
if first_player.email != second_player.email:
TournamentEmailService.notify(second_player, first_player, tournament, message_type) TournamentEmailService.notify(second_player, first_player, tournament, message_type)
elif len(players) == 1: elif len(players) == 1:
print("TournamentEmailService.notify_team 1p", team) print("TournamentEmailService.notify_team 1p", team)
@ -547,7 +611,10 @@ class TournamentEmailService:
""" """
Build payment information section for emails Build payment information section for emails
""" """
if not tournament.should_request_payment: if not tournament.should_request_payment():
return ""
if tournament.is_free():
return "" return ""
# Check payment status # Check payment status
@ -591,6 +658,10 @@ class TournamentEmailService:
if payment_amount is None: if payment_amount is None:
payment_amount = tournament.team_fee() payment_amount = tournament.team_fee()
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_that = federal_level_category.localized_prefix_that()
for player in player_registrations: for player in player_registrations:
if not player.email or not player.registered_online: if not player.email or not player.registered_online:
continue continue
@ -600,7 +671,7 @@ class TournamentEmailService:
body_parts = [ body_parts = [
"Bonjour,\n\n", "Bonjour,\n\n",
f"Votre paiement pour le tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été reçu avec succès." f"Votre paiement pour {tournament_prefix_that}{tournament_word} {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été reçu avec succès."
] ]
# Add information about the other player if available # Add information about the other player if available
@ -620,7 +691,7 @@ class TournamentEmailService:
) )
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")
@ -662,16 +733,23 @@ class TournamentEmailService:
if refund_amount is None: if refund_amount is None:
refund_amount = tournament.team_fee() refund_amount = tournament.team_fee()
federal_level_category = FederalLevelCategory(tournament.federal_level_category)
tournament_word = federal_level_category.localized_word()
tournament_prefix_that = federal_level_category.localized_prefix_that()
processed_emails = set()
for player in player_registrations: for player in player_registrations:
if not player.email or not player.registered_online: if not player.email or not player.registered_online:
continue continue
if player.email in processed_emails:
continue
processed_emails.add(player.email)
tournament_details_str = tournament.build_tournament_details_str() tournament_details_str = tournament.build_tournament_details_str()
other_player = team_registration.get_other_player(player) if len(player_registrations) > 1 else None other_player = team_registration.get_other_player(player) if len(player_registrations) > 1 else None
body_parts = [ body_parts = [
"Bonjour,\n\n", "Bonjour,\n\n",
f"Votre remboursement pour le tournoi {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été traité avec succès." f"Votre remboursement pour {tournament_prefix_that}{tournament_word} {tournament_details_str}, prévu le {tournament.formatted_start_date()} au club {tournament.event.club.name} a été traité avec succès."
] ]
# Add information about the other player if available # Add information about the other player if available
@ -691,7 +769,7 @@ class TournamentEmailService:
) )
absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info" absolute_url = f"https://padelclub.app/tournament/{tournament.id}/info"
link_text = "informations sur le tournoi" link_text = f"informations sur {tournament_prefix_that}{tournament_word}"
absolute_url = f'<a href="{absolute_url}">{link_text}</a>' absolute_url = f'<a href="{absolute_url}">{link_text}</a>'
body_parts.append(f"\n\nVoir les {absolute_url}") body_parts.append(f"\n\nVoir les {absolute_url}")

@ -208,7 +208,6 @@ class RegistrationCartManager:
elif not tournament.license_is_required: elif not tournament.license_is_required:
# License not required, check if name is provided # License not required, check if name is provided
if not first_name or not last_name: if not first_name or not last_name:
self.first_tournament = True
return False, "Le prénom et le nom sont obligatoires." return False, "Le prénom et le nom sont obligatoires."
else: else:
# License is required but not provided # License is required but not provided
@ -335,6 +334,11 @@ class RegistrationCartManager:
return False, f"Vous avez besoin d'au moins {tournament.minimum_player_per_team} joueurs pour vous inscrire." return False, f"Vous avez besoin d'au moins {tournament.minimum_player_per_team} joueurs pour vous inscrire."
# Identify captain from user's license # Identify captain from user's license
# # Update user phone if provided
if self.request.user.is_authenticated and mobile_number:
self.request.user.phone = mobile_number
self.request.user.save(update_fields=['phone'])
stripped_license = None stripped_license = None
if self.request.user.is_authenticated and self.request.user.licence_id: if self.request.user.is_authenticated and self.request.user.licence_id:
validator = LicenseValidator(self.request.user.licence_id) validator = LicenseValidator(self.request.user.licence_id)
@ -348,6 +352,7 @@ class RegistrationCartManager:
registration_date=timezone.now(), registration_date=timezone.now(),
walk_out=False, walk_out=False,
weight=weight, weight=weight,
user=self.request.user
) )
for player_data in players: # Compute rank and sex using the original logic for player_data in players: # Compute rank and sex using the original logic
@ -364,17 +369,20 @@ class RegistrationCartManager:
data_source = PlayerDataSource.FRENCH_FEDERATION # Now using the enum value data_source = PlayerDataSource.FRENCH_FEDERATION # Now using the enum value
User = get_user_model() User = get_user_model()
matching_user = None matching_user = self.request.user
if player_licence_id: if player_licence_id and (stripped_license is None or is_captain is False):
try: try:
# Using icontains for case-insensitive match # Using icontains for case-insensitive match
matching_user = User.objects.get(licence_id__icontains=player_licence_id) matching_user = User.objects.get(licence_id__icontains=player_licence_id)
if matching_user is None:
matching_user = self.request.user
except User.DoesNotExist: except User.DoesNotExist:
pass pass
# Create player registration with all the original fields # Create player registration with all the original fields
PlayerRegistration.objects.create( PlayerRegistration.objects.create(
team_registration=team_registration, team_registration=team_registration,
user=matching_user,
captain=is_captain, captain=is_captain,
source=data_source, source=data_source,
registered_online=True, registered_online=True,
@ -395,11 +403,6 @@ class RegistrationCartManager:
registration_status=RegistrationStatus.CONFIRMED if self.session.get('waiting_list_position', 0) < 0 else RegistrationStatus.WAITING registration_status=RegistrationStatus.CONFIRMED if self.session.get('waiting_list_position', 0) < 0 else RegistrationStatus.WAITING
) )
# Update user phone if provided
if self.request.user.is_authenticated and mobile_number:
self.request.user.phone = mobile_number
self.request.user.save(update_fields=['phone'])
# Clear the cart # Clear the cart
self.clear_cart() self.clear_cart()
tournament.reserved_spots = max(0, tournament.reserved_spots - 1) tournament.reserved_spots = max(0, tournament.reserved_spots - 1)

@ -18,10 +18,10 @@ class TournamentUnregistrationService:
messages.error(self.request, "Le désistement n'est plus possible pour ce tournoi. Si vous souhaitez vous désinscrire, veuillez contacter le juge-arbitre.") messages.error(self.request, "Le désistement n'est plus possible pour ce tournoi. Si vous souhaitez vous désinscrire, veuillez contacter le juge-arbitre.")
return False return False
if not self.request.user.licence_id: # if not self.request.user.licence_id:
messages.error(self.request, # messages.error(self.request,
"Vous ne pouvez pas vous désinscrire car vous n'avez pas de numéro de licence associé.") # "Vous ne pouvez pas vous désinscrire car vous n'avez pas de numéro de licence associé.")
return False # return False
return True return True
@ -80,12 +80,14 @@ class TournamentUnregistrationService:
unregistered_team = UnregisteredTeam.objects.create( unregistered_team = UnregisteredTeam.objects.create(
tournament=team_registration.tournament, tournament=team_registration.tournament,
user=team_registration.user,
unregistration_date=timezone.now(), unregistration_date=timezone.now(),
) )
for player in team_registration.players_sorted_by_rank: for player in team_registration.players_sorted_by_rank:
UnregisteredPlayer.objects.create( UnregisteredPlayer.objects.create(
unregistered_team=unregistered_team, unregistered_team=unregistered_team,
user=player.user,
first_name=player.first_name, first_name=player.first_name,
last_name=player.last_name, last_name=player.last_name,
licence_id=player.licence_id, licence_id=player.licence_id,
@ -95,18 +97,9 @@ class TournamentUnregistrationService:
) )
def _find_player_registration(self): def _find_player_registration(self):
if not self.request.user.licence_id: # First check if we can find the player registration directly by user
return False if self.request.user.is_authenticated:
validator = LicenseValidator(self.request.user.licence_id) self.player_registration = self.tournament.get_user_registered(self.request.user)
is_license_valid = validator.validate_license()
if not is_license_valid:
return False
self.player_registration = PlayerRegistration.objects.filter(
licence_id__icontains=validator.stripped_license,
team_registration__tournament_id=self.tournament.id,
).first()
if self.player_registration: if self.player_registration:
self.team_registration = self.player_registration.team_registration self.team_registration = self.player_registration.team_registration

@ -148,7 +148,7 @@
</div> </div>
{% endif %} {% endif %}
<div class="cell medium-12 large-6 padding10"> <div class="cell medium-12 large-6 padding10">
<h1 class="club padding10">{{ tournament.display_name }} {{ tournament.get_federal_age_category_display}}</h1> <h1 class="club padding10">{{ tournament.full_name }}</h1>
<div class="bubble"> <div class="bubble">
<div class="semibold">{{ tournament.local_start_date_formatted }}</div> <div class="semibold">{{ tournament.local_start_date_formatted }}</div>
<div>{{ tournament.day_duration_formatted }}</div> <div>{{ tournament.day_duration_formatted }}</div>

@ -8,7 +8,7 @@
<div class="small">{{ tournament.month }}</div> <div class="small">{{ tournament.month }}</div>
</div> </div>
<div class="table-row-element tournament-type"> <div class="table-row-element tournament-type">
<div class="very-large">{{ tournament.level }}</div> <div class="very-large">{{ tournament.short_level }}</div>
{% if tournament.category %} {% if tournament.category %}
<div class="small">{{ tournament.category }}</div> <div class="small">{{ tournament.category }}</div>
{% endif %} {% endif %}
@ -21,6 +21,9 @@
{% if tournament.event.name %} {% if tournament.event.name %}
<div class="small event-name">{{ tournament.event.name }}</div> <div class="small event-name">{{ tournament.event.name }}</div>
{% endif %} {% endif %}
{% if tournament.name %}
<div class="small event-name">{{ tournament.name }}</div>
{% endif %}
<div class="small">{{ tournament.localized_day_duration }}</div> <div class="small">{{ tournament.localized_day_duration }}</div>
</div> </div>
<div class="table-row-element tournament-status center"> <div class="table-row-element tournament-status center">

@ -172,6 +172,7 @@ def tournament_info(request, tournament_id):
team_registration = None team_registration = None
is_captain = False is_captain = False
player_register_check = None player_register_check = None
if request.method == 'POST':
storage = messages.get_messages(request) storage = messages.get_messages(request)
for _ in storage: for _ in storage:
pass pass
@ -179,21 +180,11 @@ def tournament_info(request, tournament_id):
if len(storage._loaded_messages) == 1: if len(storage._loaded_messages) == 1:
del storage._loaded_messages[0] del storage._loaded_messages[0]
if request.user.is_authenticated: if request.user.is_authenticated:
# Assuming user's licence_id is stored in the user profile (e.g., request.user.licence_id) # Assuming user's licence_id is stored in the user profile (e.g., request.user.licence_id)
user_licence_id = request.user.licence_id user_licence_id = request.user.licence_id
player_register_check = tournament.player_register_check(user_licence_id) player_register_check = tournament.player_register_check(user_licence_id)
if user_licence_id is not None and player_register_check is None: registered_user = tournament.get_user_registered(request.user)
validator = LicenseValidator(user_licence_id)
stripped_license = validator.stripped_license
# Check if there is a PlayerRegistration for this user in this tournament
registered_user = PlayerRegistration.objects.filter(
licence_id__icontains=stripped_license,
team_registration__tournament=tournament,
team_registration__walk_out=False,
).first()
# If the user is registered, retrieve their team registration # If the user is registered, retrieve their team registration
if registered_user: if registered_user:
is_captain = registered_user.captain is_captain = registered_user.captain
@ -774,63 +765,21 @@ def custom_password_change(request):
@login_required @login_required
def my_tournaments(request): def my_tournaments(request):
user = request.user
user_licence_id = request.user.licence_id
# If no licence_id, return empty lists
if user_licence_id is None:
return render(request, 'registration/my_tournaments.html', {
'upcoming_tournaments': [],
'running_tournaments': [],
'ended_tournaments': [],
'user_name': user.username
})
# Get all tournaments for the user based on their licence
validator = LicenseValidator(user_licence_id)
stripped_license = validator.stripped_license
def filter_user_tournaments(tournaments):
return [t for t in tournaments if t.team_registrations.filter(
player_registrations__licence_id__icontains=stripped_license,
walk_out=False
).exists()]
# Get filtered tournaments using the helper function # Get filtered tournaments using the helper function
upcoming_tournaments = filter_user_tournaments(future_tournaments(None)) upcoming_tournaments = get_user_tournaments(request.user, future_tournaments(None))
running_tournaments = filter_user_tournaments(live_tournaments(None)) running_tournaments = get_user_tournaments(request.user, live_tournaments(None))
ended_tournaments = filter_user_tournaments([t for t in finished_tournaments(None) if not t.is_canceled()]) ended_tournaments = get_user_tournaments(request.user, [t for t in finished_tournaments(None) if not t.is_canceled()])
return render(request, 'registration/my_tournaments.html', { return render(request, 'registration/my_tournaments.html', {
'upcoming_tournaments': upcoming_tournaments, 'upcoming_tournaments': upcoming_tournaments,
'running_tournaments': running_tournaments, 'running_tournaments': running_tournaments,
'ended_tournaments': ended_tournaments[:12], 'ended_tournaments': ended_tournaments[:12],
'user_name': user.username 'user_name': request.user.username
}) })
@login_required @login_required
def all_my_ended_tournaments(request): def all_my_ended_tournaments(request):
user_licence_id = request.user.licence_id ended_tournaments = get_user_tournaments(request.user, finished_tournaments(None))
# If no licence_id, return empty lists
if user_licence_id is None:
return render(request, '/tournaments.html', {
'tournaments': [],
'title': "Palmarès",
})
# Get all tournaments for the user based on their licence
validator = LicenseValidator(user_licence_id)
stripped_license = validator.stripped_license
def filter_user_tournaments(tournaments):
return [t for t in tournaments if t.team_registrations.filter(
player_registrations__licence_id__icontains=stripped_license,
walk_out=False
).exists()]
ended_tournaments = filter_user_tournaments(finished_tournaments(None))
return render(request, return render(request,
"tournaments/tournaments_list.html", "tournaments/tournaments_list.html",
{ {
@ -1072,23 +1021,11 @@ def custom_logout(request):
def confirm_tournament_registration(request, tournament_id): def confirm_tournament_registration(request, tournament_id):
if request.method == 'POST': if request.method == 'POST':
tournament = get_object_or_404(Tournament, pk=tournament_id) tournament = get_object_or_404(Tournament, pk=tournament_id)
# Find the team registration for this user team_registration = tournament.get_user_team_registration(request.user)
user_licence_id = request.user.licence_id if team_registration is None:
if user_licence_id is not None:
validator = LicenseValidator(user_licence_id)
stripped_license = validator.stripped_license
# Check if there is a PlayerRegistration for this user in this tournament
team_registrations = TeamRegistration.objects.filter(
tournament=tournament,
player_registrations__licence_id__icontains=stripped_license,
)
if not team_registrations.exists():
messages.error(request, "Aucune inscription trouvée pour ce tournoi.") messages.error(request, "Aucune inscription trouvée pour ce tournoi.")
return redirect('tournament-info', tournament_id=tournament_id) return redirect('tournament-info', tournament_id=tournament_id)
team_registration = team_registrations.first()
if team_registration.is_confirmation_expired(): if team_registration.is_confirmation_expired():
team_registration.check_confirmation_deadline() team_registration.check_confirmation_deadline()
messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus confirmer votre participation.") messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus confirmer votre participation.")
@ -1563,6 +1500,45 @@ def xls_to_csv(request):
else: else:
return HttpResponse("No file was uploaded", status=400) return HttpResponse("No file was uploaded", status=400)
def get_user_tournaments(user, tournaments):
user_tournaments = []
if user.is_authenticated is False:
return user_tournaments
user_licence_id = user.licence_id
stripped_license = None
# If no licence_id, return empty lists
if user_licence_id is not None:
# Get all tournaments for the user based on their licence
validator = LicenseValidator(user_licence_id)
stripped_license = validator.stripped_license
for t in tournaments:
# First check for direct user relationship
direct_registration_exists = t.team_registrations.filter(
player_registrations__user=user,
walk_out=False
).exists()
# If direct relationship exists, add the tournament
if direct_registration_exists:
user_tournaments.append(t)
continue
# Otherwise, check by license
license_registration_exists = None
if stripped_license:
license_registration_exists = t.team_registrations.filter(
player_registrations__licence_id__icontains=stripped_license,
walk_out=False
).exists()
if license_registration_exists:
user_tournaments.append(t)
return user_tournaments
class UserListExportView(LoginRequiredMixin, View): class UserListExportView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
users = CustomUser.objects.order_by('date_joined') users = CustomUser.objects.order_by('date_joined')

Loading…
Cancel
Save