Upload all authorizations

This commit is contained in:
Yohann D'ANELLO 2020-12-29 16:14:56 +01:00
parent 72753edf64
commit e3a32a41f9
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
10 changed files with 252 additions and 68 deletions

View File

@ -1,7 +1,6 @@
# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from address.forms import AddressField
from address.widgets import AddressWidget
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
@ -69,7 +68,8 @@ class StudentRegistrationForm(forms.ModelForm):
class Meta:
model = StudentRegistration
fields = ('team', 'student_class', 'birth_date', 'address', 'phone_number',
'school', 'give_contact_to_animath', 'email_confirmed',)
'school', 'responsible_name', 'responsible_phone', 'responsible_email',
'give_contact_to_animath', 'email_confirmed',)
class PhotoAuthorizationForm(forms.ModelForm):
@ -94,6 +94,50 @@ class PhotoAuthorizationForm(forms.ModelForm):
fields = ('photo_authorization',)
class HealthSheetForm(forms.ModelForm):
"""
Form to send a health sheet.
"""
def clean_health_sheet(self):
if "health_sheet" in self.files:
file = self.files["health_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["health_sheet"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["health_sheet"].widget = FileInput()
class Meta:
model = StudentRegistration
fields = ('health_sheet',)
class ParentalAuthorizationForm(forms.ModelForm):
"""
Form to send a parental authorization.
"""
def clean_parental_authorization(self):
if "parental_authorization" in self.files:
file = self.files["parental_authorization"]
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"].widget = FileInput()
class Meta:
model = StudentRegistration
fields = ('parental_authorization',)
class CoachRegistrationForm(forms.ModelForm):
"""
A coach can tell its professional activity.

View File

@ -1,6 +1,8 @@
# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date
from address.models import AddressField
from django.contrib.sites.models import Site
from django.db import models
@ -73,7 +75,7 @@ class Registration(PolymorphicModel):
@property
def participates(self):
return isinstance(self, StudentRegistration) or isinstance(self, CoachRegistration)
return isinstance(self, ParticipantRegistration)
@property
def is_admin(self):
@ -119,7 +121,7 @@ class ParticipantRegistration(Registration):
birth_date = models.DateField(
verbose_name=_("birth date"),
default=timezone.now,
default=date.today,
)
address = AddressField(
@ -147,6 +149,10 @@ class ParticipantRegistration(Registration):
default="",
)
@property
def under_18(self):
return (timezone.now().date() - self.birth_date).days < 18 * 365.24
@property
def type(self): # pragma: no cover
raise NotImplementedError

View File

@ -0,0 +1,19 @@
{% 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">
<div class="alert alert-info">
{% trans "Health sheet template:" %}
<a class="alert-link" href="{% static "Fiche_sanitaire.pdf" %}">{% trans "Download" %}</a>
</div>
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% 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">
<div class="alert alert-info">
{% trans "Authorzation template:" %}
<a class="alert-link" href="{% static "Autorisation_parentale.tex" %}">{% trans "Download" %}</a>
</div>
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
</form>
{% endblock %}

View File

@ -9,8 +9,8 @@
<div id="form-content">
<div class="alert alert-info">
{% trans "Authorzation templates:" %}
<a class="alert-link" href="{% static "Autorisation de droit à l'image - majeur.pdf" %}">{% trans "Adult" %}</a>
<a class="alert-link" href="{% static "Autorisation de droit à l'image - mineur.pdf" %}">{% trans "Child" %}</a>
<a class="alert-link" href="{% static "Autorisation_droit_image_majeur.tex" %}">{% trans "Adult" %}</a>
<a class="alert-link" href="{% static "Autorisation_droit_image_mineur.tex" %}">{% trans "Child" %}</a>
</div>
{% csrf_token %}
{{ form|crispy }}

View File

@ -21,7 +21,7 @@
<dd class="col-sm-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a>
{% if not user_object.registration.email_confirmed %} (<em>{% trans "Not confirmed" %}, <a href="{% url "registration:email_validation_resend" pk=user_object.pk %}">{% trans "resend the validation link" %}</a></em>){% endif %}</dd>
{% if user_object.registration.participates or True %}
{% if user_object.registration.participates %}
<dt class="col-sm-6 text-right">{% trans "Team:" %}</dt>
{% trans "any" as any %}
<dd class="col-sm-6">
@ -29,14 +29,15 @@
{{ user_object.registration.team|default:any }}
</a>
</dd>
{% endif %}
{% if user_object.registration.studentregistration %}
<dt class="col-sm-6 text-right">{% trans "Student class:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.get_student_class_display }}</dd>
<dt class="col-sm-6 text-right">{% trans "Birth date:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.birth_date }}</dd>
<dt class="col-sm-6 text-right">{% trans "School:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.school }}</dd>
<dt class="col-sm-6 text-right">{% trans "Address:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.address }}</dd>
<dt class="col-sm-6 text-right">{% trans "Phone number:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.phone_number }}</dd>
<dt class="col-sm-6 text-right">{% trans "Photo authorization:" %}</dt>
<dd class="col-sm-6">
@ -47,6 +48,47 @@
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadPhotoAuthorizationModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
<dt class="col-sm-6 text-right">{% trans "Health sheet:" %}</dt>
<dd class="col-sm-6">
{% if user_object.registration.health_sheet %}
<a href="{{ user_object.registration.health_sheet.url }}" data-turbolinks="false">{% trans "Download" %}</a>
{% endif %}
{% if user_object.pk == user.pk %}
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadHealthSheetModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
{% endif %}
{% if user_object.registration.studentregistration %}
{% if user_object.registration.under_18 %}
<dt class="col-sm-6 text-right">{% trans "Parental authorization:" %}</dt>
<dd class="col-sm-6">
{% if user_object.registration.parental_authorization %}
<a href="{{ user_object.registration.parental_authorization.url }}" data-turbolinks="false">{% trans "Download" %}</a>
{% endif %}
{% if user_object.pk == user.pk %}
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadParentalAuthorizationModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
{% endif %}
<dt class="col-sm-6 text-right">{% trans "Student class:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.get_student_class_display }}</dd>
<dt class="col-sm-6 text-right">{% trans "School:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.school }}</dd>
<dt class="col-sm-6 text-right">{% trans "Responsible name:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.responsible_name }}</dd>
<dt class="col-sm-6 text-right">{% trans "Responsible phone number:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.responsible_phone }}</dd>
<dt class="col-sm-6 text-right">{% trans "Responsible email address:" %}</dt>
{% with user_object.registration.responsible_email as email %}
<dd class="col-sm-6"><a href="mailto:{{ email }}">{{ email }}</a></dd>
{% endwith %}
{% elif user_object.registration.coachregistration %}
<dt class="col-sm-6 text-right">{% trans "Profesional activity:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.professional_activity }}</dd>
@ -78,6 +120,16 @@
{% trans "Upload" as modal_button %}
{% 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" %}
{% 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 %}
{% include "base_modal.html" with modal_id="uploadHealthSheet" 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 %}
{% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
{% endblock %}
{% block extrajavascript %}
@ -93,6 +145,16 @@
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk %} #form-content");
});
$('button[data-target="#uploadHealthSheetModal"]').click(function() {
let modalBody = $("#uploadHealthSheetModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk %} #form-content");
});
$('button[data-target="#uploadParentalAuthorizationModal"]').click(function() {
let modalBody = $("#uploadParentalAuthorizationModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk %} #form-content");
});
});
</script>
{% endblock %}

View File

@ -268,37 +268,38 @@ class TestRegistration(TestCase):
"""
Try to upload a photo authorization.
"""
response = self.client.get(reverse("registration:upload_user_photo_authorization",
args=(self.student.registration.pk,)))
self.assertEqual(response.status_code, 200)
for auth_type in ["photo_authorization", "health_sheet", "parental_authorization"]:
response = self.client.get(reverse("registration:upload_user_photo_authorization",
args=(self.student.registration.pk,)))
self.assertEqual(response.status_code, 200)
# README is not a valid PDF file
response = self.client.post(reverse("registration:upload_user_photo_authorization",
args=(self.student.registration.pk,)), data=dict(
photo_authorization=open("README.md", "rb"),
))
self.assertEqual(response.status_code, 200)
# README is not a valid PDF file
response = self.client.post(reverse(f"registration:upload_user_{auth_type}",
args=(self.student.registration.pk,)), data={
auth_type: open("README.md", "rb"),
})
self.assertEqual(response.status_code, 200)
# Don't send too large files
response = self.client.post(reverse("registration:upload_user_photo_authorization",
args=(self.student.registration.pk,)), data=dict(
photo_authorization=SimpleUploadedFile("file.pdf", content=int(0).to_bytes(2000001, "big"),
content_type="application/pdf"),
))
self.assertEqual(response.status_code, 200)
# Don't send too large files
response = self.client.post(reverse(f"registration:upload_user_{auth_type}",
args=(self.student.registration.pk,)), data={
auth_type: SimpleUploadedFile("file.pdf", content=int(0).to_bytes(2000001, "big"),
content_type="application/pdf"),
})
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse("registration:upload_user_photo_authorization",
args=(self.student.registration.pk,)), data=dict(
photo_authorization=open("tfjm/static/Fiche_sanitaire.pdf", "rb"),
))
self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200)
response = self.client.post(reverse(f"registration:upload_user_{auth_type}",
args=(self.student.registration.pk,)), data={
auth_type: open("tfjm/static/Fiche_sanitaire.pdf", "rb"),
})
self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200)
self.student.registration.refresh_from_db()
self.assertTrue(self.student.registration.photo_authorization)
self.student.registration.refresh_from_db()
self.assertTrue(getattr(self.student.registration, auth_type))
response = self.client.get(reverse("photo_authorization",
args=(self.student.registration.photo_authorization.name.split('/')[-1],)))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse(
auth_type, args=(self.student.registration.photo_authorization.name.split('/')[-1],)))
self.assertEqual(response.status_code, 200)
from participation.models import Team
team = Team.objects.create(name="Test", trigram="TES")

View File

@ -4,7 +4,8 @@
from django.urls import path
from .views import MyAccountDetailView, ResetAdminView, SignupView, UserDetailView, UserImpersonateView, \
UserListView, UserResendValidationEmailView, UserUpdateView, UserUploadPhotoAuthorizationView, UserValidateView, \
UserListView, UserResendValidationEmailView, UserUpdateView, UserUploadHealthSheetView, \
UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, UserValidateView, \
UserValidationEmailSentView
app_name = "registration"
@ -20,6 +21,10 @@ 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-health_sheet/", UserUploadHealthSheetView.as_view(),
name="upload_user_health_sheet"),
path("user/<int:pk>/upload-parental-authorization/", UserUploadParentalAuthorizationView.as_view(),
name="upload_user_parental_authorization"),
path("user/<int:pk>/impersonate/", UserImpersonateView.as_view(), name="user_impersonate"),
path("user/list/", UserListView.as_view(), name="user_list"),
path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"),

View File

@ -17,9 +17,10 @@ from django.views.generic import CreateView, DetailView, RedirectView, TemplateV
from django_tables2 import SingleTableView
from magic import Magic
from tfjm.tokens import email_validation_token
from tfjm.views import AdminMixin
from tfjm.views import AdminMixin, UserMixin
from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm
from .forms import CoachRegistrationForm, HealthSheetForm, ParentalAuthorizationForm, PhotoAuthorizationForm,\
SignupForm, StudentRegistrationForm, UserForm
from .models import Registration, StudentRegistration
from .tables import RegistrationTable
@ -150,7 +151,7 @@ class MyAccountDetailView(LoginRequiredMixin, RedirectView):
return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))
class UserDetailView(LoginRequiredMixin, DetailView):
class UserDetailView(UserMixin, DetailView):
"""
Display the detail about a user.
"""
@ -159,15 +160,6 @@ class UserDetailView(LoginRequiredMixin, DetailView):
context_object_name = "user_object"
template_name = "registration/user_detail.html"
def dispatch(self, request, *args, **kwargs):
user = request.user
if not user.is_authenticated:
return self.handle_no_permission()
# Only an admin or the concerned user can see the information
if not user.registration.is_admin and user.pk != kwargs["pk"]:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("Detail of user {user}").format(user=str(self.object.registration))
@ -183,7 +175,7 @@ class UserListView(AdminMixin, SingleTableView):
template_name = "registration/user_list.html"
class UserUpdateView(LoginRequiredMixin, UpdateView):
class UserUpdateView(UserMixin, UpdateView):
"""
Update the detail about a user and its registration.
"""
@ -191,12 +183,6 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
form_class = UserForm
template_name = "registration/update_user.html"
def dispatch(self, request, *args, **kwargs):
user = request.user
if not user.registration.is_admin and user.pk != kwargs["pk"]:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.get_object()
@ -229,7 +215,7 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
return reverse_lazy("registration:user_detail", args=(self.object.pk,))
class UserUploadPhotoAuthorizationView(LoginRequiredMixin, UpdateView):
class UserUploadPhotoAuthorizationView(UserMixin, UpdateView):
"""
A participant can send its photo authorization.
"""
@ -238,12 +224,6 @@ class UserUploadPhotoAuthorizationView(LoginRequiredMixin, UpdateView):
template_name = "registration/upload_photo_authorization.html"
extra_context = dict(title=_("Upload photo authorization"))
def dispatch(self, request, *args, **kwargs):
user = request.user
if not user.registration.is_admin and user.registration.pk != kwargs["pk"]:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
@transaction.atomic
def form_valid(self, form):
old_instance = StudentRegistration.objects.get(pk=self.object.pk)
@ -255,6 +235,46 @@ class UserUploadPhotoAuthorizationView(LoginRequiredMixin, UpdateView):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
class UserUploadHealthSheetView(UserMixin, 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()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
class UserUploadParentalAuthorizationView(UserMixin, 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"))
@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()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
class PhotoAuthorizationView(LoginRequiredMixin, View):
"""
Display the sent photo authorization.

View File

@ -8,7 +8,15 @@ from haystack.generic_views import SearchView
class AdminMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.registration.is_admin:
if request.user.is_authenticated and not request.user.registration.is_admin:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
class UserMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
user = request.user
if user.is_authenticated and not user.registration.is_admin and user.registration.pk != kwargs["pk"]:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)