mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 05:42:12 +01:00 
			
		
		
		
	Add vaccine sheet field, closes #18
This commit is contained in:
		@@ -74,6 +74,19 @@
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </dd>
 | 
			
		||||
 | 
			
		||||
                <dt class="col-sm-6 text-right">{% trans "Vaccine sheets:" %}</dt>
 | 
			
		||||
                <dd class="col-sm-6">
 | 
			
		||||
                    {% for student in team.students.all %}
 | 
			
		||||
                        {% if student.under_18 %}
 | 
			
		||||
                            {% if student.vaccine_sheet %}
 | 
			
		||||
                                <a href="{{ student.vaccine_sheet.url }}" data-turbolinks="false">{{ student }}</a>{% if not forloop.last %},{% endif %}
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                {{ student }} ({% trans "Not uploaded yet" %}){% if not forloop.last %},{% endif %}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </dd>
 | 
			
		||||
 | 
			
		||||
                <dt class="col-sm-6 text-right">{% trans "Parental authorizations:" %}</dt>
 | 
			
		||||
                <dd class="col-sm-6">
 | 
			
		||||
                    {% for student in team.students.all %}
 | 
			
		||||
 
 | 
			
		||||
@@ -230,6 +230,7 @@ class TestStudentParticipation(TestCase):
 | 
			
		||||
            city="Paris",
 | 
			
		||||
            photo_authorization="authorization/photo/mai-linh",
 | 
			
		||||
            health_sheet="authorization/health/mai-linh",
 | 
			
		||||
            vaccine_sheet="authorization/vaccine/mai-linh",
 | 
			
		||||
            parental_authorization="authorization/parental/mai-linh",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -251,6 +252,7 @@ class TestStudentParticipation(TestCase):
 | 
			
		||||
            city="Paris",
 | 
			
		||||
            photo_authorization="authorization/photo/emmy",
 | 
			
		||||
            health_sheet="authorization/health/emmy",
 | 
			
		||||
            vaccine_sheet="authorization/vaccine/emmy",
 | 
			
		||||
            parental_authorization="authorization/parental/emmy",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -272,11 +274,13 @@ class TestStudentParticipation(TestCase):
 | 
			
		||||
            city="Paris",
 | 
			
		||||
            photo_authorization="authorization/photo/tfjm",
 | 
			
		||||
            health_sheet="authorization/health/tfjm",
 | 
			
		||||
            vaccine_sheet="authorization/health/tfjm",
 | 
			
		||||
            parental_authorization="authorization/parental/tfjm",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.coach.registration.team = self.team
 | 
			
		||||
        self.coach.registration.health_sheet = "authorization/health/coach"
 | 
			
		||||
        self.coach.registration.vaccine_sheet = "authorization/vaccine/coach"
 | 
			
		||||
        self.coach.registration.photo_authorization = "authorization/photo/coach"
 | 
			
		||||
        self.coach.registration.email_confirmed = True
 | 
			
		||||
        self.coach.registration.save()
 | 
			
		||||
@@ -305,6 +309,7 @@ class TestStudentParticipation(TestCase):
 | 
			
		||||
 | 
			
		||||
        self.user.registration.photo_authorization = "authorization/photo/ananas"
 | 
			
		||||
        self.user.registration.health_sheet = "authorization/health/ananas"
 | 
			
		||||
        self.user.registration.vaccine_sheet = "authorization/health/ananas"
 | 
			
		||||
        self.user.registration.parental_authorization = "authorization/parental/ananas"
 | 
			
		||||
        self.user.registration.save()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -180,6 +180,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
 | 
			
		||||
        context["validation_form"] = ValidateParticipationForm(self.request.POST or None)
 | 
			
		||||
        # A team is complete when there are at least 4 members plus a coache that have sent their authorizations,
 | 
			
		||||
        # their health sheet, they confirmed their email address and under-18 people sent their parental authorization.
 | 
			
		||||
        # TODO: Add vaccine sheets
 | 
			
		||||
        context["can_validate"] = team.students.count() >= 4 and team.coaches.exists() and \
 | 
			
		||||
            team.participation.tournament and \
 | 
			
		||||
            all(r.photo_authorization for r in team.participants.all()) and \
 | 
			
		||||
@@ -414,6 +415,12 @@ class TeamAuthorizationsView(LoginRequiredMixin, DetailView):
 | 
			
		||||
                zf.write("media/" + participant.health_sheet.name,
 | 
			
		||||
                         _("Health sheet of {participant}.{ext}").format(participant=str(participant), ext=ext))
 | 
			
		||||
 | 
			
		||||
            if isinstance(participant, StudentRegistration) and participant.vaccine_sheet:
 | 
			
		||||
                mime_type = magic.from_file("media/" + participant.vaccine_sheet.name)
 | 
			
		||||
                ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | 
			
		||||
                zf.write("media/" + participant.vaccine_sheet.name,
 | 
			
		||||
                         _("Vaccine sheet of {participant}.{ext}").format(participant=str(participant), ext=ext))
 | 
			
		||||
 | 
			
		||||
        if team.motivation_letter:
 | 
			
		||||
            mime_type = magic.from_file("media/" + team.motivation_letter.name)
 | 
			
		||||
            ext = mime_type.split("/")[1].replace("jpeg", "jpg")
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,28 @@ class HealthSheetForm(forms.ModelForm):
 | 
			
		||||
        fields = ('health_sheet',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VaccineSheetForm(forms.ModelForm):
 | 
			
		||||
    """
 | 
			
		||||
    Form to send a vaccine sheet.
 | 
			
		||||
    """
 | 
			
		||||
    def clean_vaccine_sheet(self):
 | 
			
		||||
        if "vaccine_sheet" in self.files:
 | 
			
		||||
            file = self.files["vaccine_sheet"]
 | 
			
		||||
            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["vaccine_sheet"]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.fields["vaccine_sheet"].widget = FileInput()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = StudentRegistration
 | 
			
		||||
        fields = ('vaccine_sheet',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ParentalAuthorizationForm(forms.ModelForm):
 | 
			
		||||
    """
 | 
			
		||||
    Form to send a parental authorization.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
# Generated by Django 3.2.18 on 2023-02-19 23:38
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import registration.models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('registration', '0004_volunteer_admin'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='studentregistration',
 | 
			
		||||
            name='vaccine_sheet',
 | 
			
		||||
            field=models.FileField(blank=True, default='', upload_to=registration.models.get_random_vaccine_filename, verbose_name='vaccine sheet'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -108,6 +108,10 @@ 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)
 | 
			
		||||
 | 
			
		||||
@@ -242,6 +246,13 @@ class StudentRegistration(ParticipantRegistration):
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    vaccine_sheet = models.FileField(
 | 
			
		||||
        verbose_name=_("vaccine sheet"),
 | 
			
		||||
        upload_to=get_random_vaccine_filename,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def type(self):
 | 
			
		||||
        return _("student")
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
 | 
			
		||||
{% load i18n static crispy_forms_filters %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <a class="btn btn-info" href="{% url "registration:user_detail" pk=object.user.pk %}"><i class="fas fa-arrow-left"></i> {% trans "Back to the user detail" %}</a>
 | 
			
		||||
    <hr>
 | 
			
		||||
    <form method="post" enctype="multipart/form-data">
 | 
			
		||||
        <div id="form-content">
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            {{ form|crispy }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
 | 
			
		||||
    </form>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -79,6 +79,16 @@
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </dd>
 | 
			
		||||
 | 
			
		||||
                    <dt class="col-sm-6 text-right">{% trans "Vaccine sheet:" %}</dt>
 | 
			
		||||
                    <dd class="col-sm-6">
 | 
			
		||||
                        {% if user_object.registration.vaccine_sheet %}
 | 
			
		||||
                            <a href="{{ user_object.registration.vaccine_sheet.url }}" data-turbolinks="false">{% trans "Download" %}</a>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        {% if user_object.registration.team and not user_object.registration.team.participation.valid %}
 | 
			
		||||
                            <button class="btn btn-primary" data-toggle="modal" data-target="#uploadVaccineSheetModal">{% trans "Replace" %}</button>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </dd>
 | 
			
		||||
 | 
			
		||||
                    <dt class="col-sm-6 text-right">{% trans "Parental authorization:" %}</dt>
 | 
			
		||||
                    <dd class="col-sm-6">
 | 
			
		||||
                        {% if user_object.registration.parental_authorization %}
 | 
			
		||||
@@ -171,6 +181,11 @@
 | 
			
		||||
        {% url "registration:upload_user_health_sheet" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
        {% include "base_modal.html" with modal_id="uploadHealthSheet" modal_enctype="multipart/form-data" %}
 | 
			
		||||
 | 
			
		||||
        {% trans "Upload vaccine sheet" as modal_title %}
 | 
			
		||||
        {% trans "Upload" as modal_button %}
 | 
			
		||||
        {% url "registration:upload_user_vaccine_sheet" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
        {% include "base_modal.html" with modal_id="uploadVaccineSheet" modal_enctype="multipart/form-data" %}
 | 
			
		||||
 | 
			
		||||
        {% trans "Upload parental authorization" as modal_title %}
 | 
			
		||||
        {% trans "Upload" as modal_button %}
 | 
			
		||||
        {% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ from .views import AddOrganizerView, AdultPhotoAuthorizationTemplateView, ChildP
 | 
			
		||||
    InstructionsTemplateView, MyAccountDetailView, ParentalAuthorizationTemplateView, PaymentUpdateView, \
 | 
			
		||||
    ResetAdminView, SignupView, UserDetailView, UserImpersonateView, UserListView, UserResendValidationEmailView, \
 | 
			
		||||
    UserUpdateView, UserUploadHealthSheetView, UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, \
 | 
			
		||||
    UserValidateView, UserValidationEmailSentView
 | 
			
		||||
    UserUploadVaccineSheetView, UserValidateView, UserValidationEmailSentView
 | 
			
		||||
 | 
			
		||||
app_name = "registration"
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +32,8 @@ urlpatterns = [
 | 
			
		||||
    path("instructions-template/", InstructionsTemplateView.as_view(), name="instructions_template"),
 | 
			
		||||
    path("user/<int:pk>/upload-health-sheet/", UserUploadHealthSheetView.as_view(),
 | 
			
		||||
         name="upload_user_health_sheet"),
 | 
			
		||||
    path("user/<int:pk>/upload-vaccine-sheet/", UserUploadVaccineSheetView.as_view(),
 | 
			
		||||
         name="upload_user_vaccine_sheet"),
 | 
			
		||||
    path("user/<int:pk>/upload-parental-authorization/", UserUploadParentalAuthorizationView.as_view(),
 | 
			
		||||
         name="upload_user_parental_authorization"),
 | 
			
		||||
    path("update-payment/<int:pk>/", PaymentUpdateView.as_view(), name="update_payment"),
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin
 | 
			
		||||
 | 
			
		||||
from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, \
 | 
			
		||||
    ParentalAuthorizationForm, PaymentForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
 | 
			
		||||
    VolunteerRegistrationForm
 | 
			
		||||
    VaccineSheetForm, VolunteerRegistrationForm
 | 
			
		||||
from .models import ParticipantRegistration, Payment, Registration, StudentRegistration
 | 
			
		||||
from .tables import RegistrationTable
 | 
			
		||||
 | 
			
		||||
@@ -344,6 +344,27 @@ class UserUploadHealthSheetView(UserRegistrationMixin, UpdateView):
 | 
			
		||||
        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.
 | 
			
		||||
@@ -484,6 +505,29 @@ class HealthSheetView(LoginRequiredMixin, View):
 | 
			
		||||
        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.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user