adds month/year filtering for tournament lists

apikeys
Laurent 3 months ago
parent 7fa16f23c4
commit 966beb6599
  1. 4
      tournaments/static/tournaments/css/basics.css
  2. 1
      tournaments/static/tournaments/css/style.css
  3. 49
      tournaments/templates/tournaments/tournaments_list.html
  4. 16
      tournaments/templatetags/tournament_tags.py
  5. 191
      tournaments/views.py

@ -60,6 +60,10 @@
margin: 20px 0px; margin: 20px 0px;
} }
.bmargin10 {
margin-bottom: 10px;
}
/* WIDTH PERCENTAGE */ /* WIDTH PERCENTAGE */
.w15 { .w15 {

@ -1015,7 +1015,6 @@ h-margin {
color: white; /* White text on hover */ color: white; /* White text on hover */
} }
.download-button { .download-button {
margin-right: 6px;
color: #fff7ed; color: #fff7ed;
padding: 8px 12px; padding: 8px 12px;
background-color: #1a223a; background-color: #1a223a;

@ -1,4 +1,5 @@
{% extends 'tournaments/base.html' %} {% extends 'tournaments/base.html' %}
{% load tournament_tags %}
{% block head_title %}{{ head_title }}{% endblock %} {% block head_title %}{{ head_title }}{% endblock %}
{% block first_title %}{{ first_title }}{% endblock %} {% block first_title %}{{ first_title }}{% endblock %}
@ -7,6 +8,42 @@
{% block content %} {% block content %}
{% include 'tournaments/navigation_base.html' %} {% include 'tournaments/navigation_base.html' %}
{% if filter == 0 or filter == 2 %}
<!-- Year Navigation -->
{% if available_years %}
<div class="topmargin40">
<nav class="margin10">
{% for year in available_years %}
{% if year == selected_year %}
<a href="?filter={{ filter }}{% if club %}&club={{ club }}{% endif %}&year={{ year }}" class="download-button">{{ year }}</a>
{% else %}
<a href="?filter={{ filter }}{% if club %}&club={{ club }}{% endif %}&year={{ year }}" class="orange">{{ year }}</a>
{% endif %}
{% endfor %}
{% if year %}
<a href="?filter={{ filter }}{% if club %}&club={{ club }}{% endif %}" class="orange">Tous</a>
{% endif %}
</nav>
{% endif %}
<!-- Month Navigation -->
{% if available_months and selected_year %}
<nav class="margin10">
{% for month_num in available_months %}
{% if month_num == selected_month %}
<a href="?filter={{ filter }}{% if club %}&club={{ club }}{% endif %}&year={{ selected_year }}&month={{ month_num }}" class="download-button">{{ month_names|array_lookup:month_num }}</a>
{% else %}
<a href="?filter={{ filter }}{% if club %}&club={{ club }}{% endif %}&year={{ selected_year }}&month={{ month_num }}" class="orange">{{ month_names|array_lookup:month_num }}</a>
{% endif %}
{% endfor %}
</nav>
</div>
{% endif %}
{% endif %}
<div class="grid-x"> <div class="grid-x">
{% if tournaments %} {% if tournaments %}
<div class="cell medium-12 large-6 topblock padding10"> <div class="cell medium-12 large-6 topblock padding10">
@ -18,6 +55,18 @@
</div> </div>
</div> </div>
{% elif selected_month and selected_year %}
<div class="cell medium-12 large-6 topblock padding10">
<div class="bubble">
<p>Aucun tournoi trouvé pour {{ month_names|array_lookup:selected_month }} {{ selected_year }}.</p>
</div>
</div>
{% elif selected_year %}
<div class="cell medium-12 large-6 topblock padding10">
<div class="bubble">
<p>Aucun tournoi trouvé pour l'année {{ selected_year }}.</p>
</div>
</div>
{% endif %} {% endif %}
{% if first_tournament_prog_url and tournaments %} {% if first_tournament_prog_url and tournaments %}

@ -5,3 +5,19 @@ register = template.Library()
@register.filter @register.filter
def get_player_status(tournament, user): def get_player_status(tournament, user):
return tournament.get_player_registration_status_by_licence(user) return tournament.get_player_registration_status_by_licence(user)
@register.filter
def lookup(dictionary, key):
"""Template filter to lookup dictionary values by key"""
return dictionary.get(key, '')
@register.filter
def array_lookup(array, index):
"""Template filter to lookup array values by index"""
try:
index = int(index)
if 0 <= index < len(array):
return array[index]
return ''
except (ValueError, TypeError, IndexError):
return ''

@ -1,58 +1,53 @@
# Standard library imports # Standard library imports
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import login, get_user_model, logout, update_session_auth_hash
from django.contrib.auth.views import PasswordResetCompleteView, PasswordResetConfirmView
from django.contrib.sites.shortcuts import get_current_site
from django.contrib.auth.decorators import login_required
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import redirect, render, get_object_or_404
from django.utils import timezone, formats
from django.utils.encoding import force_str, force_bytes
from django.utils.http import urlsafe_base64_decode
from django.http import JsonResponse, HttpResponse, Http404
from django.urls import reverse, reverse_lazy
from django.conf import settings
from django.db.models import Q
from django.views.generic import View
from django.views.generic.edit import UpdateView
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
import os import os
import csv import csv
import locale
import calendar
from api.serializers import GroupStageSerializer, MatchSerializer, PlayerRegistrationSerializer, TeamRegistrationSerializer, TeamScoreSerializer from api.serializers import GroupStageSerializer, MatchSerializer, PlayerRegistrationSerializer, TeamRegistrationSerializer, TeamScoreSerializer
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import logout
from .utils.extensions import is_not_sqlite_backend from .utils.extensions import is_not_sqlite_backend
import stripe import stripe
from django.contrib.auth import update_session_auth_hash from .models import Club, Tournament, CustomUser, Event, Round, Match, TeamScore, TeamRegistration, PlayerRegistration, UserOrigin
from django.contrib.auth.views import PasswordResetCompleteView
from django.shortcuts import redirect
from django.contrib.auth import login
from django.contrib.auth import get_user_model
from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse, HttpResponse
from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_decode
from django.urls import reverse
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.views.generic import View
from django.db.models import Q
from .models import Club, Tournament, CustomUser, Event, Round, Match, TeamScore, TeamRegistration, PlayerRegistration, UserOrigin, Purchase
from datetime import datetime, timedelta from datetime import datetime, timedelta
import time import time
import json import json
import asyncio import asyncio
import zipfile import zipfile
import pandas as pd import pandas as pd
from tournaments.utils.extensions import create_random_filename
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.utils import formats
from tournaments.utils.extensions import create_random_filename
from api.tokens import account_activation_token from api.tokens import account_activation_token
# Third-party imports
from qr_code.qrcode.utils import QRCodeOptions from qr_code.qrcode.utils import QRCodeOptions
# Django imports
from django.http import Http404
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.template.loader import render_to_string
from django.contrib import messages
from django.contrib.sites.shortcuts import get_current_site
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import PasswordResetConfirmView
from django.core.mail import EmailMessage
from django.views.decorators.csrf import csrf_protect
from .services.tournament_unregistration import TournamentUnregistrationService from .services.tournament_unregistration import TournamentUnregistrationService
from django.core.exceptions import ValidationError
from .forms import ( from .forms import (
ProfileUpdateForm, ProfileUpdateForm,
SimpleCustomUserCreationForm, SimpleCustomUserCreationForm,
@ -62,12 +57,11 @@ from .forms import (
) )
from .utils.apns import send_push_notification from .utils.apns import send_push_notification
from .utils.licence_validator import LicenseValidator from .utils.licence_validator import LicenseValidator
from django.views.generic.edit import UpdateView
from .forms import CustomPasswordChangeForm from .forms import CustomPasswordChangeForm
from .services.email_service import TournamentEmailService from .services.email_service import TournamentEmailService
from .services.tournament_registration import RegistrationCartManager from .services.tournament_registration import RegistrationCartManager
from .services.payment_service import PaymentService from .services.payment_service import PaymentService
from django.views.decorators.csrf import csrf_exempt
import logging import logging
@ -171,6 +165,60 @@ def future_tournaments(club_id, limit=None):
tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, limit) tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, limit)
return [t for t in tournaments if t.display_tournament() and t.starts_in_the_future()] return [t for t in tournaments if t.display_tournament() and t.starts_in_the_future()]
def future_tournaments_by_month(club_id, year, month):
"""Get future tournaments filtered by year and month"""
logger.info(f'filter {year} {month}')
tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True)
filtered_tournaments = []
for t in tournaments:
if (t.display_tournament() and t.starts_in_the_future() and
t.start_date.year == year and t.start_date.month == month + 1):
filtered_tournaments.append(t)
return filtered_tournaments
def finished_tournaments_by_month(club_id, year, month):
"""Get finished tournaments filtered by year and month"""
clean_ended_tournaments = tournaments_query(Q(end_date__isnull=False), club_id, False)
clean_ended_tournaments = [t for t in clean_ended_tournaments if t.display_tournament()]
one_day_ago = timezone.now() - timedelta(days=1)
ended_tournaments = tournaments_query(
Q(end_date__isnull=True, start_date__lt=one_day_ago),
club_id,
False
)
ended_tournaments = [t for t in ended_tournaments if t.display_tournament() and t.should_be_over()]
# Combine both lists
all_tournaments = clean_ended_tournaments + ended_tournaments
# Filter by year and month
filtered_tournaments = []
for t in all_tournaments:
if t.start_date.year == year and t.start_date.month == month:
filtered_tournaments.append(t)
# Sort by start_date descending
filtered_tournaments.sort(key=lambda t: t.start_date, reverse=True)
return filtered_tournaments
def get_available_years_for_tournaments(club_id, filter_type):
"""Get available years for tournaments based on filter type"""
if filter_type == 0: # Future tournaments
tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True)
tournaments = [t for t in tournaments if t.display_tournament() and t.starts_in_the_future()]
elif filter_type == 2: # Finished tournaments
tournaments = finished_tournaments(club_id)
else:
return []
years = set()
for t in tournaments:
years.add(t.start_date.year)
return sorted(years, reverse=(filter_type == 2))
def tournament_info(request, tournament_id): def tournament_info(request, tournament_id):
tournament = get_object_or_404(Tournament, pk=tournament_id) tournament = get_object_or_404(Tournament, pk=tournament_id)
registered_user = None registered_user = None
@ -217,18 +265,45 @@ def tournaments(request):
return redirect('/') return redirect('/')
club_id = request.GET.get('club') club_id = request.GET.get('club')
year_param = request.GET.get('year')
month_param = request.GET.get('month')
title = '' title = ''
tournaments = [] tournaments = []
if filter==0: available_years = []
selected_year = None
if month_param:
selected_month = int(month_param)
else:
selected_month = timezone.now().month - 1
if filter == 0:
title = 'À venir' title = 'À venir'
tournaments = future_tournaments(club_id) available_years, selected_year = handle_year_month_filtering(year_param, club_id, filter, is_future=True)
elif filter==1:
if selected_year and selected_month:
tournaments = future_tournaments_by_month(club_id, selected_year, selected_month)
elif selected_year:
tournaments = future_tournaments(club_id)
else:
tournaments = future_tournaments(club_id)
elif filter == 1:
title = 'En cours' title = 'En cours'
tournaments = live_tournaments(club_id) tournaments = live_tournaments(club_id)
elif filter==2:
elif filter == 2:
title = 'Terminés' title = 'Terminés'
tournaments = finished_tournaments(club_id) available_years, selected_year = handle_year_month_filtering(year_param, club_id, filter, is_future=False)
if selected_year and selected_month:
tournaments = finished_tournaments_by_month(club_id, selected_year, selected_month)
elif selected_year:
tournaments = finished_tournaments(club_id)
else:
tournaments = finished_tournaments(club_id)
locale.setlocale(locale.LC_TIME, 'fr_FR.UTF-8') # Define month names in french
return render( return render(
request, request,
@ -239,9 +314,39 @@ def tournaments(request):
'second_title': title, 'second_title': title,
'head_title': title, 'head_title': title,
'club': club_id, 'club': club_id,
'filter': filter,
'available_years': available_years,
'available_months': range(0, 12),
'selected_year': selected_year,
'selected_month': selected_month,
'month_names': list(calendar.month_name)[1:],
'year': year_param,
'month': month_param,
} }
) )
def handle_year_month_filtering(year_param, club_id, filter_type, is_future=True):
"""Helper function to handle year/month filtering for future and finished tournaments"""
available_years = get_available_years_for_tournaments(club_id, filter_type)
selected_year = None
# Handle year selection
current_year = timezone.now().year
if not year_param and available_years:
if is_future:
selected_year = current_year if current_year in available_years else available_years[0]
else:
selected_year = available_years[0] # Most recent year for finished tournaments
elif year_param:
try:
selected_year = int(year_param)
if selected_year not in available_years:
selected_year = available_years[0] if available_years else None
except:
selected_year = available_years[0] if available_years else None
return available_years, selected_year
def clubs(request): def clubs(request):
all_clubs = Club.objects.all().order_by('name') all_clubs = Club.objects.all().order_by('name')
clubs = [] clubs = []

Loading…
Cancel
Save