mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-10-31 22:24:30 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			948 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			948 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (C) 2020 by Animath
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| import json
 | |
| import os
 | |
| import subprocess
 | |
| from tempfile import mkdtemp
 | |
| 
 | |
| from django.conf import settings
 | |
| from django.contrib import messages
 | |
| from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
 | |
| from django.contrib.auth.models import User
 | |
| from django.contrib.sites.models import Site
 | |
| from django.core.exceptions import PermissionDenied, ValidationError
 | |
| from django.db import transaction
 | |
| from django.db.models import Q
 | |
| from django.http import FileResponse, Http404
 | |
| from django.shortcuts import redirect, resolve_url
 | |
| from django.template.loader import render_to_string
 | |
| from django.urls import reverse_lazy
 | |
| from django.utils import timezone, translation
 | |
| from django.utils.crypto import get_random_string
 | |
| from django.utils.http import urlsafe_base64_decode
 | |
| from django.utils.text import format_lazy
 | |
| from django.utils.translation import gettext_lazy as _
 | |
| from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
 | |
| from django_tables2 import SingleTableView
 | |
| from magic import Magic
 | |
| from participation.models import Passage, Solution, Tournament, WrittenReview
 | |
| from tfjm.tokens import email_validation_token
 | |
| from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin
 | |
| 
 | |
| from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, \
 | |
|     ParentalAuthorizationFinalForm, ParentalAuthorizationForm, PaymentAdminForm, PaymentForm, \
 | |
|     PhotoAuthorizationFinalForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
 | |
|     VaccineSheetForm, VolunteerRegistrationForm
 | |
| from .models import ParticipantRegistration, Payment, Registration, StudentRegistration
 | |
| from .tables import RegistrationTable
 | |
| 
 | |
| 
 | |
| class SignupView(CreateView):
 | |
|     """
 | |
|     Signup, as a participant or a coach.
 | |
|     """
 | |
|     model = User
 | |
|     form_class = SignupForm
 | |
|     template_name = "registration/signup.html"
 | |
|     extra_context = dict(title=_("Sign up"))
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data()
 | |
| 
 | |
|         context["student_registration_form"] = StudentRegistrationForm(self.request.POST or None)
 | |
|         context["coach_registration_form"] = CoachRegistrationForm(self.request.POST or None)
 | |
| 
 | |
|         del context["student_registration_form"].fields["team"]
 | |
|         del context["student_registration_form"].fields["email_confirmed"]
 | |
|         del context["coach_registration_form"].fields["team"]
 | |
|         del context["coach_registration_form"].fields["email_confirmed"]
 | |
| 
 | |
|         return context
 | |
| 
 | |
|     def get_form(self, form_class=None):
 | |
|         form = super().get_form(form_class)
 | |
|         if self.request.method in ("POST", "PUT") \
 | |
|                 and (not self.request.user.is_authenticated or not self.request.user.registration.is_admin):
 | |
|             # Check that registrations are opened
 | |
|             now = timezone.now()
 | |
|             if now < settings.REGISTRATION_DATES['open']:
 | |
|                 form.add_error(None, format_lazy(_("Registrations are not opened yet. "
 | |
|                                                    "They will open on the {opening_date:%Y-%m-%d %H:%M}."),
 | |
|                                                  opening_date=settings.REGISTRATION_DATES['open']))
 | |
|             elif now > settings.REGISTRATION_DATES['close']:
 | |
|                 form.add_error(None, format_lazy(_("Registrations for this year are closed since "
 | |
|                                                    "{closing_date:%Y-%m-%d %H:%M}."),
 | |
|                                                  closing_date=settings.REGISTRATION_DATES['close']))
 | |
|         return form
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         role = form.cleaned_data["role"]
 | |
|         if role == "participant":
 | |
|             registration_form = StudentRegistrationForm(self.request.POST)
 | |
|         else:
 | |
|             registration_form = CoachRegistrationForm(self.request.POST)
 | |
|         del registration_form.fields["team"]
 | |
|         del registration_form.fields["email_confirmed"]
 | |
| 
 | |
|         if not registration_form.is_valid():
 | |
|             return self.form_invalid(form)
 | |
| 
 | |
|         ret = super().form_valid(form)
 | |
|         registration = registration_form.instance
 | |
|         registration.user = form.instance
 | |
|         registration.save()
 | |
|         registration.send_email_validation_link()
 | |
| 
 | |
|         return ret
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:email_validation_sent")
 | |
| 
 | |
| 
 | |
| class AddOrganizerView(VolunteerMixin, CreateView):
 | |
|     model = User
 | |
|     form_class = AddOrganizerForm
 | |
|     template_name = "registration/add_organizer.html"
 | |
|     extra_context = dict(title=_("Add organizer"))
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data()
 | |
| 
 | |
|         context["volunteer_registration_form"] = VolunteerRegistrationForm(self.request.POST or None)
 | |
|         del context["volunteer_registration_form"].fields["email_confirmed"]
 | |
|         if not self.request.user.registration.is_admin:
 | |
|             del context["volunteer_registration_form"].fields["admin"]
 | |
| 
 | |
|         return context
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         registration_form = VolunteerRegistrationForm(self.request.POST)
 | |
|         del registration_form.fields["email_confirmed"]
 | |
|         if not self.request.user.registration.is_admin:
 | |
|             del registration_form.fields["admin"]
 | |
| 
 | |
|         if not registration_form.is_valid():
 | |
|             return self.form_invalid(form)
 | |
| 
 | |
|         ret = super().form_valid(form)
 | |
|         registration = registration_form.instance
 | |
|         registration.user = form.instance
 | |
|         registration.save()
 | |
|         registration.send_email_validation_link()
 | |
| 
 | |
|         password = get_random_string(16)
 | |
|         form.instance.set_password(password)
 | |
|         form.instance.save()
 | |
| 
 | |
|         with translation.override(settings.PREFERRED_LANGUAGE_CODE):
 | |
|             subject = f"[{settings.APP_NAME}] " + str(_("New organizer account"))
 | |
|             site = Site.objects.first()
 | |
|             message = render_to_string('registration/mails/add_organizer.txt', dict(user=registration.user,
 | |
|                                                                                     inviter=self.request.user,
 | |
|                                                                                     password=password,
 | |
|                                                                                     domain=site.domain))
 | |
|             html = render_to_string('registration/mails/add_organizer.html', dict(user=registration.user,
 | |
|                                                                                   inviter=self.request.user,
 | |
|                                                                                   password=password,
 | |
|                                                                                   domain=site.domain))
 | |
|         registration.user.email_user(subject, message, html_message=html)
 | |
| 
 | |
|         if registration.is_admin:
 | |
|             registration.user.is_superuser = True
 | |
|             registration.user.save()
 | |
| 
 | |
|         return ret
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:email_validation_sent")
 | |
| 
 | |
| 
 | |
| class UserValidateView(TemplateView):
 | |
|     """
 | |
|     A view to validate the email address.
 | |
|     """
 | |
|     title = _("Email validation")
 | |
|     template_name = 'registration/email_validation_complete.html'
 | |
|     extra_context = dict(title=_("Validate email"))
 | |
| 
 | |
|     def get(self, *args, **kwargs):
 | |
|         """
 | |
|         With a given token and user id (in params), validate the email address.
 | |
|         """
 | |
|         assert 'uidb64' in kwargs and 'token' in kwargs
 | |
| 
 | |
|         self.validlink = False
 | |
|         user = self.get_user(kwargs['uidb64'])
 | |
|         token = kwargs['token']
 | |
| 
 | |
|         # Validate the token
 | |
|         if user is not None and email_validation_token.check_token(user, token):
 | |
|             self.validlink = True
 | |
|             user.registration.email_confirmed = True
 | |
|             user.registration.save()
 | |
|         return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400)
 | |
| 
 | |
|     def get_user(self, uidb64):
 | |
|         """
 | |
|         Get user from the base64-encoded string.
 | |
|         """
 | |
|         try:
 | |
|             # urlsafe_base64_decode() decodes to bytestring
 | |
|             uid = urlsafe_base64_decode(uidb64).decode()
 | |
|             user = User.objects.get(pk=uid)
 | |
|         except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
 | |
|             user = None
 | |
|         return user
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data(**kwargs)
 | |
|         context['user_object'] = self.get_user(self.kwargs["uidb64"])
 | |
|         context['login_url'] = resolve_url(settings.LOGIN_URL)
 | |
|         if self.validlink:
 | |
|             context['validlink'] = True
 | |
|         else:
 | |
|             context.update({
 | |
|                 'title': _('Email validation unsuccessful'),
 | |
|                 'validlink': False,
 | |
|             })
 | |
|         return context
 | |
| 
 | |
| 
 | |
| class UserValidationEmailSentView(TemplateView):
 | |
|     """
 | |
|     Display the information that the validation link has been sent.
 | |
|     """
 | |
|     template_name = 'registration/email_validation_email_sent.html'
 | |
|     extra_context = dict(title=_('Email validation email sent'))
 | |
| 
 | |
| 
 | |
| class UserResendValidationEmailView(LoginRequiredMixin, DetailView):
 | |
|     """
 | |
|     Rensend the email validation link.
 | |
|     """
 | |
|     model = User
 | |
|     extra_context = dict(title=_("Resend email validation link"))
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         user = self.get_object()
 | |
|         user.registration.send_email_validation_link()
 | |
|         return redirect('registration:email_validation_sent')
 | |
| 
 | |
| 
 | |
| class MyAccountDetailView(LoginRequiredMixin, RedirectView):
 | |
|     """
 | |
|     Redirect to our own profile detail page.
 | |
|     """
 | |
| 
 | |
|     def get_redirect_url(self, *args, **kwargs):
 | |
|         return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))
 | |
| 
 | |
| 
 | |
| class UserDetailView(LoginRequiredMixin, DetailView):
 | |
|     """
 | |
|     Display the detail about a user.
 | |
|     """
 | |
| 
 | |
|     model = User
 | |
|     context_object_name = "user_object"
 | |
|     template_name = "registration/user_detail.html"
 | |
| 
 | |
|     def dispatch(self, request, *args, **kwargs):
 | |
|         me = request.user
 | |
|         if not me.is_authenticated:
 | |
|             return self.handle_no_permission()
 | |
|         user = self.get_object()
 | |
|         if user == me or me.registration.is_admin or me.registration.is_volunteer \
 | |
|                 and user.registration.participates and user.registration.team \
 | |
|                 and (user.registration.team.participation.tournament in me.registration.organized_tournaments.all()
 | |
|                      or user.registration.team.participation.final
 | |
|                      and Tournament.final_tournament() in me.registration.organized_tournaments.all()) \
 | |
|                 or user.registration.is_volunteer and me.registration.is_volunteer \
 | |
|                 and me.registration.interesting_tournaments.intersection(user.registration.interesting_tournaments):
 | |
|             return super().dispatch(request, *args, **kwargs)
 | |
|         raise PermissionDenied
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data(**kwargs)
 | |
|         context["title"] = _("Detail of user {user}").format(user=str(self.object.registration))
 | |
|         return context
 | |
| 
 | |
| 
 | |
| class UserListView(VolunteerMixin, SingleTableView):
 | |
|     """
 | |
|     Display the list of all registered users.
 | |
|     """
 | |
|     model = Registration
 | |
|     table_class = RegistrationTable
 | |
|     template_name = "registration/user_list.html"
 | |
| 
 | |
| 
 | |
| class UserUpdateView(UserMixin, UpdateView):
 | |
|     """
 | |
|     Update the detail about a user and its registration.
 | |
|     """
 | |
|     model = User
 | |
|     context_object_name = "user_object"
 | |
|     form_class = UserForm
 | |
|     template_name = "registration/update_user.html"
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data(**kwargs)
 | |
|         user = self.get_object()
 | |
|         context["title"] = _("Update user {user}").format(user=str(self.object.registration))
 | |
|         context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
 | |
|                                                                     instance=self.object.registration)
 | |
|         if not self.request.user.registration.is_admin:
 | |
|             if "team" in context["registration_form"].fields:
 | |
|                 del context["registration_form"].fields["team"]
 | |
|             if "admin" in context["registration_form"].fields:
 | |
|                 del context["registration_form"].fields["admin"]
 | |
|             del context["registration_form"].fields["email_confirmed"]
 | |
|         return context
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         user = form.instance
 | |
|         registration_form = user.registration.form_class(data=self.request.POST or None,
 | |
|                                                          instance=self.object.registration)
 | |
|         if not self.request.user.registration.is_admin:
 | |
|             if "team" in registration_form.fields:
 | |
|                 del registration_form.fields["team"]
 | |
|             if "admin" in registration_form.fields:
 | |
|                 del registration_form.fields["admin"]
 | |
|             del registration_form.fields["email_confirmed"]
 | |
| 
 | |
|         if not registration_form.is_valid():
 | |
|             return self.form_invalid(form)
 | |
| 
 | |
|         registration_form.save()
 | |
|         return super().form_valid(form)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:user_detail", args=(self.object.pk,))
 | |
| 
 | |
| 
 | |
| class UserUploadPhotoAuthorizationView(UserRegistrationMixin, UpdateView):
 | |
|     """
 | |
|     A participant can send its photo authorization.
 | |
|     """
 | |
|     model = ParticipantRegistration
 | |
|     template_name = "registration/upload_photo_authorization.html"
 | |
|     extra_context = dict(title=_("Upload photo authorization"))
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data(**kwargs)
 | |
|         if self.object.team:
 | |
|             tournament = self.object.team.participation.tournament \
 | |
|                 if 'final' not in self.request.path else Tournament.final_tournament()
 | |
|             context["tournament"] = tournament
 | |
|         return context
 | |
| 
 | |
|     def get_form_class(self):
 | |
|         return PhotoAuthorizationForm if 'final' not in self.request.path else PhotoAuthorizationFinalForm
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         old_instance: ParticipantRegistration = ParticipantRegistration.objects.get(pk=self.object.pk)
 | |
|         old_field = old_instance.photo_authorization \
 | |
|             if 'final' not in self.request.path else old_instance.photo_authorization_final
 | |
|         if old_field:
 | |
|             old_field.delete()
 | |
|             old_instance.save()
 | |
|         return super().form_valid(form)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
 | |
| 
 | |
| 
 | |
| class UserUploadHealthSheetView(UserRegistrationMixin, UpdateView):
 | |
|     """
 | |
|     A participant can send its health sheet.
 | |
|     """
 | |
|     model = StudentRegistration
 | |
|     form_class = HealthSheetForm
 | |
|     template_name = "registration/upload_health_sheet.html"
 | |
|     extra_context = dict(title=_("Upload health sheet"))
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         old_instance = StudentRegistration.objects.get(pk=self.object.pk)
 | |
|         if old_instance.health_sheet:
 | |
|             old_instance.health_sheet.delete()
 | |
|             old_instance.save()
 | |
|         return super().form_valid(form)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
 | |
| 
 | |
| 
 | |
| class UserUploadVaccineSheetView(UserRegistrationMixin, UpdateView):
 | |
|     """
 | |
|     A participant can send its vaccine sheet.
 | |
|     """
 | |
|     model = StudentRegistration
 | |
|     form_class = VaccineSheetForm
 | |
|     template_name = "registration/upload_vaccine_sheet.html"
 | |
|     extra_context = dict(title=_("Upload vaccine sheet"))
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         old_instance = StudentRegistration.objects.get(pk=self.object.pk)
 | |
|         if old_instance.vaccine_sheet:
 | |
|             old_instance.vaccine_sheet.delete()
 | |
|             old_instance.save()
 | |
|         return super().form_valid(form)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
 | |
| 
 | |
| 
 | |
| class UserUploadParentalAuthorizationView(UserRegistrationMixin, UpdateView):
 | |
|     """
 | |
|     A participant can send its parental authorization.
 | |
|     """
 | |
|     model = StudentRegistration
 | |
|     template_name = "registration/upload_parental_authorization.html"
 | |
|     extra_context = dict(title=_("Upload parental authorization"))
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data(**kwargs)
 | |
|         if self.object.team:
 | |
|             tournament = self.object.team.participation.tournament \
 | |
|                 if 'final' not in self.request.path else Tournament.final_tournament()
 | |
|             context["tournament"] = tournament
 | |
|         return context
 | |
| 
 | |
|     def get_form_class(self):
 | |
|         return ParentalAuthorizationForm if 'final' not in self.request.path else ParentalAuthorizationFinalForm
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def form_valid(self, form):
 | |
|         old_instance: StudentRegistration = StudentRegistration.objects.get(pk=self.object.pk)
 | |
|         old_field = old_instance.parental_authorization \
 | |
|             if 'final' not in self.request.path else old_instance.parental_authorization_final
 | |
|         if old_field:
 | |
|             old_field.delete()
 | |
|             old_instance.save()
 | |
|         return super().form_valid(form)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
 | |
| 
 | |
| 
 | |
| class AuthorizationTemplateView(TemplateView):
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data(**kwargs)
 | |
| 
 | |
|         translation.activate("fr")
 | |
| 
 | |
|         if "registration_id" in self.request.GET:
 | |
|             registration = Registration.objects.get(pk=self.request.GET.get("registration_id"))
 | |
|             # Don't get unwanted information
 | |
|             if registration.user == self.request.user \
 | |
|                     or self.request.user.is_authenticated and self.request.user.registration.is_admin:
 | |
|                 context["registration"] = registration
 | |
|         if "tournament_id" in self.request.GET and self.request.GET.get("tournament_id").isnumeric():
 | |
|             if not Tournament.objects.filter(pk=self.request.GET.get("tournament_id")).exists():
 | |
|                 raise PermissionDenied("Ce tournoi n'existe pas.")
 | |
|             context["tournament"] = Tournament.objects.get(pk=self.request.GET.get("tournament_id"))
 | |
|         elif "tournament_name" in self.request.GET:
 | |
|             if not Tournament.objects.filter(name__iexact=self.request.GET.get("tournament_name")).exists():
 | |
|                 raise PermissionDenied("Ce tournoi n'existe pas.")
 | |
|             context["tournament"] = Tournament.objects.get(name__iexact=self.request.GET.get("tournament_name"))
 | |
|         elif settings.SINGLE_TOURNAMENT:
 | |
|             # One single tournament (for ETEAM)
 | |
|             context["tournament"] = Tournament.objects.first()
 | |
|         else:
 | |
|             raise PermissionDenied("Merci d'indiquer un tournoi.")
 | |
| 
 | |
|         return context
 | |
| 
 | |
|     def render_to_response(self, context, **response_kwargs):
 | |
|         template_name = self.get_template_names()[0]
 | |
|         with translation.override(settings.PREFERRED_LANGUAGE_CODE):
 | |
|             tex = render_to_string(template_name, context=context, request=self.request)
 | |
|         temp_dir = mkdtemp()
 | |
|         with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
 | |
|             f.write(tex)
 | |
|         process = subprocess.Popen(["pdflatex", "-interaction=nonstopmode", f"-output-directory={temp_dir}",
 | |
|                                     os.path.join(temp_dir, "texput.tex"), ])
 | |
|         process.wait()
 | |
|         return FileResponse(open(os.path.join(temp_dir, "texput.pdf"), "rb"),
 | |
|                             content_type="application/pdf",
 | |
|                             filename=template_name.split("/")[-1][:-3] + "pdf")
 | |
| 
 | |
| 
 | |
| class AdultPhotoAuthorizationTemplateView(AuthorizationTemplateView):
 | |
|     def get_template_names(self):
 | |
|         if settings.TFJM_APP == "TFJM":
 | |
|             return ["registration/tex/Autorisation_droit_image_majeur.tex"]
 | |
|         elif settings.TFJM_APP == "ETEAM":
 | |
|             return ["registration/tex/photo_authorization_eteam_adult.tex"]
 | |
| 
 | |
| 
 | |
| class ChildPhotoAuthorizationTemplateView(AuthorizationTemplateView):
 | |
|     def get_template_names(self):
 | |
|         if settings.TFJM_APP == "TFJM":
 | |
|             return ["registration/tex/Autorisation_droit_image_mineur.tex"]
 | |
|         elif settings.TFJM_APP == "ETEAM":
 | |
|             return ["registration/tex/photo_authorization_eteam_child.tex"]
 | |
| 
 | |
| 
 | |
| class ParentalAuthorizationTemplateView(AuthorizationTemplateView):
 | |
|     template_name = "registration/tex/Autorisation_parentale.tex"
 | |
| 
 | |
|     def get_template_names(self):
 | |
|         if settings.TFJM_APP == "TFJM":
 | |
|             return ["registration/tex/Autorisation_parentale.tex"]
 | |
|         elif settings.TFJM_APP == "ETEAM":
 | |
|             return ["registration/tex/parental_authorization_eteam.tex"]
 | |
| 
 | |
| 
 | |
| class InstructionsTemplateView(AuthorizationTemplateView):
 | |
|     template_name = "registration/tex/Instructions.tex"
 | |
| 
 | |
| 
 | |
| class PaymentUpdateView(LoginRequiredMixin, UpdateView):
 | |
|     model = Payment
 | |
|     form_class = PaymentAdminForm
 | |
| 
 | |
|     def dispatch(self, request, *args, **kwargs):
 | |
|         user = self.request.user
 | |
|         object = self.get_object()
 | |
|         if not user.is_authenticated or \
 | |
|                 not user.registration.is_admin \
 | |
|                 and (user.registration.is_volunteer and user.registration not in object.tournament.organizers.all()
 | |
|                      or user.registration.is_student and user.registration not in object.registrations.all()
 | |
|                      or user.registration.is_coach and user.registration.team != object.team):
 | |
|             return self.handle_no_permission()
 | |
|         return super().dispatch(request, *args, **kwargs)
 | |
| 
 | |
|     def get_context_data(self, **kwargs):
 | |
|         context = super().get_context_data()
 | |
|         context['title'] = _("Update payment")
 | |
|         # Grouping is only possible if there isn't any validated payment in the team
 | |
|         context['can_group'] = all(p.valid is False for reg in self.object.team.students.all()
 | |
|                                    for p in reg.payments.filter(final=self.object.final).all())
 | |
|         context['bank_transfer_form'] = PaymentForm(payment_type='bank_transfer',
 | |
|                                                     data=self.request.POST or None,
 | |
|                                                     instance=self.object)
 | |
| 
 | |
|         if not self.object.grouped:
 | |
|             context['scholarship_form'] = PaymentForm(payment_type='scholarship',
 | |
|                                                       data=self.request.POST or None,
 | |
|                                                       instance=self.object)
 | |
| 
 | |
|         context['other_form'] = PaymentForm(payment_type='other',
 | |
|                                             data=self.request.POST or None,
 | |
|                                             instance=self.object)
 | |
|         return context
 | |
| 
 | |
|     def form_valid(self, form):
 | |
|         old_instance = Payment.objects.get(pk=self.object.pk)
 | |
|         if self.request.user.registration.participates:
 | |
|             if old_instance.valid is not False:
 | |
|                 raise PermissionDenied(_("This payment is already valid or pending validation."))
 | |
|         if old_instance.valid is False:
 | |
|             form.instance.valid = None
 | |
|         if old_instance.receipt:
 | |
|             old_instance.receipt.delete()
 | |
|             old_instance.save()
 | |
|         return super().form_valid(form)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         return reverse_lazy("participation:team_detail", args=(self.object.registrations.first().team.pk,))
 | |
| 
 | |
| 
 | |
| class PaymentUpdateGroupView(LoginRequiredMixin, DetailView):
 | |
|     model = Payment
 | |
| 
 | |
|     def dispatch(self, request, *args, **kwargs):
 | |
|         payment = self.get_object()
 | |
| 
 | |
|         if not self.request.user.is_authenticated or \
 | |
|                 not self.request.user.registration.is_admin \
 | |
|                 and (self.request.user.registration not in self.get_object().registrations.all()
 | |
|                      or payment.valid is not False):
 | |
|             return self.handle_no_permission()
 | |
| 
 | |
|         if any(p.valid is not False for reg in payment.team.students.all()
 | |
|                for p in reg.payments.filter(final=payment.final).all()):
 | |
|             raise PermissionDenied(_("Since one payment is already validated, or pending validation, "
 | |
|                                      "grouping is not possible."))
 | |
|         return super().dispatch(request, *args, **kwargs)
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         payment = self.get_object()
 | |
| 
 | |
|         if payment.valid is not False:
 | |
|             raise PermissionDenied(_("This payment is already valid or pending validation."))
 | |
| 
 | |
|         if payment.grouped:
 | |
|             registrations = list(payment.registrations.all())
 | |
|             first_reg = registrations[0]
 | |
|             payment.registrations.set([first_reg])
 | |
|             payment.grouped = False
 | |
|             tournament = first_reg.team.participation.tournament if not payment.final else Tournament.final_tournament()
 | |
|             payment.amount = tournament.price
 | |
|             payment.checkout_intent_id = None
 | |
|             payment.save()
 | |
|             for registration in registrations[1:]:
 | |
|                 p = Payment.objects.create(type=payment.type,
 | |
|                                            grouped=False,
 | |
|                                            final=payment.final,
 | |
|                                            amount=tournament.price,
 | |
|                                            receipt=payment.receipt,
 | |
|                                            additional_information=payment.additional_information)
 | |
|                 p.registrations.set([registration])
 | |
|                 p.save()
 | |
|         else:
 | |
|             reg = payment.registrations.get()
 | |
|             tournament = reg.team.participation.tournament if not payment.final else Tournament.final_tournament()
 | |
|             for student in reg.team.students.all():
 | |
|                 if student != reg:
 | |
|                     Payment.objects.filter(registrations=student, final=payment.final).delete()
 | |
|                 payment.registrations.add(student)
 | |
|             payment.amount = tournament.price * reg.team.students.count()
 | |
|             payment.grouped = True
 | |
|             payment.checkout_intent_id = None
 | |
|             payment.save()
 | |
| 
 | |
|         return redirect(reverse_lazy("registration:update_payment", args=(payment.pk,)))
 | |
| 
 | |
| 
 | |
| class PaymentRedirectHelloAssoView(AccessMixin, DetailView):
 | |
|     model = Payment
 | |
| 
 | |
|     def dispatch(self, request, *args, **kwargs):
 | |
|         payment = self.get_object()
 | |
| 
 | |
|         # An external user has the link for the payment
 | |
|         token = request.GET.get('token', "")
 | |
|         if token and token == payment.token:
 | |
|             return super().dispatch(request, *args, **kwargs)
 | |
| 
 | |
|         if not request.user.is_authenticated:
 | |
|             return self.handle_no_permission()
 | |
| 
 | |
|         if not request.user.registration.is_admin:
 | |
|             if request.user.registration.is_volunteer \
 | |
|                     and payment.tournament not in request.user.registration.organized_tournaments.all():
 | |
|                 return self.handle_no_permission()
 | |
| 
 | |
|             if request.user.registration.is_student \
 | |
|                     and request.user.registration not in payment.registrations.all():
 | |
|                 return self.handle_no_permission()
 | |
| 
 | |
|             if request.user.registration.is_coach \
 | |
|                     and request.user.registration.team != payment.team:
 | |
|                 return self.handle_no_permission()
 | |
| 
 | |
|         return super().dispatch(request, *args, **kwargs)
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         payment = self.get_object()
 | |
|         if payment.valid is not False:
 | |
|             raise PermissionDenied(_("The payment is already valid or pending validation."))
 | |
| 
 | |
|         checkout_intent = payment.create_checkout_intent()
 | |
|         return redirect(checkout_intent["redirectUrl"])
 | |
| 
 | |
| 
 | |
| class PaymentHelloAssoReturnView(DetailView):
 | |
|     model = Payment
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         checkout_id = request.GET.get("checkoutIntentId")
 | |
|         payment = self.get_object()
 | |
|         payment_qs = Payment.objects.exclude(valid=True).filter(checkout_intent_id=checkout_id).filter(pk=payment.pk)
 | |
|         if not payment_qs.exists():
 | |
|             messages.error(request, _("The payment is not found or is already validated."), "danger")
 | |
|             return redirect("index")
 | |
| 
 | |
|         team = payment.team
 | |
|         tournament = payment.tournament
 | |
|         right_to_see = not request.user.is_anonymous \
 | |
|             and (request.user.registration.is_admin
 | |
|                  or request.user.registration in payment.registrations.all()
 | |
|                  or (request.user.registration.is_volunteer
 | |
|                      and tournament in request.user.registration.organized_tournaments.all())
 | |
|                  or (request.user.registration.is_coach and request.user.registration.team == team))
 | |
| 
 | |
|         if right_to_see:
 | |
|             error_response = redirect("registration:update_payment", pk=payment.pk)
 | |
|         else:
 | |
|             error_response = redirect("index")
 | |
| 
 | |
|         return_type = request.GET.get("type")
 | |
|         if return_type == "error":
 | |
|             messages.error(request, format_lazy(_("An error occurred during the payment: {error}"),
 | |
|                                                 error=request.GET.get("error")), "danger")
 | |
|             return error_response
 | |
|         elif return_type == "return":
 | |
|             code = request.GET.get("code")
 | |
|             if code == "refused":
 | |
|                 messages.error(request, _("The payment has been refused."), "danger")
 | |
|                 return error_response
 | |
|             elif code != "succeeded":
 | |
|                 messages.error(request, format_lazy(_("The return code is unknown: {code}"), code=code), "danger")
 | |
|                 return error_response
 | |
|         else:
 | |
|             messages.error(request, format_lazy(_("The return type is unknown: {type}"), type=return_type), "danger")
 | |
|             return error_response
 | |
| 
 | |
|         checkout_intent = payment.get_checkout_intent()
 | |
|         if 'order' in checkout_intent:
 | |
|             payment.type = "helloasso"
 | |
|             payment.valid = True
 | |
|             payment.additional_information = json.dumps(checkout_intent['order'])
 | |
|             payment.save()
 | |
|             messages.success(request, _("The payment has been successfully validated! "
 | |
|                                         "Your registration is now complete."))
 | |
|             payment.send_helloasso_payment_confirmation_mail()
 | |
|         else:
 | |
|             payment.type = "helloasso"
 | |
|             payment.valid = None
 | |
|             payment.save()
 | |
|             messages.success(request, _("Your payment is done! "
 | |
|                                         "The validation of your payment may takes a few minutes, "
 | |
|                                         "and will be automatically done. "
 | |
|                                         "If it is not the case, please contact us."))
 | |
| 
 | |
|         if right_to_see:
 | |
|             return redirect("participation:team_detail", pk=team.pk)
 | |
|         else:
 | |
|             return redirect("index")
 | |
| 
 | |
| 
 | |
| class PhotoAuthorizationView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent photo authorization.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/authorization/photo/{filename}"
 | |
|         student_qs = ParticipantRegistration.objects.filter(Q(photo_authorization__endswith=filename)
 | |
|                                                             | Q(photo_authorization_final__endswith=filename))
 | |
|         if not os.path.exists(path) or not student_qs.exists():
 | |
|             raise Http404
 | |
|         student = student_qs.get()
 | |
|         user = request.user
 | |
|         if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
 | |
|                 and student.team.participation.tournament in user.registration.organized_tournaments.all()):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         true_file_name = _("Photo authorization of {student}.{ext}").format(student=str(student), ext=ext)
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class HealthSheetView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent health sheet.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/authorization/health/{filename}"
 | |
|         if not os.path.exists(path):
 | |
|             raise Http404
 | |
|         student = StudentRegistration.objects.get(health_sheet__endswith=filename)
 | |
|         user = request.user
 | |
|         if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
 | |
|                 and student.team.participation.tournament in user.registration.organized_tournaments.all()):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         true_file_name = _("Health sheet of {student}.{ext}").format(student=str(student), ext=ext)
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class VaccineSheetView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent health sheet.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/authorization/vaccine/{filename}"
 | |
|         if not os.path.exists(path):
 | |
|             raise Http404
 | |
|         student = StudentRegistration.objects.get(vaccine_sheet__endswith=filename)
 | |
|         user = request.user
 | |
|         if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
 | |
|                 and student.team.participation.tournament in user.registration.organized_tournaments.all()):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         true_file_name = _("Vaccine sheet of {student}.{ext}").format(student=str(student), ext=ext)
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class ParentalAuthorizationView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent parental authorization.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/authorization/parental/{filename}"
 | |
|         if not os.path.exists(path):
 | |
|             raise Http404
 | |
|         student = StudentRegistration.objects.get(Q(parental_authorization__endswith=filename)
 | |
|                                                   | Q(parental_authorization_final__endswith=filename))
 | |
|         user = request.user
 | |
|         if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
 | |
|                 and student.team.participation.tournament in user.registration.organized_tournaments.all()):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         true_file_name = _("Parental authorization of {student}.{ext}").format(student=str(student), ext=ext)
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class ReceiptView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent payment receipt or scholarship notification.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/authorization/receipt/{filename}"
 | |
|         if not os.path.exists(path):
 | |
|             raise Http404
 | |
|         payment = Payment.objects.get(receipt__endswith=filename)
 | |
|         user = request.user
 | |
|         if not (user.registration in payment.registrations.all() or user.registration.is_admin):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         registrations = ", ".join(str(registration) for registration in payment.registrations.all())
 | |
|         true_file_name = _("Payment receipt of {registrations}.{ext}").format(registrations=registrations, ext=ext)
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class SolutionView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent solution.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/solutions/{filename}"
 | |
|         if not os.path.exists(path):
 | |
|             raise Http404
 | |
|         solution = Solution.objects.get(file__endswith=filename)
 | |
|         user = request.user
 | |
|         if user.registration.participates and user.registration.team.participation:
 | |
|             passage_participant_qs = Passage.objects.filter(Q(reporter=user.registration.team.participation)
 | |
|                                                             | Q(opponent=user.registration.team.participation)
 | |
|                                                             | Q(reviewer=user.registration.team.participation)
 | |
|                                                             | Q(observer=user.registration.team.participation),
 | |
|                                                             reporter=solution.participation,
 | |
|                                                             solution_number=solution.problem)
 | |
|         else:
 | |
|             passage_participant_qs = Passage.objects.none()
 | |
|         if not (user.registration.is_admin
 | |
|                 or (user.registration.is_volunteer
 | |
|                     and user.registration in solution.tournament.organizers.all())
 | |
|                 or (user.registration.is_volunteer
 | |
|                     and user.registration.pools_presided.filter(tournament=solution.tournament).exists())
 | |
|                 or user.registration.is_volunteer
 | |
|                 and Passage.objects.filter(Q(pool__juries=user.registration)
 | |
|                                            | Q(pool__tournament__in=user.registration.organized_tournaments.all()),
 | |
|                                            reporter=solution.participation,
 | |
|                                            solution_number=solution.problem).exists()
 | |
|                 or user.registration.participates and user.registration.team
 | |
|                 and (solution.participation.team == user.registration.team or
 | |
|                      any(passage.pool.round == 1
 | |
|                          or (passage.pool.round == 2 and passage.pool.tournament.solutions_available_second_phase)
 | |
|                          or (passage.pool.round == 3 and passage.pool.tournament.solutions_available_third_phase)
 | |
|                          for passage in passage_participant_qs.all()))):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         true_file_name = str(solution) + f".{ext}"
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class WrittenReviewView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Display the sent written reviews.
 | |
|     """
 | |
| 
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         filename = kwargs["filename"]
 | |
|         path = f"media/reviews/{filename}"
 | |
|         if not os.path.exists(path):
 | |
|             raise Http404
 | |
|         review = WrittenReview.objects.get(file__endswith=filename)
 | |
|         user = request.user
 | |
|         if not (user.registration.is_admin or user.registration.is_volunteer
 | |
|                 and (user.registration in review.passage.pool.juries.all()
 | |
|                      or user.registration in review.passage.pool.tournament.organizers.all()
 | |
|                      or user.registration.pools_presided.filter(tournament=review.passage.pool.tournament).exists())
 | |
|                 or user.registration.participates and user.registration.team == review.participation.team):
 | |
|             raise PermissionDenied
 | |
|         # Guess mime type of the file
 | |
|         mime = Magic(mime=True)
 | |
|         mime_type = mime.from_file(path)
 | |
|         ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | |
|         # Replace file name
 | |
|         true_file_name = str(review) + f".{ext}"
 | |
|         return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
 | |
| 
 | |
| 
 | |
| class UserImpersonateView(LoginRequiredMixin, RedirectView):
 | |
|     """
 | |
|     An administrator can log in through this page as someone else, and act as this other person.
 | |
|     """
 | |
| 
 | |
|     def dispatch(self, request, *args, **kwargs):
 | |
|         if self.request.user.registration.is_admin:
 | |
|             if not User.objects.filter(pk=kwargs["pk"]).exists():
 | |
|                 raise Http404
 | |
|             session = request.session
 | |
|             session["admin"] = request.user.pk
 | |
|             session["_fake_user_id"] = kwargs["pk"]
 | |
|         return super().dispatch(request, *args, **kwargs)
 | |
| 
 | |
|     def get_redirect_url(self, *args, **kwargs):
 | |
|         return reverse_lazy("registration:user_detail", args=(kwargs["pk"],))
 | |
| 
 | |
| 
 | |
| class ResetAdminView(LoginRequiredMixin, View):
 | |
|     """
 | |
|     Return to admin view, clear the session field that let an administrator to log in as someone else.
 | |
|     """
 | |
| 
 | |
|     def dispatch(self, request, *args, **kwargs):
 | |
|         user = request.user
 | |
|         if not user.is_authenticated:
 | |
|             return self.handle_no_permission()
 | |
|         if "_fake_user_id" in request.session:
 | |
|             del request.session["_fake_user_id"]
 | |
|         return redirect(request.GET.get("path", reverse_lazy("index")))
 |