You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1539 lines
60 KiB
1539 lines
60 KiB
# Standard library imports
|
|
import os
|
|
import csv
|
|
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
|
|
import stripe
|
|
|
|
from django.contrib.auth import update_session_auth_hash
|
|
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
|
|
from datetime import datetime, timedelta
|
|
import time
|
|
import json
|
|
import asyncio
|
|
import zipfile
|
|
|
|
from api.tokens import account_activation_token
|
|
|
|
# Third-party imports
|
|
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 django.core.exceptions import ValidationError
|
|
from .forms import (
|
|
ProfileUpdateForm,
|
|
SimpleCustomUserCreationForm,
|
|
SimpleForm,
|
|
TournamentRegistrationForm,
|
|
AddPlayerForm
|
|
)
|
|
from .utils.apns import send_push_notification
|
|
from .utils.licence_validator import LicenseValidator
|
|
from django.views.generic.edit import UpdateView
|
|
from .forms import CustomPasswordChangeForm
|
|
from .services.email_service import TournamentEmailService
|
|
from .services.tournament_registration import RegistrationCartManager
|
|
from .services.payment_service import PaymentService
|
|
|
|
def index(request):
|
|
now = timezone.now()
|
|
thirty_days_ago = now - timedelta(days=30)
|
|
thirty_days_future = now + timedelta(days=30)
|
|
|
|
club_id = request.GET.get('club')
|
|
if club_id:
|
|
tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, 50)
|
|
else:
|
|
tournaments = tournaments_query(Q(end_date__isnull=True, start_date__gte=thirty_days_ago, start_date__lte=thirty_days_future), club_id, True, 50)
|
|
|
|
display_tournament = [t for t in tournaments if t.display_tournament()]
|
|
live = []
|
|
future = []
|
|
for t in display_tournament:
|
|
if t.supposedly_in_progress():
|
|
live.append(t)
|
|
elif t.starts_in_the_future():
|
|
future.append(t)
|
|
clean_ended_tournaments = tournaments_query(Q(end_date__isnull=False), club_id, False, 50)
|
|
clean_ended_tournaments = [t for t in clean_ended_tournaments if t.display_tournament()]
|
|
ended_tournaments = [t for t in display_tournament if t.should_be_over()]
|
|
|
|
# Combine both lists
|
|
finished = clean_ended_tournaments + ended_tournaments
|
|
|
|
# Sort the combined list by start_date in descending order
|
|
finished.sort(key=lambda t: t.start_date, reverse=True)
|
|
|
|
club = None
|
|
if club_id:
|
|
club = get_object_or_404(Club, pk=club_id)
|
|
|
|
return render(
|
|
request,
|
|
"tournaments/tournaments.html",
|
|
{
|
|
'future': future[:10],
|
|
'live': live[:10],
|
|
'ended': finished[:10],
|
|
'club': club,
|
|
}
|
|
)
|
|
|
|
def tournaments_query(query, club_id, ascending, limit=None):
|
|
queries = [query, Q(is_private=False, is_deleted=False, event__club__isnull=False)]
|
|
|
|
club = None
|
|
if club_id:
|
|
club = get_object_or_404(Club, pk=club_id)
|
|
q_club = Q(event__club=club)
|
|
queries.append(q_club)
|
|
|
|
sortkey = 'start_date'
|
|
if not ascending:
|
|
sortkey = '-start_date'
|
|
|
|
queryset = Tournament.objects.filter(*queries).prefetch_related(
|
|
'group_stages',
|
|
'rounds',
|
|
'team_registrations',
|
|
).order_by(sortkey)
|
|
|
|
# Apply limit directly in the database query
|
|
if limit is not None and isinstance(limit, int) and limit > 0:
|
|
queryset = queryset[:limit]
|
|
|
|
return queryset
|
|
|
|
def finished_tournaments(club_id, limit=None):
|
|
clean_ended_tournaments = tournaments_query(Q(end_date__isnull=False), club_id, False, limit)
|
|
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,
|
|
limit
|
|
)
|
|
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
|
|
|
|
# Sort the combined list by start_date in descending order
|
|
all_tournaments.sort(key=lambda t: t.start_date, reverse=True)
|
|
|
|
return all_tournaments
|
|
|
|
def live_tournaments(club_id, limit=None):
|
|
tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, limit)
|
|
return [t for t in tournaments if t.display_tournament() and t.supposedly_in_progress()]
|
|
|
|
def future_tournaments(club_id, limit=None):
|
|
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()]
|
|
|
|
def tournament_info(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
registered_user = None
|
|
team_registration = None
|
|
is_captain = False
|
|
player_register_check = None
|
|
|
|
if request.user.is_authenticated:
|
|
# Assuming user's licence_id is stored in the user profile (e.g., request.user.licence_id)
|
|
user_licence_id = request.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:
|
|
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 registered_user:
|
|
is_captain = registered_user.captain
|
|
team_registration = registered_user.team_registration
|
|
|
|
return render(request, 'tournaments/tournament_info.html', {
|
|
'tournament': tournament,
|
|
'team': team_registration,
|
|
'is_captain': is_captain,
|
|
'player_register_check': player_register_check
|
|
})
|
|
|
|
|
|
def tournaments(request):
|
|
filter_param = request.GET.get('filter')
|
|
filter = None
|
|
|
|
if filter_param:
|
|
try:
|
|
filter = int(filter_param)
|
|
if filter not in [0, 1, 2]: # Valid filter values
|
|
return redirect('/')
|
|
|
|
except:
|
|
return redirect('/')
|
|
|
|
club_id = request.GET.get('club')
|
|
|
|
title = ''
|
|
tournaments = []
|
|
if filter==0:
|
|
title = 'À venir'
|
|
tournaments = future_tournaments(club_id)
|
|
elif filter==1:
|
|
title = 'En cours'
|
|
tournaments = live_tournaments(club_id)
|
|
elif filter==2:
|
|
title = 'Terminés'
|
|
tournaments = finished_tournaments(club_id)
|
|
|
|
return render(
|
|
request,
|
|
"tournaments/tournaments_list.html",
|
|
{
|
|
'tournaments': tournaments,
|
|
'title': title,
|
|
'club': club_id,
|
|
}
|
|
)
|
|
|
|
def clubs(request):
|
|
all_clubs = Club.objects.all().order_by('name')
|
|
clubs = []
|
|
for club in all_clubs:
|
|
if club.events_count() > 0:
|
|
clubs.append(club)
|
|
|
|
return render(request, 'tournaments/clubs.html', {
|
|
'clubs': clubs,
|
|
})
|
|
|
|
def club(request, club_id):
|
|
club = get_object_or_404(Club, pk=club_id)
|
|
return render(request, 'tournaments/summons.html', {
|
|
'tournament': tournament,
|
|
'team_summons': team_summons,
|
|
})
|
|
|
|
def tournament(request, tournament_id):
|
|
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
|
|
round_id = request.GET.get('round')
|
|
group_stage_id = request.GET.get('group_stage')
|
|
match_groups = tournament.match_groups(False, group_stage_id, round_id)
|
|
|
|
bracket_rounds = tournament.rounds.filter(parent=None, group_stage_loser_bracket=False).order_by('-index')
|
|
rounds = list(tournament.rounds.filter(group_stage_loser_bracket=True))
|
|
rounds.extend(bracket_rounds)
|
|
|
|
group_stages = sorted(tournament.sorted_group_stages(), key=lambda s: (s.step, s.index))
|
|
|
|
if tournament.display_matches() or tournament.display_group_stages():
|
|
return render(request, 'tournaments/matches.html', {
|
|
'tournament': tournament,
|
|
'rounds': rounds,
|
|
'group_stages': group_stages,
|
|
'match_groups': match_groups,
|
|
})
|
|
else:
|
|
return tournament_info(request, tournament_id)
|
|
|
|
def tournament_teams(request, tournament_id):
|
|
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
teams = tournament.teams(True)
|
|
selected_teams = [team for team in teams if team.stage != "Attente"]
|
|
waiting_teams = [team for team in teams if team.stage == "Attente"]
|
|
|
|
return render(request, 'tournaments/teams.html', {
|
|
'tournament': tournament,
|
|
'selected_teams': selected_teams,
|
|
'waiting_teams': waiting_teams,
|
|
})
|
|
|
|
def tournament_summons(request, tournament_id):
|
|
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
team_summons = tournament.team_summons()
|
|
|
|
return render(request, 'tournaments/summons.html', {
|
|
'tournament': tournament,
|
|
'team_summons': team_summons,
|
|
})
|
|
|
|
def tournament_broadcasted_summons(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_summons.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def tournament_summons_json(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
team_summons = [summon.to_dict() for summon in tournament.team_summons()]
|
|
data = json.dumps(team_summons)
|
|
return HttpResponse(data, content_type='application/json')
|
|
|
|
def tournament_broadcast_home(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcast.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def automatic_broadcast(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_auto.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def automatic_broadcast_event(request, event_id):
|
|
event = get_object_or_404(Event, pk=event_id)
|
|
tournaments = Tournament.objects.filter(event=event)
|
|
tournament_ids = [str(tournament.id) for tournament in tournaments]
|
|
return render(request, 'tournaments/broadcast/broadcasted_auto_event.html', {
|
|
'tournament_ids': tournament_ids,
|
|
})
|
|
|
|
def qr_code_url(request, tournament_id):
|
|
qr_code_url = reverse('tournament', args=[tournament_id])
|
|
return request.build_absolute_uri(qr_code_url)
|
|
|
|
def qr_code_url_with_query(request, club_id):
|
|
url_with_query = f"{request.build_absolute_uri('/')}?club={str(club_id)}"
|
|
return url_with_query
|
|
|
|
def qr_code_options():
|
|
return QRCodeOptions(size=10, border=4, error_correction='L')
|
|
|
|
def tournament_matches(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_matches.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def broadcast_json(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
broadcast = tournament.broadcast_content()
|
|
data = json.dumps(broadcast)
|
|
return HttpResponse(data, content_type='application/json')
|
|
|
|
def tournament_matches_json(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
matches, group_stages = tournament.broadcasted_matches_and_group_stages()
|
|
|
|
live_matches_dicts = [match.live_match().to_dict() for match in matches]
|
|
data = json.dumps(live_matches_dicts, default=vars)
|
|
return HttpResponse(data, content_type='application/json')
|
|
|
|
def tournament_prog_json(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
matches = tournament.broadcasted_prog()
|
|
|
|
# Convert matches to JSON-serializable format, handling None/empty slots
|
|
live_matches = []
|
|
for match in matches:
|
|
if match is None or isinstance(match, dict) and match.get('empty'):
|
|
live_matches.append({"empty": True})
|
|
else:
|
|
live_matches.append(match.live_match().to_dict())
|
|
|
|
data = json.dumps(live_matches, default=vars)
|
|
return HttpResponse(data, content_type='application/json')
|
|
|
|
def tournament_group_stages(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
live_group_stages = list(tournament.live_group_stages())
|
|
unique_steps = sorted(set(gs.step for gs in live_group_stages), reverse=True)
|
|
|
|
return render(request, 'tournaments/group_stages.html', {
|
|
'tournament': tournament,
|
|
'group_stages': live_group_stages,
|
|
'unique_steps': unique_steps,
|
|
})
|
|
|
|
def tournament_broadcasted_group_stages(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_group_stages.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def tournament_broadcasted_prog(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_prog.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def tournament_broadcasted_bracket(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_bracket.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
|
|
def tournament_bracket_json(request, tournament_id):
|
|
"""
|
|
JSON endpoint for tournament bracket data.
|
|
"""
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
context = get_butterfly_bracket_view_context(tournament, parent_round=None, double_butterfly_mode=True, display_loser_final=True)
|
|
|
|
data = {
|
|
'match_groups': [match_group.to_dict() for match_group in context['match_groups']],
|
|
'double_butterfly_mode': context['double_butterfly_mode'],
|
|
'display_loser_final': context['display_loser_final']
|
|
}
|
|
|
|
return JsonResponse(data)
|
|
|
|
|
|
def tournament_live_group_stage_json(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
|
|
# Get all live group stages and filter for the last running step
|
|
live_group_stages = [gs.live_group_stages() for gs in tournament.last_group_stage_step()]
|
|
|
|
gs_dicts = [gs.to_dict() for gs in live_group_stages]
|
|
data = json.dumps(gs_dicts)
|
|
return HttpResponse(data, content_type='application/json')
|
|
|
|
def tournament_rankings(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
rankings = tournament.rankings()
|
|
|
|
return render(request, 'tournaments/rankings.html', {
|
|
'tournament': tournament,
|
|
'rankings': rankings,
|
|
})
|
|
|
|
def tournament_rankings_json(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
rankings = tournament.rankings()
|
|
|
|
data = json.dumps(rankings, default=vars)
|
|
return HttpResponse(data, content_type='application/json')
|
|
|
|
def tournament_broadcast_rankings(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
return render(request, 'tournaments/broadcast/broadcasted_rankings.html', {
|
|
'tournament': tournament,
|
|
'qr_code_url': qr_code_url(request, tournament_id),
|
|
'qr_code_options': qr_code_options(),
|
|
})
|
|
|
|
def players(request):
|
|
return render(request, 'tournaments/test.html')
|
|
|
|
def activate(request, uidb64, token):
|
|
try:
|
|
uid = force_str(urlsafe_base64_decode(uidb64))
|
|
user = CustomUser.objects.get(pk=uid)
|
|
except(TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
|
|
user = None
|
|
if user is not None and account_activation_token.check_token(user, token):
|
|
user.is_active = True
|
|
user.save()
|
|
|
|
# Specify the authentication backend when logging in
|
|
login(request, user, backend='django.contrib.auth.backends.ModelBackend')
|
|
|
|
return redirect('activation_success')
|
|
else:
|
|
# Redirect to a custom error page
|
|
return redirect('activation_failed') # Define this URL in your urls.py
|
|
|
|
def activation_success(request):
|
|
return render(request, 'registration/activation_success.html')
|
|
|
|
def activation_failed(request):
|
|
return render(request, 'registration/activation_failed.html')
|
|
|
|
def club_broadcast(request, broadcast_code):
|
|
club = get_object_or_404(Club, broadcast_code=broadcast_code)
|
|
q_not_deleted = Q(is_deleted=False, event__club=club)
|
|
tournaments = Tournament.objects.filter(q_not_deleted).order_by('-start_date')
|
|
|
|
return render(request, 'tournaments/broadcast/broadcast_club.html', {
|
|
'club': club,
|
|
'tournaments': tournaments,
|
|
})
|
|
|
|
def club_broadcast_auto(request, broadcast_code):
|
|
club = get_object_or_404(Club, broadcast_code=broadcast_code)
|
|
q_not_deleted = Q(is_deleted=False, event__club=club)
|
|
now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
|
|
recent_time_window = now - timedelta(hours=4)
|
|
|
|
tournaments = Tournament.objects.filter(
|
|
q_not_deleted,
|
|
Q(is_private=False), # Exclude private tournaments
|
|
(
|
|
(
|
|
Q(end_date__isnull=True) & # Include ongoing tournaments
|
|
(Q(start_date__gte=now - timedelta(days=3)) & Q(start_date__lt=now + timedelta(days=3))) # Include tournaments that are ongoing and start within 3 days
|
|
) |
|
|
(Q(end_date__isnull=False) & Q(end_date__gte=recent_time_window)) # Include tournaments finished within the last 4 hours
|
|
)
|
|
)
|
|
|
|
tournament_ids = [str(tournament.id) for tournament in tournaments]
|
|
#print(tournament_ids)
|
|
return render(request, 'tournaments/broadcast/broadcasted_auto_event.html', {
|
|
'tournament_ids': tournament_ids,
|
|
'club_name': club.name,
|
|
'qr_code_url': qr_code_url_with_query(request, club.id),
|
|
'qr_code_options': qr_code_options(),
|
|
|
|
})
|
|
|
|
def download(request):
|
|
return render(request, 'tournaments/download.html')
|
|
|
|
def test_apns(request):
|
|
|
|
user = CustomUser.objects.filter(username='laurent').first()
|
|
for device_token in user.device_tokens.all():
|
|
asyncio.run(send_push_notification(device_token.value, 'LOL?!'))
|
|
|
|
# token = DeviceToken.objects.first()
|
|
|
|
return HttpResponse('OK!')
|
|
|
|
def test_websocket(request):
|
|
return render(request, 'tournaments/test_websocket.html')
|
|
|
|
def terms_of_use(request):
|
|
return render(request, 'terms_of_use.html')
|
|
|
|
def simple_form_view(request):
|
|
|
|
if request.method == 'POST':
|
|
# If this is a POST request, we need to process the form data
|
|
form = SimpleForm(request.POST)
|
|
if form.is_valid():
|
|
# Process the data in form.cleaned_data
|
|
name = form.cleaned_data['name']
|
|
# Do something with the data (e.g., save to the database)
|
|
send_email_to_jap_list()
|
|
# send_email('laurent@staxriver.com', 'Laurent')
|
|
return HttpResponse(f"Hello, {name}!") # Simple response with name
|
|
else:
|
|
form = SimpleForm()
|
|
# print(request.method)
|
|
# If this is a GET request, we display an empty form
|
|
|
|
return render(request, 'tournaments/admin/mail_test.html', {'form': form})
|
|
|
|
def send_email_to_jap_list():
|
|
|
|
# Open the CSV file
|
|
with open('tournaments/static/misc/jap-test.csv', 'r') as file:
|
|
csv_reader = csv.reader(file)
|
|
|
|
def log_error(message):
|
|
with open('email_errors.log', 'a') as log_file:
|
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
log_file.write(f"{timestamp}: {message}\n")
|
|
|
|
batch = []
|
|
for row in csv_reader:
|
|
if len(row) >= 4:
|
|
name = row[2]
|
|
mail = row[3]
|
|
batch.append((mail, name))
|
|
|
|
if len(batch) == 10:
|
|
for email, recipient_name in batch:
|
|
try:
|
|
send_email(email, recipient_name)
|
|
print(f"Name: {recipient_name}, mail: {email}")
|
|
except Exception as e:
|
|
error_message = f"Error sending email to {email}: {str(e)}"
|
|
log_error(error_message)
|
|
print(error_message)
|
|
|
|
batch = []
|
|
time.sleep(2)
|
|
else:
|
|
print(f"Row doesn't have enough elements: {row}")
|
|
|
|
# Send any remaining emails in the last batch
|
|
for email, recipient_name in batch:
|
|
try:
|
|
send_email(email, recipient_name)
|
|
print(f"Name: {recipient_name}, mail: {email}")
|
|
except Exception as e:
|
|
error_message = f"Error sending email to {email}: {str(e)}"
|
|
log_error(error_message)
|
|
print(error_message)
|
|
|
|
def send_email(mail, name):
|
|
if name.lower() not in mail.lower():
|
|
name = ""
|
|
|
|
subject = "Tes tournois en toute simplicité avec Padel Club"
|
|
body = f"Salut {name} !\n\nJe me permets de t'écrire car je suis JAP2 en région PACA et développeur, et je viens de lancer Padel Club, une app iOS qui facilite enfin l'organisation des tournois.\n\nAvec elle, tu peux convoquer rapidement, simuler et programmer tes structures, diffuser tous les résultats à tous les joueurs, et ce depuis un iPhone.\n\nTu peux l'essayer gratuitement pour découvrir tout son potentiel ! Télécharge l'app ici et teste la dès ton prochain tournoi: https://padelclub.app/download/\n\nJe suis disponible pour échanger avec toi par mail ou téléphone au 06 81 59 81 93 et voir ce que tu en penses.\nÀ bientôt j'espère !\n\nRazmig"
|
|
|
|
email = EmailMessage(subject, body, to=[mail])
|
|
email.send()
|
|
|
|
def signup(request):
|
|
next_url = request.GET.get('next', '/')
|
|
|
|
if request.method == 'POST':
|
|
form = SimpleCustomUserCreationForm(request.POST)
|
|
if form.is_valid():
|
|
user = form.save(commit=False)
|
|
user.is_active = False
|
|
user.origin = UserOrigin.SITE
|
|
user.save()
|
|
|
|
if not settings.DEBUG:
|
|
send_verification_email(request, user, next_url)
|
|
|
|
return render(request, 'registration/signup_success.html', {
|
|
'next_url': next_url,
|
|
'user_email': user.email # Add the user's email to the context
|
|
})
|
|
else:
|
|
form = SimpleCustomUserCreationForm()
|
|
|
|
response = render(request, 'registration/signup.html', {'form': form})
|
|
return response
|
|
|
|
def send_verification_email(request, user, next_url):
|
|
print('next_url', next_url)
|
|
|
|
current_site = get_current_site(request)
|
|
mail_subject = 'Activez votre compte Padel Club !'
|
|
# Prepare the email subject and message
|
|
message = render_to_string('tournaments/acc_active_email.html', {
|
|
'user': user,
|
|
'domain': current_site.domain,
|
|
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
|
|
'token': account_activation_token.make_token(user),
|
|
'next': next_url, # Pass next URL to the template
|
|
})
|
|
|
|
email = EmailMessage(mail_subject, message, to=[user.email])
|
|
email.content_subtype = "html"
|
|
email.send()
|
|
|
|
@login_required
|
|
def profile(request):
|
|
user = request.user # Get the currently authenticated user
|
|
return render(request, 'registration/profile.html', {
|
|
'user_name': user.username
|
|
})
|
|
|
|
@login_required
|
|
def unregister_tournament(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, id=tournament_id)
|
|
service = TournamentUnregistrationService(request, tournament)
|
|
|
|
if not service.can_unregister():
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
service.process_unregistration()
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
class CustomPasswordResetConfirmView(PasswordResetConfirmView):
|
|
template_name = 'registration/password_reset_confirm.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['custom_message'] = "Veuillez entrer un nouveau mot de passe."
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
return response
|
|
|
|
def get_user(self, uidb64):
|
|
"""
|
|
Override this method to decode the uid and return the corresponding user.
|
|
"""
|
|
try:
|
|
# Use get_user_model() instead of direct User import
|
|
User = get_user_model()
|
|
|
|
uid = urlsafe_base64_decode(uidb64).decode()
|
|
user = User.objects.get(pk=uid)
|
|
return user
|
|
except (TypeError, ValueError, User.DoesNotExist):
|
|
raise Http404("User not found")
|
|
|
|
class CustomPasswordResetCompleteView(PasswordResetCompleteView):
|
|
template_name = 'registration/password_reset_complete.html'
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
# Get the user from the session
|
|
username = request.session.get('reset_username')
|
|
|
|
if username:
|
|
try:
|
|
# Get the user
|
|
User = get_user_model()
|
|
user = User.objects.get(username=username)
|
|
|
|
# Log the user in
|
|
login(request, user, backend='django.contrib.auth.backends.ModelBackend')
|
|
|
|
# Clean up the session
|
|
if 'reset_username' in request.session:
|
|
del request.session['reset_username']
|
|
|
|
# Redirect to the profile page
|
|
return redirect('profile')
|
|
except User.DoesNotExist:
|
|
pass
|
|
|
|
# If no username in session or user not found, proceed with normal view
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
@login_required
|
|
def custom_password_change(request):
|
|
if request.method == 'POST':
|
|
form = CustomPasswordChangeForm(user=request.user, data=request.POST)
|
|
if form.is_valid():
|
|
user = form.save()
|
|
update_session_auth_hash(request, user) # Important to keep user logged in
|
|
messages.success(request, 'Votre mot de passe a été mis à jour avec succès!')
|
|
return redirect('profile')
|
|
else:
|
|
# Form is invalid, show errors
|
|
profile_form = ProfileUpdateForm(instance=request.user)
|
|
return render(request, 'profile.html', {
|
|
'form': profile_form,
|
|
'password_change_form': form
|
|
})
|
|
|
|
# If not POST, redirect to profile page
|
|
return redirect('profile')
|
|
|
|
@login_required
|
|
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
|
|
upcoming_tournaments = filter_user_tournaments(future_tournaments(None))
|
|
running_tournaments = filter_user_tournaments(live_tournaments(None))
|
|
ended_tournaments = filter_user_tournaments([t for t in finished_tournaments(None) if not t.is_canceled()])
|
|
|
|
return render(request, 'registration/my_tournaments.html', {
|
|
'upcoming_tournaments': upcoming_tournaments,
|
|
'running_tournaments': running_tournaments,
|
|
'ended_tournaments': ended_tournaments[:12],
|
|
'user_name': user.username
|
|
})
|
|
|
|
@login_required
|
|
def all_my_ended_tournaments(request):
|
|
user_licence_id = request.user.licence_id
|
|
|
|
# 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,
|
|
"tournaments/tournaments_list.html",
|
|
{
|
|
'tournaments': ended_tournaments,
|
|
'title': "Palmarès",
|
|
})
|
|
|
|
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = CustomUser
|
|
form_class = ProfileUpdateForm
|
|
template_name = 'profile.html'
|
|
success_url = reverse_lazy('profile')
|
|
login_url = '/login/' # Specify where to redirect if user is not logged in
|
|
|
|
def get_object(self, queryset=None):
|
|
return self.request.user
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['password_change_form'] = CustomPasswordChangeForm(user=self.request.user)
|
|
return context
|
|
|
|
@staff_member_required
|
|
def tournament_import_view(request):
|
|
if request.method == 'POST':
|
|
zip_file = request.FILES.get('tournament_zip')
|
|
if zip_file:
|
|
print('yes')
|
|
|
|
try:
|
|
tournament_id = os.path.splitext(zip_file.name)[0]
|
|
tournament = Tournament.objects.get(id=tournament_id)
|
|
|
|
# Delete existing relationships
|
|
print('round delete')
|
|
|
|
rounds = Round.objects.filter(tournament=tournament,parent=None)
|
|
for round in rounds:
|
|
round.delete_dependencies()
|
|
for gs in tournament.group_stages.all():
|
|
gs.delete_dependencies()
|
|
for tr in tournament.team_registrations.all():
|
|
tr.delete_dependencies()
|
|
|
|
# with transaction.atomic():
|
|
# for round in tournament.rounds.all():
|
|
# round.delete()
|
|
tournament.rounds.all().delete()
|
|
tournament.group_stages.all().delete()
|
|
tournament.team_registrations.all().delete()
|
|
# tournament.player_registrations.all().delete()
|
|
# tournament.matches.all().delete()
|
|
# tournament.team_scores.all().delete()
|
|
|
|
print(f'round count = {tournament.rounds.count()}')
|
|
print(f'group_stages count = {tournament.group_stages.count()}')
|
|
print(f'team_registrations count = {tournament.team_registrations.count()}')
|
|
|
|
print(f'PR count = {PlayerRegistration.objects.count()}')
|
|
print(f'TS count = {TeamScore.objects.count()}')
|
|
print(f'M count = {Match.objects.count()}')
|
|
|
|
with zipfile.ZipFile(zip_file) as z:
|
|
# First, process rounds
|
|
rounds_data = get_file_data(z, f"{tournament_id}/rounds.json")
|
|
# print(f'rounds count = {len(rounds_data)}')
|
|
|
|
if rounds_data:
|
|
# First pass: Create rounds with preserved UUIDs
|
|
for item in rounds_data:
|
|
# print('round #1 create')
|
|
item['tournament'] = tournament.id
|
|
round_id = item['id'] # Preserve the original UUID
|
|
Round.objects.create(
|
|
id=round_id,
|
|
tournament_id=tournament.id,
|
|
index=item['index'],
|
|
format=item.get('format'),
|
|
start_date=item.get('start_date'),
|
|
group_stage_loser_bracket=item.get('group_stage_loser_bracket', False),
|
|
loser_bracket_mode=item.get('loser_bracket_mode', 0)
|
|
)
|
|
|
|
# Second pass: Set parent relationships
|
|
for item in rounds_data:
|
|
# print('round #2 set parent')
|
|
|
|
if item.get('parent'):
|
|
round_obj = Round.objects.get(id=item['id'])
|
|
round_obj.parent_id = item['parent']
|
|
round_obj.save()
|
|
|
|
# Then process all other files
|
|
serializer_mapping = {
|
|
# 'rounds.json': RoundSerializer,
|
|
'group-stages.json': GroupStageSerializer,
|
|
'team-registrations.json': TeamRegistrationSerializer,
|
|
'matches.json': MatchSerializer,
|
|
'player-registrations.json': PlayerRegistrationSerializer,
|
|
'team-scores.json': TeamScoreSerializer
|
|
}
|
|
|
|
# Process each remaining file
|
|
for filename, serializer_class in serializer_mapping.items():
|
|
process_file(z, filename, tournament_id, tournament, serializer_class)
|
|
|
|
return JsonResponse({'status': 'success'})
|
|
|
|
except Exception as e:
|
|
print(f'error = {str(e)}')
|
|
return JsonResponse({'status': 'error', 'message': str(e)})
|
|
else:
|
|
return render(request, 'tournaments/admin/tournament_cleaner.html')
|
|
|
|
def process_file(zip_file, filename, tournament_id, tournament, serializer_class):
|
|
"""Helper function to process individual files"""
|
|
try:
|
|
print(f'process {filename}')
|
|
file_path = f"{tournament_id}/{filename}"
|
|
json_data = get_file_data(zip_file, file_path)
|
|
|
|
if json_data:
|
|
# Add tournament to each item
|
|
for item in json_data:
|
|
# print(f'process {item}')
|
|
item['tournament'] = tournament.id
|
|
item['store_id'] = str(tournament.id)
|
|
|
|
serializer = serializer_class(data=json_data, many=True)
|
|
serializer.is_valid(raise_exception=True)
|
|
serializer.save()
|
|
|
|
except Exception as e:
|
|
print(f"Error processing {filename}: {str(e)}")
|
|
|
|
def get_file_data(zip_file, file_path):
|
|
"""Helper function to read and parse JSON data from zip file"""
|
|
try:
|
|
file_content = zip_file.read(file_path)
|
|
return json.loads(file_content)
|
|
except KeyError:
|
|
print(f"File not found: {file_path}")
|
|
return None
|
|
except json.JSONDecodeError as e:
|
|
print(f"JSON Error for {file_path}: {str(e)}")
|
|
raise Exception(f"Invalid JSON in file {file_path}")
|
|
|
|
def status_page(request):
|
|
return render(request, 'tournaments/admin/status.html')
|
|
|
|
def team_details(request, tournament_id, team_id):
|
|
# First check if team_id is None or invalid
|
|
if team_id is None or team_id == 'None':
|
|
# Redirect to tournament page or show an error
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
tournament = get_object_or_404(Tournament, id=tournament_id)
|
|
try:
|
|
team = get_object_or_404(TeamRegistration, id=team_id)
|
|
except (ValueError, ValidationError):
|
|
# Handle invalid UUID
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
# Get all matches for this team
|
|
all_matches = team.get_matches()
|
|
|
|
return render(request, 'tournaments/team_details.html', {
|
|
'tournament': tournament,
|
|
'team': team,
|
|
'matches': all_matches,
|
|
'debug': False # Set to False in production
|
|
})
|
|
|
|
def tournament_bracket(request, tournament_id):
|
|
"""
|
|
View to display tournament bracket structure.
|
|
"""
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
context = get_butterfly_bracket_view_context(tournament, parent_round=None, double_butterfly_mode=False, display_loser_final=True)
|
|
return render(request, 'tournaments/tournament_bracket.html', context)
|
|
|
|
def round_bracket(request, tournament_id, round_id):
|
|
"""
|
|
View to display round bracket structure.
|
|
"""
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
round = get_object_or_404(Round, pk=round_id)
|
|
context = get_butterfly_bracket_view_context(tournament, round, double_butterfly_mode=False, display_loser_final=False)
|
|
return render(request, 'tournaments/tournament_bracket.html', context)
|
|
|
|
def get_butterfly_bracket_view_context(tournament, parent_round=None, double_butterfly_mode=False, display_loser_final=True):
|
|
if parent_round:
|
|
double_butterfly_mode = False
|
|
display_loser_final = True
|
|
|
|
serializable_match_groups = tournament.get_butterfly_bracket_match_group(parent_round, double_butterfly_mode, display_loser_final)
|
|
|
|
context = {
|
|
'tournament': tournament,
|
|
'match_groups': serializable_match_groups,
|
|
'double_butterfly_mode': double_butterfly_mode,
|
|
'display_loser_final': display_loser_final
|
|
}
|
|
|
|
return context
|
|
|
|
def tournament_prog(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, id=tournament_id)
|
|
|
|
# Get matches from all_groups
|
|
match_groups = tournament.all_groups(broadcasted=False)
|
|
|
|
# Flatten matches and add necessary attributes
|
|
all_matches = []
|
|
for group in match_groups:
|
|
for match in group.matches:
|
|
if match.start_date: # Only include matches with start dates
|
|
all_matches.append({
|
|
'start_date': match.start_date,
|
|
'court_index': match.court_index,
|
|
# Add other match attributes needed for match_cell.html
|
|
'match': match
|
|
})
|
|
|
|
# Sort matches by date and court
|
|
all_matches.sort(key=lambda x: (x['start_date'], x['court_index'] or 999))
|
|
|
|
context = {
|
|
'tournament': tournament,
|
|
'match_groups': {'matches': all_matches}
|
|
}
|
|
return render(request, 'tournaments/prog.html', context)
|
|
|
|
def custom_logout(request):
|
|
logout(request)
|
|
return redirect('index') # or whatever URL you want to redirect to
|
|
|
|
@login_required
|
|
def confirm_tournament_registration(request, tournament_id):
|
|
if request.method == 'POST':
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
# Find the team registration for this user
|
|
user_licence_id = request.user.licence_id
|
|
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.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
team_registration = team_registrations.first()
|
|
|
|
if team_registration.is_confirmation_expired():
|
|
team_registration.check_confirmation_deadline()
|
|
messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus confirmer votre participation.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
# Confirm registration
|
|
team_registration.confirm_registration()
|
|
|
|
messages.success(request, "Votre participation est confirmée !")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
@login_required
|
|
def proceed_to_payment(request, tournament_id):
|
|
"""Handle direct payment from tournament info page"""
|
|
tournament = get_object_or_404(Tournament, id=tournament_id)
|
|
team = tournament.get_user_team_registration(request.user)
|
|
|
|
if not team:
|
|
messages.error(request, "Aucune inscription trouvée pour ce tournoi.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
if team.is_confirmation_expired():
|
|
team.check_confirmation_deadline()
|
|
messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus régler votre participation.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
if team.is_paid():
|
|
messages.info(request, "Votre inscription est déjà payée.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
try:
|
|
# Create payment session
|
|
payment_service = PaymentService(request)
|
|
checkout_session = payment_service.create_checkout_session(
|
|
tournament_id=str(tournament_id), # Convert UUID to string
|
|
team_fee=team.get_remaining_fee(),
|
|
team_registration_id=str(team.id) # Convert UUID to string
|
|
)
|
|
|
|
# Redirect to Stripe checkout
|
|
return redirect(checkout_session.url)
|
|
except Exception as e:
|
|
print(f"Payment error: {str(e)}") # Add debug print
|
|
messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
@login_required
|
|
def tournament_payment_success(request, tournament_id):
|
|
"""Handle successful Stripe payment for tournament registration"""
|
|
# Get checkout session ID from session
|
|
checkout_session_id = request.session.get('stripe_checkout_session_id')
|
|
if not checkout_session_id:
|
|
messages.error(request, "Session de paiement introuvable.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
try:
|
|
# Verify payment status with Stripe
|
|
stripe.api_key = settings.STRIPE_SECRET_KEY
|
|
checkout_session = stripe.checkout.Session.retrieve(checkout_session_id)
|
|
|
|
if checkout_session.payment_status == 'paid':
|
|
# Process the payment success
|
|
payment_service = PaymentService(request)
|
|
success = payment_service.process_successful_payment(str(tournament_id), checkout_session)
|
|
|
|
if success:
|
|
# Set a flag for successful registration if the payment was from registration page
|
|
source_page = request.session.get('payment_source_page', 'tournament_info')
|
|
if source_page == 'register_tournament':
|
|
request.session['registration_successful'] = True
|
|
|
|
messages.success(request, "Paiement réussi et inscription confirmée !")
|
|
else:
|
|
messages.error(request, "Erreur lors du traitement du paiement.")
|
|
else:
|
|
messages.error(request, "Le paiement n'a pas été complété.")
|
|
|
|
except Exception as e:
|
|
print(f"Payment processing error: {str(e)}") # Add debug print
|
|
messages.error(request, f"Erreur lors de la vérification du paiement: {str(e)}")
|
|
|
|
# Get the source page to determine where to redirect
|
|
source_page = request.session.get('payment_source_page', 'tournament_info')
|
|
|
|
# Clean up session variables
|
|
for key in ['stripe_checkout_session_id', 'team_registration_id', 'payment_source_page']:
|
|
if key in request.session:
|
|
del request.session[key]
|
|
|
|
# Redirect to the appropriate page
|
|
if source_page == 'register_tournament':
|
|
# For payments during registration, redirect back to registration page
|
|
# with registration_successful flag in session
|
|
return redirect('register_tournament', tournament_id=tournament_id)
|
|
else:
|
|
# For direct payments, go to tournament info
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
@csrf_protect
|
|
def register_tournament(request, tournament_id):
|
|
tournament = get_object_or_404(Tournament, id=tournament_id)
|
|
|
|
# Check for registration_successful flag
|
|
registration_successful = request.session.pop('registration_successful', False)
|
|
|
|
# If registration was successful, render success page immediately
|
|
if registration_successful:
|
|
storage = messages.get_messages(request)
|
|
for _ in storage:
|
|
pass
|
|
|
|
if len(storage._loaded_messages) == 1:
|
|
del storage._loaded_messages[0]
|
|
|
|
context = {
|
|
'tournament': tournament,
|
|
'registration_successful': True,
|
|
'current_players': [],
|
|
'cart_data': {'players': []}
|
|
}
|
|
return render(request, 'register_tournament.html', context)
|
|
|
|
cart_manager = RegistrationCartManager(request)
|
|
|
|
# Debug session content
|
|
print_session_debug(request, "SESSION DUMP AT START")
|
|
|
|
# Check if tournament is open for registration
|
|
if not tournament.enable_online_registration:
|
|
messages.error(request, "L'inscription en ligne n'est pas activée pour ce tournoi.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
# Check if user is already registered
|
|
if request.user.is_authenticated and request.user.licence_id and tournament.is_user_registered(request.user):
|
|
messages.info(request, "Vous êtes déjà inscrit à ce tournoi.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
# Only initialize a fresh cart for GET requests
|
|
# For POST requests, use the existing cart to maintain state
|
|
if request.method == 'GET':
|
|
# Check if we're returning from Stripe (don't reinitialize if we have a checkout session)
|
|
if 'stripe_checkout_session_id' in request.session:
|
|
# We're returning from Stripe, don't reinitialize the cart
|
|
pass
|
|
else:
|
|
|
|
print("Initializing cart")
|
|
storage = messages.get_messages(request)
|
|
for _ in storage:
|
|
pass
|
|
|
|
if len(storage._loaded_messages) == 1:
|
|
del storage._loaded_messages[0]
|
|
|
|
# ALWAYS initialize a fresh cart when entering the registration page (GET request)
|
|
# This ensures no old cart data persists
|
|
cart_manager.initialize_cart(tournament_id)
|
|
|
|
# Auto-add the authenticated user with license
|
|
if request.user.is_authenticated and request.user.licence_id:
|
|
cart_manager.add_authenticated_user()
|
|
else:
|
|
# For POST, ensure tournament ID is correct
|
|
current_tournament_id = cart_manager.get_tournament_id()
|
|
if current_tournament_id != str(tournament_id):
|
|
cart_manager.initialize_cart(tournament_id)
|
|
|
|
# Re-add the authenticated user if they have a license
|
|
if request.user.is_authenticated and request.user.licence_id:
|
|
cart_manager.add_authenticated_user()
|
|
|
|
# Get cart data
|
|
cart_data = cart_manager.get_cart_data()
|
|
print(f"View - Cart Players Count: {len(cart_data['players'])}")
|
|
|
|
# Initialize context with cart data
|
|
context = {
|
|
'tournament': tournament,
|
|
'current_players': cart_data['players'],
|
|
'registration_successful': False,
|
|
'cart_data': cart_data
|
|
}
|
|
|
|
# Initialize forms
|
|
context['team_form'] = TournamentRegistrationForm(initial={
|
|
'email': request.user.email if request.user.is_authenticated else '',
|
|
'mobile_number': request.user.phone if request.user.is_authenticated else cart_data.get('mobile_number', '')
|
|
})
|
|
|
|
# Initialize the add player form
|
|
context['add_player_form'] = create_add_player_form(request, tournament, cart_data)
|
|
|
|
# Handle POST requests
|
|
if request.method == 'POST':
|
|
if 'add_player' in request.POST:
|
|
handle_add_player_request(request, tournament, cart_manager, context)
|
|
elif 'remove_player' in request.POST:
|
|
handle_remove_player_request(request, tournament, cart_manager, context)
|
|
elif 'register_team' in request.POST:
|
|
handle_register_team_request(request, tournament, cart_manager, context)
|
|
elif 'proceed_to_payment' in request.POST:
|
|
result = handle_payment_request(request, tournament, cart_manager, context, tournament_id)
|
|
if result:
|
|
return result # This is the redirect to Stripe checkout
|
|
|
|
# Debug session content before rendering
|
|
print_session_debug(request, "SESSION DUMP BEFORE RENDER")
|
|
|
|
return render(request, 'register_tournament.html', context)
|
|
|
|
def create_add_player_form(request, tournament, cart_data):
|
|
"""Create the appropriate add player form based on user status"""
|
|
# Special handling for authenticated user without license
|
|
if request.user.is_authenticated and not request.user.licence_id and not cart_data['players']:
|
|
# Setup form for user without license
|
|
initial_data = {
|
|
'first_name': request.user.first_name,
|
|
'last_name': request.user.last_name
|
|
}
|
|
|
|
# If license is required, only show license field initially
|
|
if tournament.license_is_required:
|
|
add_player_form = AddPlayerForm(initial=initial_data)
|
|
add_player_form.user_without_licence = True
|
|
return add_player_form
|
|
else:
|
|
# If license not required, show all fields
|
|
return AddPlayerForm(initial=initial_data)
|
|
elif not cart_data['players'] or len(cart_data['players']) < tournament.minimum_player_per_team:
|
|
# Regular partner addition form
|
|
return AddPlayerForm()
|
|
|
|
return None
|
|
|
|
def handle_add_player_request(request, tournament, cart_manager, context):
|
|
"""Handle the 'add_player' POST action"""
|
|
add_player_form = AddPlayerForm(request.POST)
|
|
if add_player_form.is_valid():
|
|
# Get the current form data for phone number
|
|
team_form = TournamentRegistrationForm(request.POST)
|
|
if team_form.is_valid():
|
|
# Update cart with mobile number before adding player
|
|
cart_manager.update_contact_info(
|
|
mobile_number=team_form.cleaned_data.get('mobile_number')
|
|
)
|
|
|
|
success, message = cart_manager.add_player(add_player_form.cleaned_data)
|
|
if success:
|
|
messages.success(request, message)
|
|
# Refresh cart data
|
|
cart_data = cart_manager.get_cart_data()
|
|
context['current_players'] = cart_data['players']
|
|
|
|
context['team_form'] = TournamentRegistrationForm(initial={
|
|
'email': request.user.email if request.user.is_authenticated else '',
|
|
'mobile_number': cart_data.get('mobile_number', '')
|
|
})
|
|
|
|
# Prepare a fresh form for the next player if needed
|
|
if len(cart_data['players']) < tournament.minimum_player_per_team:
|
|
context['add_player_form'] = AddPlayerForm()
|
|
else:
|
|
# Remove the form if we've reached the team limit
|
|
context['add_player_form'] = None
|
|
else:
|
|
messages.error(request, message)
|
|
if cart_manager.first_tournament:
|
|
add_player_form.first_tournament = True
|
|
|
|
context['add_player_form'] = add_player_form
|
|
else:
|
|
for field, errors in add_player_form.errors.items():
|
|
for error in errors:
|
|
messages.error(request, f"{field}: {error}")
|
|
context['add_player_form'] = add_player_form
|
|
|
|
def handle_remove_player_request(request, tournament, cart_manager, context):
|
|
"""Handle the 'remove_player' POST action"""
|
|
success, message = cart_manager.remove_player()
|
|
if success:
|
|
messages.info(request, message)
|
|
# Refresh cart data
|
|
cart_data = cart_manager.get_cart_data()
|
|
context['current_players'] = cart_data['players']
|
|
|
|
# Update the add player form based on the new state
|
|
if not cart_data['players'] and request.user.is_authenticated:
|
|
if not request.user.licence_id and tournament.license_is_required:
|
|
initial_data = {
|
|
'first_name': request.user.first_name,
|
|
'last_name': request.user.last_name
|
|
}
|
|
add_player_form = AddPlayerForm(initial=initial_data)
|
|
add_player_form.user_without_licence = True
|
|
else:
|
|
add_player_form = AddPlayerForm()
|
|
|
|
context['add_player_form'] = add_player_form
|
|
else:
|
|
context['add_player_form'] = AddPlayerForm()
|
|
else:
|
|
messages.error(request, message)
|
|
|
|
def handle_register_team_request(request, tournament, cart_manager, context):
|
|
"""Handle the 'register_team' POST action"""
|
|
team_form = TournamentRegistrationForm(request.POST)
|
|
if team_form.is_valid():
|
|
# Debug print before checkout
|
|
cart_data = cart_manager.get_cart_data()
|
|
print(f"Before checkout - Players in cart: {len(cart_data['players'])}")
|
|
|
|
# Ensure the session data is correctly saved before proceeding
|
|
request.session.modified = True
|
|
request.session.save()
|
|
|
|
# Update cart with contact info
|
|
cart_manager.update_contact_info(
|
|
mobile_number=team_form.cleaned_data.get('mobile_number')
|
|
)
|
|
|
|
# Get fresh cart data again after updating contact info
|
|
fresh_cart_data = cart_manager.get_cart_data()
|
|
print(f"After contact update - Players in cart: {len(fresh_cart_data['players'])}")
|
|
|
|
# Debug session content
|
|
print_session_debug(request, "SESSION DUMP BEFORE CHECKOUT")
|
|
|
|
# Checkout and create registration
|
|
success, result = cart_manager.checkout()
|
|
if success:
|
|
waiting_list_position = cart_data.get('waiting_list_position', -1)
|
|
if is_not_sqlite_backend():
|
|
email_service = TournamentEmailService()
|
|
email_service.send_registration_confirmation(
|
|
request,
|
|
tournament,
|
|
result, # team_registration
|
|
waiting_list_position
|
|
)
|
|
|
|
context['registration_successful'] = True
|
|
context['current_players'] = []
|
|
context['add_player_form'] = None # No more adding players after success
|
|
else:
|
|
messages.error(request, result)
|
|
else:
|
|
for field, errors in team_form.errors.items():
|
|
for error in errors:
|
|
messages.error(request, f"{field}: {error}")
|
|
context['team_form'] = team_form
|
|
|
|
def handle_payment_request(request, tournament, cart_manager, context, tournament_id):
|
|
"""Handle the 'proceed_to_payment' POST action"""
|
|
team_form = TournamentRegistrationForm(request.POST)
|
|
if team_form.is_valid():
|
|
# Update cart with contact info
|
|
cart_manager.update_contact_info(
|
|
mobile_number=team_form.cleaned_data.get('mobile_number')
|
|
)
|
|
|
|
# Create payment session
|
|
try:
|
|
# Get cart data for payment metadata
|
|
cart_data = cart_manager.get_cart_data()
|
|
|
|
# Create and redirect to payment session
|
|
payment_service = PaymentService(request)
|
|
checkout_session = payment_service.create_checkout_session(
|
|
tournament_id=tournament_id,
|
|
team_fee=tournament.team_fee(), # Use the appropriate fee field
|
|
cart_data=cart_data
|
|
)
|
|
|
|
# Redirect to Stripe checkout
|
|
return redirect(checkout_session.url)
|
|
except Exception as e:
|
|
messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
|
|
else:
|
|
for field, errors in team_form.errors.items():
|
|
for error in errors:
|
|
messages.error(request, f"{field}: {error}")
|
|
context['team_form'] = team_form
|
|
|
|
return None
|
|
|
|
def print_session_debug(request, header):
|
|
"""Debug utility to print session contents"""
|
|
print(f"===== {header} =====")
|
|
for key, value in request.session.items():
|
|
if key.startswith('registration_'):
|
|
print(f"{key}: {value}")
|
|
print("================================")
|
|
|
|
@login_required
|
|
def cancel_registration(request, tournament_id):
|
|
"""Cancel the current registration process and clear the cart"""
|
|
cart_manager = RegistrationCartManager(request)
|
|
cart_manager.clear_cart()
|
|
|
|
try:
|
|
tournament = Tournament.objects.get(id=tournament_id)
|
|
tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
|
|
tournament.save()
|
|
except Tournament.DoesNotExist:
|
|
messages.error(request, "Tournoi introuvable.")
|
|
return redirect('tournaments-list')
|
|
|
|
messages.info(request, "Processus d'inscription annulé.")
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
@login_required
|
|
def confirm_call(request, tournament_id):
|
|
if request.method == 'POST':
|
|
tournament = get_object_or_404(Tournament, pk=tournament_id)
|
|
team = tournament.get_user_team_registration(request.user)
|
|
|
|
if team and team.call_date:
|
|
team.confirmation_date = timezone.now()
|
|
team.save()
|
|
# messages.success(request, 'Réception de la convocation confirmée.')
|
|
else:
|
|
messages.error(request, 'Une erreur est survenue.')
|
|
|
|
return redirect('tournament-info', tournament_id=tournament_id)
|
|
|
|
class UserListExportView(LoginRequiredMixin, View):
|
|
def get(self, request, *args, **kwargs):
|
|
users = CustomUser.objects.order_by('date_joined')
|
|
|
|
# Prepare the response
|
|
response = HttpResponse(content_type='text/plain; charset=utf-8')
|
|
|
|
# Write header
|
|
headers = [
|
|
'Prenom', 'Nom', 'Club', 'Email', 'Telephone', 'Username',
|
|
'Origine', 'Actif', 'Inscription', 'Tournois'
|
|
]
|
|
response.write('\t'.join(headers) + '\n')
|
|
|
|
# Write data rows
|
|
for user in users:
|
|
row = [
|
|
str(user.first_name or ''),
|
|
str(user.last_name or ''),
|
|
str(user.latest_event_club_name() or ''),
|
|
str(user.email or ''),
|
|
str(user.phone or ''),
|
|
user.username,
|
|
str(user.get_origin_display()),
|
|
'Oui' if user.is_active else 'Non',
|
|
user.date_joined.strftime('%Y-%m-%d %H:%M:%S'),
|
|
str(user.event_count())
|
|
]
|
|
# Replace any tabs or newlines in the data to prevent formatting issues
|
|
row = [field.replace('\t', ' ') for field in row]
|
|
response.write('\t'.join(row) + '\r\n')
|
|
|
|
return response
|
|
|