# Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date from django.contrib.sites.models import Site from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.template import loader from django.urls import reverse_lazy from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from polymorphic.models import PolymorphicModel from tfjm.tokens import email_validation_token class Registration(PolymorphicModel): """ Registrations store extra content that are not asked in the User Model. This is specific to the role of the user, see StudentRegistration, CoachRegistration or VolunteerRegistration. """ user = models.OneToOneField( "auth.User", on_delete=models.CASCADE, verbose_name=_("user"), ) give_contact_to_animath = models.BooleanField( default=False, verbose_name=_("Grant Animath to contact me in the future about other actions"), ) email_confirmed = models.BooleanField( default=False, verbose_name=_("email confirmed"), ) def send_email_validation_link(self): """ The account got created or the email got changed. Send an email that contains a link to validate the address. """ subject = "[TFJM²] " + str(_("Activate your TFJM² account")) token = email_validation_token.make_token(self.user) uid = urlsafe_base64_encode(force_bytes(self.user.pk)) site = Site.objects.first() message = loader.render_to_string('registration/mails/email_validation_email.txt', { 'user': self.user, 'domain': site.domain, 'token': token, 'uid': uid, }) html = loader.render_to_string('registration/mails/email_validation_email.html', { 'user': self.user, 'domain': site.domain, 'token': token, 'uid': uid, }) self.user.email_user(subject, message, html_message=html) @property def type(self): # pragma: no cover raise NotImplementedError @property def form_class(self): # pragma: no cover raise NotImplementedError @property def participates(self): return isinstance(self, ParticipantRegistration) @property def is_admin(self): return isinstance(self, VolunteerRegistration) and self.admin or self.user.is_superuser @property def is_volunteer(self): return isinstance(self, VolunteerRegistration) def get_absolute_url(self): return reverse_lazy("registration:user_detail", args=(self.user_id,)) def __str__(self): return f"{self.user.first_name} {self.user.last_name}" class Meta: verbose_name = _("registration") verbose_name_plural = _("registrations") def get_random_photo_filename(instance, filename): return "authorization/photo/" + get_random_string(64) def get_random_health_filename(instance, filename): return "authorization/health/" + get_random_string(64) def get_random_vaccine_filename(instance, filename): return "authorization/vaccine/" + get_random_string(64) def get_random_parental_filename(instance, filename): return "authorization/parental/" + get_random_string(64) class ParticipantRegistration(Registration): team = models.ForeignKey( "participation.Team", related_name="participants", on_delete=models.PROTECT, blank=True, null=True, default=None, verbose_name=_("team"), ) gender = models.CharField( max_length=6, verbose_name=_("gender"), choices=[ ("female", _("Female")), ("male", _("Male")), ("other", _("Other")), ], default="other", ) address = models.CharField( max_length=255, verbose_name=_("address"), ) zip_code = models.PositiveIntegerField( verbose_name=_("zip code"), validators=[MinValueValidator(1000), MaxValueValidator(99999)], ) city = models.CharField( max_length=255, verbose_name=_("city"), ) phone_number = PhoneNumberField( verbose_name=_("phone number"), blank=True, ) health_issues = models.TextField( verbose_name=_("health issues"), blank=True, help_text=_("You can indicate here your allergies or anything that is important to know for organizers"), ) photo_authorization = models.FileField( verbose_name=_("photo authorization"), upload_to=get_random_photo_filename, blank=True, default="", ) @property def under_18(self): if isinstance(self, CoachRegistration): return False # In normal case important_date = timezone.now().date() if self.team and self.team.participation.tournament: important_date = self.team.participation.tournament.date_start if self.team.participation.final: from participation.models import Tournament important_date = Tournament.final_tournament().date_start return (important_date - self.birth_date).days < 18 * 365.24 @property def type(self): # pragma: no cover raise NotImplementedError @property def form_class(self): # pragma: no cover raise NotImplementedError class Meta: verbose_name = _("participant registration") verbose_name_plural = _("participant registrations") class StudentRegistration(ParticipantRegistration): """ Specific registration for students. They have a team, a student class and a school. """ birth_date = models.DateField( verbose_name=_("birth date"), default=date.today, ) student_class = models.IntegerField( choices=[ (12, _("12th grade")), (11, _("11th grade")), (10, _("10th grade or lower")), ], verbose_name=_("student class"), ) school = models.CharField( max_length=255, verbose_name=_("school"), ) responsible_name = models.CharField( max_length=255, verbose_name=_("responsible name"), default="", ) responsible_phone = PhoneNumberField( verbose_name=_("responsible phone number"), default="", ) responsible_email = models.EmailField( verbose_name=_("responsible email address"), default="", ) parental_authorization = models.FileField( verbose_name=_("parental authorization"), upload_to=get_random_parental_filename, blank=True, default="", ) health_sheet = models.FileField( verbose_name=_("health sheet"), upload_to=get_random_health_filename, blank=True, default="", ) vaccine_sheet = models.FileField( verbose_name=_("vaccine sheet"), upload_to=get_random_vaccine_filename, blank=True, default="", ) @property def type(self): return _("student") @property def form_class(self): from registration.forms import StudentRegistrationForm return StudentRegistrationForm class Meta: verbose_name = _("student registration") verbose_name_plural = _("student registrations") class CoachRegistration(ParticipantRegistration): """ Specific registration for coaches. They have a team and a professional activity. """ professional_activity = models.TextField( verbose_name=_("professional activity"), ) @property def type(self): return _("coach") @property def form_class(self): from registration.forms import CoachRegistrationForm return CoachRegistrationForm class Meta: verbose_name = _("coach registration") verbose_name_plural = _("coach registrations") class VolunteerRegistration(Registration): """ Specific registration for organizers and juries. """ professional_activity = models.TextField( verbose_name=_("professional activity"), ) admin = models.BooleanField( verbose_name=_("administrator"), help_text=_("An administrator has all rights. Please don't give this right to all juries and volunteers."), default=False, ) @property def interesting_tournaments(self) -> set: return set(self.organized_tournaments.all()).union(map(lambda pool: pool.tournament, self.jury_in.all())) @property def type(self): return _('admin') if self.is_admin else _('volunteer') @property def form_class(self): from registration.forms import VolunteerRegistrationForm return VolunteerRegistrationForm class Meta: verbose_name = _("volunteer registration") verbose_name_plural = _("volunteer registrations") def get_scholarship_filename(instance, filename): return f"authorization/scholarship/scholarship_{instance.registration.pk}" class Payment(models.Model): registration = models.OneToOneField( ParticipantRegistration, on_delete=models.CASCADE, related_name="payment", verbose_name=_("registration"), ) type = models.CharField( verbose_name=_("type"), max_length=16, choices=[ ('', _("No payment")), ('helloasso', "Hello Asso"), ('scholarship', _("Scholarship")), ('bank_transfer', _("Bank transfer")), ('other', _("Other (please indicate)")), ('free', _("The tournament is free")), ], blank=True, default="", ) scholarship_file = models.FileField( verbose_name=_("scholarship file"), help_text=_("only if you have a scholarship."), upload_to=get_scholarship_filename, blank=True, default="", ) additional_information = models.TextField( verbose_name=_("additional information"), help_text=_("To help us to find your payment."), blank=True, default="", ) valid = models.BooleanField( verbose_name=_("payment valid"), null=True, default=False, ) def get_absolute_url(self): return reverse_lazy("registration:user_detail", args=(self.registration.user.id,)) def __str__(self): return _("Payment of {registration}").format(registration=self.registration) class Meta: verbose_name = _("payment") verbose_name_plural = _("payments")