diff --git a/shop/static/shop/css/shop.css b/shop/static/shop/css/shop.css index 105aaf3..824a705 100644 --- a/shop/static/shop/css/shop.css +++ b/shop/static/shop/css/shop.css @@ -95,7 +95,7 @@ .add-to-cart-button, .checkout-button { background-color: #90ee90; - color: #707070; + color: #505050; border: none; border-radius: 12px; font-size: 12px; @@ -120,7 +120,7 @@ } .coupon-section { - color: #707070; + color: #505050; font-size: 12px; font-weight: 600; text-decoration: none; @@ -129,7 +129,7 @@ .confirm-nav-button { background-color: #90ee90; - color: #707070; + color: #505050; font-size: 12px; font-weight: 600; text-decoration: none; diff --git a/shop/templates/shop/product_item.html b/shop/templates/shop/product_item.html index 4cdb4bb..bb7c275 100644 --- a/shop/templates/shop/product_item.html +++ b/shop/templates/shop/product_item.html @@ -185,7 +185,7 @@ function addToCartAjax(productId) { notification.style.right = '20px'; notification.style.padding = '20px'; notification.style.backgroundColor = '#90ee90'; - notification.style.color = '#707070'; + notification.style.color = '#505050'; notification.style.borderRadius = '12px'; notification.style.zIndex = '9999'; notification.style.opacity = '0'; diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py index d2e5187..8a2f5e4 100644 --- a/tournaments/models/enums.py +++ b/tournaments/models/enums.py @@ -146,6 +146,7 @@ class OnlineRegistrationStatus(models.IntegerChoices): WAITING_LIST_FULL = 6, 'Waiting List Full' IN_PROGRESS = 7, 'In Progress' ENDED_WITH_RESULTS = 8, 'Ended with Results' + CANCELED = 9, 'Canceled' def status_localized(self) -> str: status_map = { @@ -156,10 +157,58 @@ class OnlineRegistrationStatus(models.IntegerChoices): OnlineRegistrationStatus.WAITING_LIST_POSSIBLE: "Liste d'attente ouverte", OnlineRegistrationStatus.WAITING_LIST_FULL: "Liste d'attente complète", OnlineRegistrationStatus.IN_PROGRESS: "Tournoi en cours", - OnlineRegistrationStatus.ENDED_WITH_RESULTS: "Tournoi terminé" + OnlineRegistrationStatus.ENDED_WITH_RESULTS: "Tournoi terminé", + OnlineRegistrationStatus.CANCELED: "Tournoi annulé" } return status_map.get(self, "") + def short_label(self) -> str: + """Returns a short, concise label for the status box""" + label_map = { + OnlineRegistrationStatus.OPEN: "ouvert", + OnlineRegistrationStatus.NOT_ENABLED: "désactivé", + OnlineRegistrationStatus.NOT_STARTED: "à venir", + OnlineRegistrationStatus.ENDED: "clôturé", + OnlineRegistrationStatus.WAITING_LIST_POSSIBLE: "ouvert", + OnlineRegistrationStatus.WAITING_LIST_FULL: "complet", + OnlineRegistrationStatus.IN_PROGRESS: "en cours", + OnlineRegistrationStatus.ENDED_WITH_RESULTS: "résultats", + OnlineRegistrationStatus.CANCELED: "annulé" + } + return label_map.get(self, "") + + def box_class(self) -> str: + """Returns the CSS class for the status box""" + class_map = { + OnlineRegistrationStatus.OPEN: "light-green", + OnlineRegistrationStatus.NOT_ENABLED: "gray", + OnlineRegistrationStatus.NOT_STARTED: "light-green", + OnlineRegistrationStatus.ENDED: "gray", + OnlineRegistrationStatus.WAITING_LIST_POSSIBLE: "light-orange", + OnlineRegistrationStatus.WAITING_LIST_FULL: "light-red", + OnlineRegistrationStatus.IN_PROGRESS: "blue", + OnlineRegistrationStatus.ENDED_WITH_RESULTS: "dark-gray", + OnlineRegistrationStatus.CANCELED: "light-red", + } + return class_map.get(self, "gray") + + def display_box(self) -> bool: + """ + Determines whether this status should display a status box + Returns True if the status should be displayed, False otherwise + """ + # List the statuses that should display a box + display_statuses = [ + OnlineRegistrationStatus.OPEN, + OnlineRegistrationStatus.NOT_STARTED, + OnlineRegistrationStatus.WAITING_LIST_POSSIBLE, + OnlineRegistrationStatus.WAITING_LIST_FULL, + OnlineRegistrationStatus.CANCELED, + # You can add or remove statuses as needed + ] + + return self in display_statuses + class UserOrigin(models.IntegerChoices): ADMIN = 0, 'Admin' SITE = 1, 'Site' diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index e23be85..9d00faa 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -1,5 +1,5 @@ from django.db import models -from . import SideStoreModel, TeamRegistration, PlayerSexType, PlayerDataSource, PlayerPaymentType, FederalCategory +from . import SideStoreModel, TeamRegistration, PlayerSexType, PlayerDataSource, PlayerPaymentType, OnlineRegistrationStatus import uuid from django.utils import timezone @@ -92,3 +92,52 @@ class PlayerRegistration(SideStoreModel): return "1ère" return "1er" return f"{self.rank}ème" + + def get_registration_status(self): + """ + Returns a status object with information about the player's registration status. + This object contains display_box, box_class, and short_label properties + used in the tournament row template. + Returns None if no relevant status can be determined. + """ + # If no team registration exists, return None + if not self.team_registration: + return None + + status = { + 'display_box': True, + 'box_class': 'gray', + 'short_label': 'inscrit' + } + + tournament = self.team_registration.tournament + team = self.team_registration + + # Tournament is ended with results + if tournament.get_online_registration_status() is OnlineRegistrationStatus.ENDED_WITH_RESULTS: + if team.get_final_ranking_component(): + status['box_class'] = 'light-green' + status['short_label'] = team.get_final_ranking_component() + return status + + # Team has walked out + if team.walk_out: + status['box_class'] = 'light-red' + status['short_label'] = 'forfait' + return status + + # Tournament is in progress + if tournament.supposedly_in_progress(): + status['box_class'] = 'light-green' + status['short_label'] = 'en lice' + return status + + # Tournament hasn't started yet + if team.is_in_waiting_list() >= 0: + status['box_class'] = 'light-yellow' + status['short_label'] = "en attente" + else: + status['box_class'] = 'light-green' + status['short_label'] = 'inscrit' + + return status diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index 5d468d1..ebb2e08 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -240,10 +240,16 @@ class TeamRegistration(SideStoreModel): def get_final_ranking(self): + get_final_ranking_component = self.get_final_ranking_component() + if get_final_ranking_component: + return get_final_ranking_component + self.ranking_delta() + return None + + def get_final_ranking_component(self): if self.final_ranking: if self.final_ranking == 1: - return "1er" + self.ranking_delta() - return f"{self.final_ranking}ème" + self.ranking_delta() + return "1er" + return f"{self.final_ranking}ème" return None def ranking_delta(self): diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index c1a4e15..b117f62 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -1,4 +1,3 @@ -from time import daylight from zoneinfo import ZoneInfo from django.db import models from typing import TYPE_CHECKING @@ -8,12 +7,13 @@ if TYPE_CHECKING: from . import BaseModel, Event, TournamentPayment, FederalMatchCategory, FederalCategory, FederalLevelCategory, FederalAgeCategory, OnlineRegistrationStatus import uuid from django.utils import timezone, formats -from datetime import datetime, timedelta -from zoneinfo import ZoneInfo +from datetime import datetime, timedelta, time from tournaments.utils.player_search import get_player_name_from_csv from shared.cryptography import encryption_util from ..utils.extensions import plural_format +from django.utils.formats import date_format +from ..utils.licence_validator import LicenseValidator class TeamSortingType(models.IntegerChoices): RANK = 1, 'Rank' @@ -231,37 +231,29 @@ class Tournament(BaseModel): else: return None - def tournament_status_display(self): - if self.is_canceled() is True: - return "Annulé" - - teams = self.teams(True) - if self.supposedly_in_progress() or self.end_date is not None or self.should_be_over(): - teams = [t for t in teams if t.stage != "Attente"] - if teams is not None and len(teams) > 0: - word = "équipe" - if len(teams) > 1: - word = word + "s" - return f"{len(teams)} {word}" - else: - return None + def get_tournament_status(self): + return self.get_online_registration_status().status_localized() - registration_status = None - if self.enable_online_registration == True: - registration_status = self.get_online_registration_status().status_localized() + def tournament_status_display(self): + teams = self.teams(True) + if self.supposedly_in_progress() or self.end_date is not None or self.should_be_over(): + teams = [t for t in teams if t.stage != "Attente"] if teams is not None and len(teams) > 0: - word = "inscription" + word = "équipe" if len(teams) > 1: word = word + "s" - if registration_status is not None: - return f"{registration_status}\n{len(teams)} {word}" - else: - return f"{len(teams)} {word}" + return f"{len(teams)} {word}" else: - if registration_status is not None: - return f"{registration_status}" return None + if teams is not None and len(teams) > 0: + word = "inscription" + if len(teams) > 1: + word = word + "s" + return f"{len(teams)} {word}" + else: + return None + def name_and_event(self): event_name = None if self.event: @@ -332,9 +324,9 @@ class Tournament(BaseModel): index = i # Check if team_count exists - if self.team_count: + if self.team_count_limit == True: # Team is not in list - if index < self.team_count: + if index < 0: print("Team is not in list", index, self.team_count) return -1 # Return position in waiting list relative to target count @@ -1133,39 +1125,56 @@ class Tournament(BaseModel): return "La sélection se fait par date d'inscription" def get_online_registration_status(self): - if self.supposedly_in_progress(): - return OnlineRegistrationStatus.ENDED - if self.closed_registration_date is not None: - return OnlineRegistrationStatus.WAITING_LIST_POSSIBLE - if self.end_date is not None: - return OnlineRegistrationStatus.ENDED_WITH_RESULTS + if self.is_canceled(): + return OnlineRegistrationStatus.CANCELED + if self.end_date is not None: + return OnlineRegistrationStatus.ENDED_WITH_RESULTS + if self.supposedly_in_progress(): + return OnlineRegistrationStatus.ENDED + if self.closed_registration_date is not None: + return OnlineRegistrationStatus.WAITING_LIST_POSSIBLE - now = timezone.now() + now = timezone.now() + + if self.opening_registration_date is not None: + timezoned_datetime = timezone.localtime(self.opening_registration_date) + if now < timezoned_datetime: + return OnlineRegistrationStatus.NOT_STARTED - if self.opening_registration_date is not None: - timezoned_datetime = timezone.localtime(self.opening_registration_date) - if now < timezoned_datetime: - return OnlineRegistrationStatus.NOT_STARTED - - if self.registration_date_limit is not None: - timezoned_datetime = timezone.localtime(self.registration_date_limit) - if now > timezoned_datetime: - return OnlineRegistrationStatus.WAITING_LIST_POSSIBLE - - if self.team_sorting == TeamSortingType.RANK: - return OnlineRegistrationStatus.OPEN - - if self.team_count_limit is True: - # Get all team registrations excluding walk_outs - current_team_count = self.team_registrations.exclude(walk_out=True).count() - if current_team_count >= self.team_count: - if self.waiting_list_limit is not None: - waiting_list_count = current_team_count - self.team_count - if waiting_list_count >= self.waiting_list_limit: - return OnlineRegistrationStatus.WAITING_LIST_FULL - return OnlineRegistrationStatus.WAITING_LIST_POSSIBLE + if self.registration_date_limit is not None: + timezoned_datetime = timezone.localtime(self.registration_date_limit) + if now > timezoned_datetime: + return OnlineRegistrationStatus.WAITING_LIST_POSSIBLE + + if self.team_sorting == TeamSortingType.RANK: return OnlineRegistrationStatus.OPEN + if self.team_count_limit is True: + # Get all team registrations excluding walk_outs + current_team_count = self.team_registrations.exclude(walk_out=True).count() + if current_team_count >= self.team_count: + if self.waiting_list_limit is not None: + waiting_list_count = current_team_count - self.team_count + if waiting_list_count >= self.waiting_list_limit: + return OnlineRegistrationStatus.WAITING_LIST_FULL + return OnlineRegistrationStatus.WAITING_LIST_POSSIBLE + return OnlineRegistrationStatus.OPEN + + def get_registration_status_short_label(self): + """Returns a short label for the registration status""" + status = self.get_online_registration_status() + return status.short_label() + + def get_registration_status_class(self): + """Returns the CSS class for the registration status box""" + status = self.get_online_registration_status() + return status.box_class() + + def should_display_status_box(self): + """Returns whether the registration status box should be displayed""" + status = self.get_online_registration_status() + return status.display_box() + def is_unregistration_possible(self): # Check if tournament has started if self.supposedly_in_progress(): @@ -1490,6 +1499,87 @@ class Tournament(BaseModel): return self.event.creator.phone + + @property + def week_day(self): + """Return the weekday name (e.g., 'Monday')""" + date = self.local_start_date() + return date_format(date, format='D') + '.' # 'l' gives full weekday name + + @property + def day(self): + """Return the day of the month""" + date = self.local_start_date() + return date.day + + @property + def month(self): + """ + Return the month name in lowercase: + - If full month name is 4 letters or fewer, return as is + - If more than 4 letters, return first 4 letters with a dot + """ + date = self.local_start_date() + # Get full month name and convert to lowercase + full_month = date_format(date, format='F').lower() + + # Check if the month name is 5 letters or fewer + if len(full_month) <= 5: + return full_month + else: + # Truncate to 5 letters and add a dot + return f"{full_month[:5]}." + + @property + def year(self): + """Return the year""" + date = self.local_start_date() + return date.year + + @property + def localized_day_duration(self): + """ + Return localized day duration in French: + - If multiple days: '2 jours', '3 jours', etc. + - If 1 day and starts after 18:00: 'soirée' + - If 1 day and starts before 18:00: 'journée' + """ + # Assuming day_duration is a property or field that returns the number of days + days = self.day_duration + + if days > 1: + return f"{days} jours" + else: + # For single day events, check the starting hour + start_time = self.local_start_date().time() + evening_threshold = time(18, 0) # 18:00 (6 PM) + + if start_time >= evening_threshold: + return "soirée" + else: + return "journée" + + def get_player_registration_status_by_licence(self, user): + from . import PlayerRegistration + licence_id = user.licence_id + if not licence_id: + return None + + validator = LicenseValidator(licence_id) + stripped_license = validator.stripped_license + + # Check if there is a PlayerRegistration for this user in this tournament + user_player = PlayerRegistration.objects.filter( + licence_id__icontains=stripped_license, + team_registration__tournament=self, + ).first() + + if user_player: + return user_player.get_registration_status() + else: + return None + + class MatchGroup: def __init__(self, name, matches, formatted_schedule, round_id=None): self.name = name diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index 82b4f40..9409d48 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -35,7 +35,7 @@ body { } label { - color: #707070; + color: #505050; font-size: 1.1em; } @@ -55,7 +55,7 @@ footer { } a { - color: #707070; + color: #505050; } a:hover { @@ -73,7 +73,7 @@ nav { } nav a { - color: #707070; + color: #505050; padding: 8px 12px; background-color: #fae7ce; border-radius: 12px; @@ -161,7 +161,7 @@ tr { .rounded-button { background-color: #fae7ce; /* Green background */ - color: #707070; /* White text */ + color: #505050; /* White text */ padding: 15px 32px; /* Some padding */ font-size: 1em; font-weight: 800; @@ -193,7 +193,7 @@ tr { } .mybox { - color: #707070; + color: #505050; padding: 8px 12px; background-color: #fae7ce; border-radius: 12px; @@ -260,6 +260,11 @@ tr { font-size: 1.2em; } +.very-large { + font-family: "Montserrat-SemiBold"; + font-size: 1.4em; +} + @media screen and (max-width: 40em) { .large { font-size: 0.9em; @@ -278,7 +283,7 @@ tr { .info { font-family: "Montserrat-SemiBold"; font-size: 0.9em; - color: #707070; + color: #505050; } .small { @@ -286,7 +291,7 @@ tr { } .minor-info { - color: #707070; + color: #505050; font-size: 0.85em; } @@ -362,7 +367,7 @@ tr { .separator { height: 1px; - background-color: #707070; + background-color: #505050; margin: 5px 0px; } @@ -608,12 +613,126 @@ h-margin { padding: 5px 0px; } -.table-row-4-colums-tournament { +.table-row-5-colums-tournament { display: grid; - grid-template-columns: auto 1fr auto auto; + grid-template-columns: 75px 90px 1fr 120px; align-items: center; - /* Vertically center the content within each column */ - padding: 5px 0px; + gap: 4px; +} + +.very-large.club-name { + font-size: 1.2em; +} + +.table-row-5-colums-tournament.header { + grid-template-columns: 1fr auto; /* Override to just 2 columns for header */ + justify-content: space-between; +} + +@media screen and (max-width: 800px) { + /* Adjust breakpoint as needed */ + .table-row-5-colums-tournament { + grid-template-columns: 60px 70px 1fr 80px; + gap: 2px; + } + + .small { + font-size: 1em; + } + + .very-large { + font-size: 1.4em; + } + + .very-large.club-name { + font-size: 1.2em; + } +} + +@media screen and (max-width: 400px) { + /* Adjust breakpoint as needed */ + .table-row-5-colums-tournament { + grid-template-columns: 55px 65px 1fr 75px; + gap: 2px; + } + + .small { + font-size: 0.9em; + } + + .very-large { + font-size: 1.3em; + } + + .very-large.club-name { + font-size: 1em; + } +} + +.light-green { + background-color: #90ee90 !important; +} + +.light-yellow { + background-color: #fed300 !important; +} + +.light-orange { + color: white !important; + background-color: #f39200 !important; +} + +.light-red { + background-color: #e84039 !important; + color: white !important; +} + +.table-row-element { + width: 100%; + line-height: 1.2; + padding: 8px 8px; + align-items: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; /* Prevents text from wrapping to a new line */ + max-width: 100%; /* Ensures children don't overflow */ +} + +.table-row-element.tournament-date { + grid-column: 1; + color: #505050; + background-color: #fae7ce; + border-radius: 12px; +} + +.table-row-element.tournament-type { + grid-column: 2; +} + +.table-row-element.tournament-name { + grid-column: 3; + align-self: center; /* Align in grid cell vertically */ + margin: auto 0; /* Alternative vertical centering */ +} + +.very-large.club-name { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; /* Limit to 2 lines */ + -webkit-box-orient: vertical; + white-space: normal; + /* Keep any existing styling for .large */ +} + +.table-row-element.tournament-status { + grid-column: 4; +} + +.box { + color: #505050; + border-radius: 12px; + padding: 4px; } .table-row-6-colums-club-tournament { @@ -867,7 +986,7 @@ h-margin { .match-result a:hover { background-color: #fae7ce; - color: #707070; + color: #505050; } .group-stage-link { diff --git a/tournaments/static/tournaments/css/tournament_bracket.css b/tournaments/static/tournaments/css/tournament_bracket.css index ed1a1a4..5593b51 100644 --- a/tournaments/static/tournaments/css/tournament_bracket.css +++ b/tournaments/static/tournaments/css/tournament_bracket.css @@ -58,7 +58,7 @@ } .round-name { - color: #707070; + color: #505050; font-size: 1.5em; padding: 8px 12px; white-space: nowrap; /* Prevent text wrapping */ @@ -67,7 +67,7 @@ .round-format { font-size: 0.9em; - color: #707070; + color: #505050; margin-top: -5px; /* Reduced from -10px to bring it closer */ white-space: nowrap; /* Prevent text wrapping */ display: block; /* Ensure proper centering */ @@ -199,7 +199,7 @@ .broadcast-mode .round-name, .broadcast-mode .round-format { padding: 0px; - color: #707070; + color: #505050; } .broadcast-mode .round-title { @@ -215,7 +215,7 @@ .outgoing-line, .outgoing-line-upward, .outgoing-line-downward { - background-color: #707070 !important; /* Bright yellow - change to your preferred color */ + background-color: #505050 !important; /* Bright yellow - change to your preferred color */ } /* Broadcast mode styling for all lines */ diff --git a/tournaments/templates/registration/my_tournaments.html b/tournaments/templates/registration/my_tournaments.html index 555b8e2..631fcf1 100644 --- a/tournaments/templates/registration/my_tournaments.html +++ b/tournaments/templates/registration/my_tournaments.html @@ -11,8 +11,8 @@ {% load tz %}
-
-
+
+
{% if upcoming_tournaments %} {% for tournament in upcoming_tournaments %} @@ -23,8 +23,8 @@ {% endif %}
-
-
+
+
{% if running_tournaments %} {% for tournament in running_tournaments %} @@ -36,8 +36,8 @@
-
-
+
+
{% if ended_tournaments %} {% for tournament in ended_tournaments %} diff --git a/tournaments/templates/tournaments/tournament_row.html b/tournaments/templates/tournaments/tournament_row.html index 199c25f..e9eba85 100644 --- a/tournaments/templates/tournaments/tournament_row.html +++ b/tournaments/templates/tournaments/tournament_row.html @@ -1,43 +1,56 @@ - -
-
-
{{ tournament.level }}
-
{{ tournament.category }}
- {% if tournament.age %} -
{{ tournament.age }}
+{% load tournament_tags %} +{% with status=tournament.get_online_registration_status %} +
+ + + {% if not forloop.last %} +
+ {% endif %} +{% endwith %} diff --git a/tournaments/templates/tournaments/tournaments.html b/tournaments/templates/tournaments/tournaments.html index d55463b..96240ab 100644 --- a/tournaments/templates/tournaments/tournaments.html +++ b/tournaments/templates/tournaments/tournaments.html @@ -15,48 +15,51 @@
{% if live or future %} -
+
-
+
{% if live %} - +
+ + {% if live|length >= 10 %} +
+ {% if club %} + tout voir + {% else %} + tout voir + {% endif %} +
+ {% endif %} +
+ {% for tournament in live %} {% include 'tournaments/tournament_row.html' %} {% endfor %} - {% if live|length >= 10 %} -
- {% if club %} - Voir tous... - {% else %} - Voir tous... - {% endif %} -
- {% endif %} - {% endif %} {% if future %} - +
+ + {% if future|length >= 10 %} +
+ {% if club %} + tout voir + {% else %} + tout voir + {% endif %} +
+ {% endif %} +
{% for tournament in future %} {% include 'tournaments/tournament_row.html' %} {% endfor %} - {% if future|length >= 10 %} -
- {% if club %} - Voir tous... - {% else %} - Voir tous... - {% endif %} -
- {% endif %} - {% endif %}
@@ -65,25 +68,25 @@ {% endif %} {% if ended %} -
-
- - - +
+
+ +
+ + {% if ended|length >= 10 %} +
+ {% if club %} + tout voir + {% else %} + tout voir + {% endif %} +
+ {% endif %} +
{% for tournament in ended %} {% include 'tournaments/tournament_row.html' %} {% endfor %} - {% if ended|length >= 10 %} -
- {% if club %} - Voir tous... - {% else %} - Voir tous... - {% endif %} -
- {% endif %} -
{% endif %} diff --git a/tournaments/templates/tournaments/tournaments_list.html b/tournaments/templates/tournaments/tournaments_list.html index 1e0d7d5..8258f5c 100644 --- a/tournaments/templates/tournaments/tournaments_list.html +++ b/tournaments/templates/tournaments/tournaments_list.html @@ -11,9 +11,9 @@
{% if tournaments %} -
+
-
+
{% for tournament in tournaments %} {% include 'tournaments/tournament_row.html' %} diff --git a/tournaments/templatetags/__init__.py b/tournaments/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tournaments/templatetags/tournament_tags.py b/tournaments/templatetags/tournament_tags.py new file mode 100644 index 0000000..072fd07 --- /dev/null +++ b/tournaments/templatetags/tournament_tags.py @@ -0,0 +1,7 @@ +from django import template + +register = template.Library() + +@register.filter +def get_player_status(tournament, user): + return tournament.get_player_registration_status_by_licence(user)