mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 16:42:28 +01:00 
			
		
		
		
	Implement final selection
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
		
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,11 +1,8 @@
 | 
			
		||||
# Copyright (C) 2020 by Animath
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import csv
 | 
			
		||||
import math
 | 
			
		||||
from io import StringIO
 | 
			
		||||
import re
 | 
			
		||||
from typing import Iterable
 | 
			
		||||
 | 
			
		||||
from crispy_forms.helper import FormHelper
 | 
			
		||||
from crispy_forms.layout import Div, Field, Submit
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,15 @@ def create_payments(instance: Participation, created, raw, **_):
 | 
			
		||||
                else:
 | 
			
		||||
                    payment = Payment.objects.create(final=True)
 | 
			
		||||
                    payment.registrations.add(student)
 | 
			
		||||
 | 
			
		||||
                    payment_regional = Payment.objects.get(registrations=student, final=False)
 | 
			
		||||
                    if payment_regional.type == 'scholarship':
 | 
			
		||||
                        payment.type = 'scholarship'
 | 
			
		||||
                        with open(payment_regional.receipt.path, 'rb') as f:
 | 
			
		||||
                            payment.receipt.save(payment_regional.receipt.name, f)
 | 
			
		||||
                        payment.additional_information = payment_regional.additional_information
 | 
			
		||||
                        payment.fee = 0
 | 
			
		||||
                        payment.valid = payment_regional.valid
 | 
			
		||||
                    payment.save()
 | 
			
		||||
                payment.amount = Tournament.final_tournament().price
 | 
			
		||||
                if payment.amount == 0:
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,19 @@
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </dd>
 | 
			
		||||
 | 
			
		||||
            {% if team.participation.final %}
 | 
			
		||||
                <dt class="col-sm-6 text-end">{% trans "Photo authorizations (final):" %}</dt>
 | 
			
		||||
                <dd class="col-sm-6">
 | 
			
		||||
                    {% for participant in team.participants.all %}
 | 
			
		||||
                        {% if participant.photo_authorization_final %}
 | 
			
		||||
                            <a href="{{ participant.photo_authorization_final.url }}">{{ participant }}</a>{% if not forloop.last %},{% endif %}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            {{ participant }} ({% trans "Not uploaded yet" %}){% if not forloop.last %},{% endif %}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </dd>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
            {% if not team.participation.tournament.remote  %}
 | 
			
		||||
                <dt class="col-sm-6 text-end">{% trans "Health sheets:" %}</dt>
 | 
			
		||||
                <dd class="col-sm-6">
 | 
			
		||||
@@ -99,6 +112,21 @@
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </dd>
 | 
			
		||||
 | 
			
		||||
                {% if team.participation.final %}
 | 
			
		||||
                    <dt class="col-sm-6 text-end">{% trans "Parental authorizations (final):" %}</dt>
 | 
			
		||||
                    <dd class="col-sm-6">
 | 
			
		||||
                        {% for student in team.students.all %}
 | 
			
		||||
                            {% if student.under_18_final %}
 | 
			
		||||
                                {% if student.parental_authorization_final %}
 | 
			
		||||
                                    <a href="{{ student.parental_authorization_final.url }}">{{ student }}</a>{% if not forloop.last %},{% endif %}
 | 
			
		||||
                                {% else %}
 | 
			
		||||
                                    {{ student }} ({% trans "Not uploaded yet" %}){% if not forloop.last %},{% endif %}
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </dd>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
            <dt class="col-sm-6 text-end">{% trans "Motivation letter:" %}</dt>
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,24 @@
 | 
			
		||||
            <div class="card-body">
 | 
			
		||||
                <ul>
 | 
			
		||||
                    {% for participation, note in notes %}
 | 
			
		||||
                        <li><strong>{{ participation.team }} :</strong> {{ note|floatformat }}</li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <strong>{{ participation.team }} :</strong> {{ note|floatformat }}
 | 
			
		||||
                            {% if participation.final %}
 | 
			
		||||
                                <span class="badge badge-sm bg-warning">
 | 
			
		||||
                                    <i class="fas fa-medal"></i>
 | 
			
		||||
                                    {% trans "Selected for final tournament" %}
 | 
			
		||||
                                </span>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            {% if user.registration.is_admin or user.registration in tournament.organizers.all %}
 | 
			
		||||
                                {% if team_selectable_for_final == participation %}
 | 
			
		||||
                                    <a href="{% url 'participation:select_team_final' pk=tournament.pk participation_id=participation.pk %}"
 | 
			
		||||
                                       class="badge badge-sm bg-success">
 | 
			
		||||
                                        <i class="fas fa-medal"></i>
 | 
			
		||||
                                        {% trans "Select for final tournament" %}
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,8 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, GSheetNotific
 | 
			
		||||
    MyParticipationDetailView, MyTeamDetailView, NotationSheetsArchiveView, NoteUpdateView, ParticipationDetailView, \
 | 
			
		||||
    PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
 | 
			
		||||
    PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \
 | 
			
		||||
    ScaleNotationSheetTemplateView, SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \
 | 
			
		||||
    ScaleNotationSheetTemplateView, SelectTeamFinalView, \
 | 
			
		||||
    SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \
 | 
			
		||||
    TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
 | 
			
		||||
    TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
 | 
			
		||||
    TournamentHarmonizeNoteView, TournamentHarmonizeView, TournamentListView, TournamentPaymentsView, \
 | 
			
		||||
@@ -54,6 +55,8 @@ urlpatterns = [
 | 
			
		||||
         name="tournament_harmonize"),
 | 
			
		||||
    path("tournament/<int:pk>/harmonize/<int:round>/<str:action>/<str:trigram>/", TournamentHarmonizeNoteView.as_view(),
 | 
			
		||||
         name="tournament_harmonize_note"),
 | 
			
		||||
    path("tournament/<int:pk>/select-final/<int:participation_id>/", SelectTeamFinalView.as_view(),
 | 
			
		||||
         name="select_team_final"),
 | 
			
		||||
    path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
 | 
			
		||||
    path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),
 | 
			
		||||
    path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"),
 | 
			
		||||
 
 | 
			
		||||
@@ -399,6 +399,7 @@ class TeamAuthorizationsView(LoginRequiredMixin, View):
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
        if 'team_id' in kwargs:
 | 
			
		||||
            team = Team.objects.get(pk=kwargs["team_id"])
 | 
			
		||||
            tournament = team.participation.tournament
 | 
			
		||||
            teams = [team]
 | 
			
		||||
            filename = _("Authorizations of team {trigram}.zip").format(trigram=team.trigram)
 | 
			
		||||
        else:
 | 
			
		||||
@@ -415,6 +416,9 @@ class TeamAuthorizationsView(LoginRequiredMixin, View):
 | 
			
		||||
            for participant in team.participants.all():
 | 
			
		||||
                user_prefix = f"{team_prefix}{participant.user.first_name} {participant.user.last_name}/"
 | 
			
		||||
 | 
			
		||||
                if 'team_id' in kwargs or not tournament.final:
 | 
			
		||||
                    # Don't include the photo authorization and the parental authorization of the regional tournament
 | 
			
		||||
                    # in the final authorizations
 | 
			
		||||
                    if participant.photo_authorization \
 | 
			
		||||
                            and participant.photo_authorization.storage.exists(participant.photo_authorization.path):
 | 
			
		||||
                        mime_type = magic.from_file("media/" + participant.photo_authorization.name)
 | 
			
		||||
@@ -424,7 +428,8 @@ class TeamAuthorizationsView(LoginRequiredMixin, View):
 | 
			
		||||
                                 .format(participant=str(participant), ext=ext))
 | 
			
		||||
 | 
			
		||||
                    if participant.is_student and participant.parental_authorization \
 | 
			
		||||
                        and participant.parental_authorization.storage.exists(participant.parental_authorization.path):
 | 
			
		||||
                            and participant.parental_authorization.storage.exists(
 | 
			
		||||
                                    participant.parental_authorization.path):
 | 
			
		||||
                        mime_type = magic.from_file("media/" + participant.parental_authorization.name)
 | 
			
		||||
                        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | 
			
		||||
                        zf.write("media/" + participant.parental_authorization.name,
 | 
			
		||||
@@ -447,6 +452,26 @@ class TeamAuthorizationsView(LoginRequiredMixin, View):
 | 
			
		||||
                             user_prefix + _("Vaccine sheet of {participant}.{ext}")
 | 
			
		||||
                             .format(participant=str(participant), ext=ext))
 | 
			
		||||
 | 
			
		||||
                if 'team_id' in kwargs or tournament.final:
 | 
			
		||||
                    # Don't include final authorizations in the regional authorizations
 | 
			
		||||
                    if participant.photo_authorization_final \
 | 
			
		||||
                            and participant.photo_authorization_final.storage.exists(
 | 
			
		||||
                                participant.photo_authorization_final.path):
 | 
			
		||||
                        mime_type = magic.from_file("media/" + participant.photo_authorization_final.name)
 | 
			
		||||
                        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | 
			
		||||
                        zf.write("media/" + participant.photo_authorization_final.name,
 | 
			
		||||
                                 user_prefix + _("Photo authorization of {participant} (final).{ext}")
 | 
			
		||||
                                 .format(participant=str(participant), ext=ext))
 | 
			
		||||
 | 
			
		||||
                    if participant.is_student and participant.parental_authorization_final \
 | 
			
		||||
                            and participant.parental_authorization_final.storage.exists(
 | 
			
		||||
                                participant.parental_authorization_final.path):
 | 
			
		||||
                        mime_type = magic.from_file("media/" + participant.parental_authorization_final.name)
 | 
			
		||||
                        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | 
			
		||||
                        zf.write("media/" + participant.parental_authorization_final.name,
 | 
			
		||||
                                 user_prefix + _("Parental authorization of {participant} (final).{ext}")
 | 
			
		||||
                                 .format(participant=str(participant), ext=ext))
 | 
			
		||||
 | 
			
		||||
            if team.motivation_letter and team.motivation_letter.storage.exists(team.motivation_letter.path):
 | 
			
		||||
                mime_type = magic.from_file("media/" + team.motivation_letter.name)
 | 
			
		||||
                ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | 
			
		||||
@@ -591,10 +616,15 @@ class TournamentDetailView(MultiTableMixin, DetailView):
 | 
			
		||||
                       or (self.request.user.is_authenticated and self.request.user.registration.is_volunteer))
 | 
			
		||||
            if note:
 | 
			
		||||
                notes[participation] = note
 | 
			
		||||
        context["notes"] = sorted(notes.items(), key=lambda x: x[1], reverse=True)
 | 
			
		||||
        sorted_notes = sorted(notes.items(), key=lambda x: x[1], reverse=True)
 | 
			
		||||
        context["notes"] = sorted_notes
 | 
			
		||||
        context["available_notes_1"] = all(pool.results_available for pool in self.object.pools.filter(round=1).all())
 | 
			
		||||
        context["available_notes_2"] = all(pool.results_available for pool in self.object.pools.filter(round=2).all())
 | 
			
		||||
 | 
			
		||||
        if not self.object.final and notes and self.request.user.registration.is_volunteer:
 | 
			
		||||
            context["team_selectable_for_final"] = next(participation for participation, _note in sorted_notes
 | 
			
		||||
                                                        if not participation.final)
 | 
			
		||||
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get_tables(self):
 | 
			
		||||
@@ -625,8 +655,11 @@ class TournamentPaymentsView(VolunteerMixin, SingleTableMixin, DetailView):
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_table_data(self):
 | 
			
		||||
        return Payment.objects.filter(registrations__team__participation__tournament=self.get_object()) \
 | 
			
		||||
            .annotate(team_id=F('registrations__team')).order_by('-valid', 'registrations__team__trigram') \
 | 
			
		||||
        if self.object.final:
 | 
			
		||||
            payments = Payment.objects.filter(final=True)
 | 
			
		||||
        else:
 | 
			
		||||
            payments = Payment.objects.filter(registrations__team__participation__tournament=self.get_object())
 | 
			
		||||
        return payments.annotate(team_id=F('registrations__team')).order_by('-valid', 'registrations__team__trigram') \
 | 
			
		||||
            .distinct().all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -795,6 +828,30 @@ class TournamentHarmonizeNoteView(VolunteerMixin, DetailView):
 | 
			
		||||
        return redirect(reverse_lazy("participation:tournament_harmonize", args=(tournament.pk, kwargs['round'],)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SelectTeamFinalView(VolunteerMixin, DetailView):
 | 
			
		||||
    model = Tournament
 | 
			
		||||
 | 
			
		||||
    def dispatch(self, request, *args, **kwargs):
 | 
			
		||||
        if not request.user.is_authenticated:
 | 
			
		||||
            return self.handle_no_permission()
 | 
			
		||||
        tournament = self.get_object()
 | 
			
		||||
        reg = request.user.registration
 | 
			
		||||
        if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()):
 | 
			
		||||
            return self.handle_no_permission()
 | 
			
		||||
        participation_qs = tournament.participations.filter(pk=self.kwargs["participation_id"])
 | 
			
		||||
        if not participation_qs.exists():
 | 
			
		||||
            raise Http404
 | 
			
		||||
        self.participation = participation_qs.get()
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @transaction.atomic
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
        tournament = self.get_object()
 | 
			
		||||
        self.participation.final = True
 | 
			
		||||
        self.participation.save()
 | 
			
		||||
        return redirect(reverse_lazy("participation:tournament_detail", args=(tournament.pk,)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SolutionUploadView(LoginRequiredMixin, FormView):
 | 
			
		||||
    template_name = "participation/upload_solution.html"
 | 
			
		||||
    form_class = SolutionForm
 | 
			
		||||
 
 | 
			
		||||
@@ -133,6 +133,28 @@ class PhotoAuthorizationForm(forms.ModelForm):
 | 
			
		||||
        fields = ('photo_authorization',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PhotoAuthorizationFinalForm(forms.ModelForm):
 | 
			
		||||
    """
 | 
			
		||||
    Form to send a photo authorization.
 | 
			
		||||
    """
 | 
			
		||||
    def clean_photo_authorization_final(self):
 | 
			
		||||
        if "photo_authorization_final" in self.files:
 | 
			
		||||
            file = self.files["photo_authorization_final"]
 | 
			
		||||
            if file.size > 2e6:
 | 
			
		||||
                raise ValidationError(_("The uploaded file size must be under 2 Mo."))
 | 
			
		||||
            if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
 | 
			
		||||
                raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
 | 
			
		||||
            return self.cleaned_data["photo_authorization_final"]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.fields["photo_authorization_final"].widget = FileInput()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = ParticipantRegistration
 | 
			
		||||
        fields = ('photo_authorization_final',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HealthSheetForm(forms.ModelForm):
 | 
			
		||||
    """
 | 
			
		||||
    Form to send a health sheet.
 | 
			
		||||
@@ -199,6 +221,28 @@ class ParentalAuthorizationForm(forms.ModelForm):
 | 
			
		||||
        fields = ('parental_authorization',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ParentalAuthorizationFinalForm(forms.ModelForm):
 | 
			
		||||
    """
 | 
			
		||||
    Form to send a parental authorization.
 | 
			
		||||
    """
 | 
			
		||||
    def clean_parental_authorization(self):
 | 
			
		||||
        if "parental_authorization_final" in self.files:
 | 
			
		||||
            file = self.files["parental_authorization_final"]
 | 
			
		||||
            if file.size > 2e6:
 | 
			
		||||
                raise ValidationError(_("The uploaded file size must be under 2 Mo."))
 | 
			
		||||
            if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
 | 
			
		||||
                raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
 | 
			
		||||
            return self.cleaned_data["parental_authorization"]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.fields["parental_authorization_final"].widget = FileInput()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = StudentRegistration
 | 
			
		||||
        fields = ('parental_authorization_final',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CoachRegistrationForm(forms.ModelForm):
 | 
			
		||||
    """
 | 
			
		||||
    A coach can tell its professional activity.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
# Generated by Django 5.0.3 on 2024-04-07 08:34
 | 
			
		||||
 | 
			
		||||
import registration.models
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("registration", "0012_payment_token_alter_payment_type"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="participantregistration",
 | 
			
		||||
            name="photo_authorization_final",
 | 
			
		||||
            field=models.FileField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                default="",
 | 
			
		||||
                upload_to=registration.models.get_random_photo_filename,
 | 
			
		||||
                verbose_name="photo authorization (final)",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="studentregistration",
 | 
			
		||||
            name="parental_authorization_final",
 | 
			
		||||
            field=models.FileField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                default="",
 | 
			
		||||
                upload_to=registration.models.get_random_parental_filename,
 | 
			
		||||
                verbose_name="parental authorization (final)",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -210,17 +210,31 @@ class ParticipantRegistration(Registration):
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    photo_authorization_final = models.FileField(
 | 
			
		||||
        verbose_name=_("photo authorization (final)"),
 | 
			
		||||
        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()
 | 
			
		||||
        important_date = localtime(timezone.now()).date()
 | 
			
		||||
        if self.team and self.team.participation.tournament:
 | 
			
		||||
            important_date = self.team.participation.tournament.date_start
 | 
			
		||||
            if self.team.participation.final:
 | 
			
		||||
        over_18_on = self.birth_date.replace(year=self.birth_date.year + 18)
 | 
			
		||||
        return important_date < over_18_on
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def under_18_final(self):
 | 
			
		||||
        if isinstance(self, CoachRegistration):
 | 
			
		||||
            return False  # In normal case
 | 
			
		||||
        from participation.models import Tournament
 | 
			
		||||
        important_date = Tournament.final_tournament().date_start
 | 
			
		||||
        return (important_date - self.birth_date).days < 18 * 365.24
 | 
			
		||||
        over_18_on = self.birth_date.replace(year=self.birth_date.year + 18)
 | 
			
		||||
        return important_date < over_18_on
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def type(self):  # pragma: no cover
 | 
			
		||||
@@ -314,6 +328,13 @@ class StudentRegistration(ParticipantRegistration):
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    parental_authorization_final = models.FileField(
 | 
			
		||||
        verbose_name=_("parental authorization (final)"),
 | 
			
		||||
        upload_to=get_random_parental_filename,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    health_sheet = models.FileField(
 | 
			
		||||
        verbose_name=_("health sheet"),
 | 
			
		||||
        upload_to=get_random_health_filename,
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
        <div id="form-content">
 | 
			
		||||
            <div class="alert alert-info">
 | 
			
		||||
                {% trans "Authorization template:" %}
 | 
			
		||||
                <a class="alert-link" href="{% url "registration:parental_authorization_template" %}?registration_id={{ object.pk }}&tournament_id={{ object.team.participation.tournament.pk }}">{% trans "Download" %}</a>
 | 
			
		||||
                <a class="alert-link" href="{% url "registration:parental_authorization_template" %}?registration_id={{ object.pk }}&tournament_id={{ tournament.pk }}">{% trans "Download" %}</a>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            {{ form|crispy }}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,8 @@
 | 
			
		||||
        <div id="form-content">
 | 
			
		||||
            <div class="alert alert-info">
 | 
			
		||||
                {% trans "Authorization templates:" %}
 | 
			
		||||
                <a class="alert-link" href="{% url "registration:photo_authorization_adult_template" %}?registration_id={{ object.pk }}&tournament_id={{ object.team.participation.tournament.pk }}">{% trans "Adult" %}</a> —
 | 
			
		||||
                <a class="alert-link" href="{% url "registration:photo_authorization_child_template" %}?registration_id={{ object.pk }}&tournament_id={{ object.team.participation.tournament.pk }}">{% trans "Child" %}</a>
 | 
			
		||||
                <a class="alert-link" href="{% url "registration:photo_authorization_adult_template" %}?registration_id={{ object.pk }}&tournament_id={{ tournament.pk }}">{% trans "Adult" %}</a> —
 | 
			
		||||
                <a class="alert-link" href="{% url "registration:photo_authorization_child_template" %}?registration_id={{ object.pk }}&tournament_id={{ tournament.pk }}">{% trans "Child" %}</a>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            {{ form|crispy }}
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,16 @@
 | 
			
		||||
                        <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadPhotoAuthorizationModal">{% trans "Replace" %}</button>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </dd>
 | 
			
		||||
 | 
			
		||||
                {% if user_object.registration.team.participation.final %}
 | 
			
		||||
                    <dt class="col-sm-6 text-end">{% trans "Photo authorization (final):" %}</dt>
 | 
			
		||||
                    <dd class="col-sm-6">
 | 
			
		||||
                        {% if user_object.registration.photo_authorization_final %}
 | 
			
		||||
                            <a href="{{ user_object.registration.photo_authorization_final.url }}">{% trans "Download" %}</a>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadPhotoAuthorizationFinalModal">{% trans "Replace" %}</button>
 | 
			
		||||
                    </dd>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
            {% if user_object.registration.studentregistration %}
 | 
			
		||||
@@ -105,6 +115,16 @@
 | 
			
		||||
                            <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadParentalAuthorizationModal">{% trans "Replace" %}</button>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </dd>
 | 
			
		||||
 | 
			
		||||
                    {% if user_object.registration.team.participation.final %}
 | 
			
		||||
                        <dt class="col-sm-6 text-end">{% trans "Parental authorization (final):" %}</dt>
 | 
			
		||||
                        <dd class="col-sm-6">
 | 
			
		||||
                            {% if user_object.registration.parental_authorization_final %}
 | 
			
		||||
                                <a href="{{ user_object.registration.parental_authorization_final.url }}">{% trans "Download" %}</a>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadParentalAuthorizationFinalModal">{% trans "Replace" %}</button>
 | 
			
		||||
                        </dd>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                {% endif %}
 | 
			
		||||
 | 
			
		||||
                <dt class="col-sm-6 text-end">{% trans "Student class:" %}</dt>
 | 
			
		||||
@@ -143,11 +163,16 @@
 | 
			
		||||
        </dl>
 | 
			
		||||
 | 
			
		||||
        {% if user_object.registration.participates and user_object.registration.team.participation.valid %}
 | 
			
		||||
            {% for payment in user_object.registration.payments.all %}
 | 
			
		||||
            <hr>
 | 
			
		||||
    
 | 
			
		||||
            {% for payment in user_object.registration.payments.all %}
 | 
			
		||||
                <dl class="row">
 | 
			
		||||
                    <dt class="col-sm-6 text-end">{% trans "Payment information:" %}</dt>
 | 
			
		||||
                    <dt class="col-sm-6 text-end">
 | 
			
		||||
                        {% if payment.final %}
 | 
			
		||||
                            {% trans "Payment information (final):" %}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            {% trans "Payment information:" %}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </dt>
 | 
			
		||||
                    <dd class="col-sm-6">
 | 
			
		||||
                        {% trans "yes,no,pending" as yesnodefault %}
 | 
			
		||||
                        {% with info=payment.additional_information %}
 | 
			
		||||
@@ -197,6 +222,7 @@
 | 
			
		||||
        {% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
        {% include "base_modal.html" with modal_id="uploadPhotoAuthorization" modal_enctype="multipart/form-data" %}
 | 
			
		||||
 | 
			
		||||
        {% if user_object.registration.under_18 %}
 | 
			
		||||
            {% trans "Upload health sheet" as modal_title %}
 | 
			
		||||
            {% trans "Upload" as modal_button %}
 | 
			
		||||
            {% url "registration:upload_user_health_sheet" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
@@ -211,11 +237,21 @@
 | 
			
		||||
            {% trans "Upload" as modal_button %}
 | 
			
		||||
            {% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
            {% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% trans "Upload parental authorization" as modal_title %}
 | 
			
		||||
    {% if user_object.registration.team.participation.final %}
 | 
			
		||||
        {% trans "Upload photo authorization (final)" as modal_title %}
 | 
			
		||||
        {% trans "Upload" as modal_button %}
 | 
			
		||||
        {% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
        {% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
 | 
			
		||||
        {% url "registration:upload_user_photo_authorization_final" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
        {% include "base_modal.html" with modal_id="uploadPhotoAuthorizationFinal" modal_enctype="multipart/form-data" %}
 | 
			
		||||
 | 
			
		||||
        {% if user_object.registration.under_18_final %}
 | 
			
		||||
            {% trans "Upload parental authorization (final)" as modal_title %}
 | 
			
		||||
            {% trans "Upload" as modal_button %}
 | 
			
		||||
            {% url "registration:upload_user_parental_authorization_final" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
            {% include "base_modal.html" with modal_id="uploadParentalAuthorizationFinal" modal_enctype="multipart/form-data" %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -224,10 +260,19 @@
 | 
			
		||||
        document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
            {% if user_object.registration.team and not user_object.registration.team.participation.valid %}
 | 
			
		||||
                initModal("uploadPhotoAuthorization", "{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk %}")
 | 
			
		||||
                {% if user_object.registration.under_18 %}
 | 
			
		||||
                    initModal("uploadHealthSheet", "{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk %}")
 | 
			
		||||
                    initModal("uploadVaccineSheet", "{% url "registration:upload_user_vaccine_sheet" pk=user_object.registration.pk %}")
 | 
			
		||||
                    initModal("uploadParentalAuthorization", "{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk %}")
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
            {% if user_object.registration.team.participation.final %}
 | 
			
		||||
                initModal("uploadPhotoAuthorizationFinal", "{% url "registration:upload_user_photo_authorization_final" pk=user_object.registration.pk %}")
 | 
			
		||||
                 {% if user_object.registration.under_18_final %}
 | 
			
		||||
                     initModal("uploadParentalAuthorizationFinal", "{% url "registration:upload_user_parental_authorization_final" pk=user_object.registration.pk %}")
 | 
			
		||||
                 {% endif %}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        });
 | 
			
		||||
    </script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,8 @@ urlpatterns = [
 | 
			
		||||
    path("user/<int:pk>/update/", UserUpdateView.as_view(), name="update_user"),
 | 
			
		||||
    path("user/<int:pk>/upload-photo-authorization/", UserUploadPhotoAuthorizationView.as_view(),
 | 
			
		||||
         name="upload_user_photo_authorization"),
 | 
			
		||||
    path("user/<int:pk>/upload-photo-authorization/final/", UserUploadPhotoAuthorizationView.as_view(),
 | 
			
		||||
         name="upload_user_photo_authorization_final"),
 | 
			
		||||
    path("parental-authorization-template/", ParentalAuthorizationTemplateView.as_view(),
 | 
			
		||||
         name="parental_authorization_template"),
 | 
			
		||||
    path("photo-authorization-template/adult/", AdultPhotoAuthorizationTemplateView.as_view(),
 | 
			
		||||
@@ -37,6 +39,8 @@ urlpatterns = [
 | 
			
		||||
         name="upload_user_vaccine_sheet"),
 | 
			
		||||
    path("user/<int:pk>/upload-parental-authorization/", UserUploadParentalAuthorizationView.as_view(),
 | 
			
		||||
         name="upload_user_parental_authorization"),
 | 
			
		||||
    path("user/<int:pk>/upload-parental-authorization/final/", UserUploadParentalAuthorizationView.as_view(),
 | 
			
		||||
         name="upload_user_parental_authorization_final"),
 | 
			
		||||
    path("update-payment/<int:pk>/", PaymentUpdateView.as_view(), name="update_payment"),
 | 
			
		||||
    path("update-payment/<int:pk>/toggle-group-mode/", PaymentUpdateGroupView.as_view(),
 | 
			
		||||
         name="update_payment_group_mode"),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by Animath
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
@@ -29,8 +30,9 @@ from participation.models import Passage, Solution, Synthesis, Tournament
 | 
			
		||||
from tfjm.tokens import email_validation_token
 | 
			
		||||
from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin
 | 
			
		||||
 | 
			
		||||
from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, ParentalAuthorizationForm, \
 | 
			
		||||
    PaymentAdminForm, PaymentForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
 | 
			
		||||
from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, \
 | 
			
		||||
    PhotoAuthorizationFinalForm, ParentalAuthorizationForm, PaymentAdminForm, PaymentForm, \
 | 
			
		||||
    ParentalAuthorizationFinalForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
 | 
			
		||||
    VaccineSheetForm, VolunteerRegistrationForm
 | 
			
		||||
from .models import ParticipantRegistration, Payment, Registration, StudentRegistration
 | 
			
		||||
from .tables import RegistrationTable
 | 
			
		||||
@@ -311,15 +313,27 @@ class UserUploadPhotoAuthorizationView(UserRegistrationMixin, UpdateView):
 | 
			
		||||
    A participant can send its photo authorization.
 | 
			
		||||
    """
 | 
			
		||||
    model = ParticipantRegistration
 | 
			
		||||
    form_class = PhotoAuthorizationForm
 | 
			
		||||
    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.objects.get(pk=self.object.pk)
 | 
			
		||||
        if old_instance.photo_authorization:
 | 
			
		||||
            old_instance.photo_authorization.delete()
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
@@ -374,15 +388,27 @@ class UserUploadParentalAuthorizationView(UserRegistrationMixin, UpdateView):
 | 
			
		||||
    A participant can send its parental authorization.
 | 
			
		||||
    """
 | 
			
		||||
    model = StudentRegistration
 | 
			
		||||
    form_class = ParentalAuthorizationForm
 | 
			
		||||
    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.objects.get(pk=self.object.pk)
 | 
			
		||||
        if old_instance.parental_authorization:
 | 
			
		||||
            old_instance.parental_authorization.delete()
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
@@ -666,7 +692,8 @@ class PhotoAuthorizationView(LoginRequiredMixin, View):
 | 
			
		||||
        path = f"media/authorization/photo/{filename}"
 | 
			
		||||
        if not os.path.exists(path):
 | 
			
		||||
            raise Http404
 | 
			
		||||
        student = ParticipantRegistration.objects.get(photo_authorization__endswith=filename)
 | 
			
		||||
        student = ParticipantRegistration.objects.get(Q(photo_authorization__endswith=filename)
 | 
			
		||||
                                                      | Q(photo_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()):
 | 
			
		||||
@@ -738,7 +765,8 @@ class ParentalAuthorizationView(LoginRequiredMixin, View):
 | 
			
		||||
        path = f"media/authorization/parental/{filename}"
 | 
			
		||||
        if not os.path.exists(path):
 | 
			
		||||
            raise Http404
 | 
			
		||||
        student = StudentRegistration.objects.get(parental_authorization__endswith=filename)
 | 
			
		||||
        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()):
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user