Add parental and photo authorizations + make health and vaccine sheet and motivation letter optional

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-06-07 17:20:06 +02:00
parent ea03bd314b
commit e026f49f8d
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
13 changed files with 316 additions and 88 deletions

View File

@ -1,6 +1,7 @@
# Copyright (C) 2024 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.core.management import BaseCommand
from django.utils.translation import activate
from participation.models import Team, Tournament
@ -18,7 +19,7 @@ class Command(BaseCommand):
help = "Create chat channels for tournaments and teams."
def handle(self, *args, **kwargs):
activate('fr')
activate(settings.PREFERRED_LANGUAGE_CODE)
# Création de canaux généraux, d'annonces, d'aide jurys et orgas, etc.
# Le canal d'annonces est accessibles à tous⋅tes, mais seul⋅es les admins peuvent y écrire.

View File

@ -1,6 +1,7 @@
# Copyright (C) 2021 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.core.management import BaseCommand
from django.utils.formats import date_format
from django.utils.translation import activate
@ -9,7 +10,7 @@ from participation.models import Tournament
class Command(BaseCommand):
def handle(self, *args, **kwargs):
activate('fr')
activate(settings.PREFERRED_LANGUAGE_CODE)
tournaments = Tournament.objects.order_by('-date_start', 'name')
for tournament in tournaments:

View File

@ -11,7 +11,7 @@ from participation.models import Solution, Tournament
class Command(BaseCommand):
def handle(self, *args, **kwargs):
activate('fr')
activate(settings.PROBLEMS)
base_dir = Path(__file__).parent.parent.parent.parent
base_dir /= "output"

View File

@ -12,7 +12,7 @@ from ...models import Passage, Tournament
class Command(BaseCommand):
def handle(self, *args, **options):
activate('fr')
activate(settings.PREFERRED_LANGUAGE_CODE)
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
try:
spreadsheet = gc.open("Tableau des deuxièmes", folder_id=settings.NOTES_DRIVE_FOLDER_ID)

View File

@ -81,12 +81,12 @@ class Team(models.Model):
return False
if any(not r.photo_authorization for r in self.participants.all()):
return False
if not self.motivation_letter:
if settings.MOTIVATION_LETTER_REQUIRED and not self.motivation_letter:
return False
if not self.participation.tournament.remote:
if any(r.under_18 and not r.health_sheet for r in self.students.all()):
if settings.HEALTH_SHEET_REQUIRED and any(r.under_18 and not r.health_sheet for r in self.students.all()):
return False
if any(r.under_18 and not r.vaccine_sheet for r in self.students.all()):
if settings.VACCINE_SHEET_REQUIRED and any(r.under_18 and not r.vaccine_sheet for r in self.students.all()):
return False
if any(r.under_18 and not r.parental_authorization for r in self.students.all()):
return False
@ -119,7 +119,7 @@ class Team(models.Model):
'content': content,
})
if not self.motivation_letter:
if settings.MOTIVATION_LETTER_REQUIRED and not self.motivation_letter:
text = _("The team {trigram} has not uploaded a motivation letter. "
"You can upload your motivation letter using <a href='{url}'>this link</a>.")
url = reverse_lazy("participation:upload_team_motivation_letter", args=(self.pk,))
@ -458,7 +458,7 @@ class Tournament(models.Model):
self.save()
def update_ranking_spreadsheet(self): # noqa: C901
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
spreadsheet = gc.open_by_key(self.notes_sheet_id)
@ -1101,7 +1101,7 @@ class Pool(models.Model):
return super().validate_constraints()
def update_spreadsheet(self): # noqa: C901
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
# Create tournament sheet if it does not exist
self.tournament.create_spreadsheet()
@ -1446,7 +1446,7 @@ class Pool(models.Model):
worksheet.client.batch_update(spreadsheet.id, body)
def update_juries_lines_spreadsheet(self):
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
@ -1467,7 +1467,7 @@ class Pool(models.Model):
max_row += 1
def parse_spreadsheet(self):
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
self.tournament.create_spreadsheet()
@ -1837,7 +1837,7 @@ class Note(models.Model):
if not self.has_any_note():
return
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
passage = Passage.objects.prefetch_related('pool__tournament', 'pool__participations').get(pk=self.passage.pk)

View File

@ -74,6 +74,7 @@
{% endif %}
{% if not team.participation.tournament.remote %}
{% if TFJM.HEALTH_SHEET_REQUIRED %}
<dt class="col-sm-6 text-sm-end">{% trans "Health sheets:" %}</dt>
<dd class="col-sm-6">
{% for student in team.students.all %}
@ -86,7 +87,9 @@
{% endif %}
{% endfor %}
</dd>
{% endif %}
{% if TFJM.VACCINE_SHEET_REQUIRED %}
<dt class="col-sm-6 text-sm-end">{% trans "Vaccine sheets:" %}</dt>
<dd class="col-sm-6">
{% for student in team.students.all %}
@ -99,6 +102,7 @@
{% endif %}
{% endfor %}
</dd>
{% endif %}
<dt class="col-sm-6 text-sm-end">{% trans "Parental authorizations:" %}</dt>
<dd class="col-sm-6">
@ -129,6 +133,7 @@
{% endif %}
{% endif %}
{% if TFJM.MOTIVATION_LETTER_REQUIRED %}
<dt class="col-sm-6 text-sm-end">{% trans "Motivation letter:" %}</dt>
<dd class="col-sm-6">
{% if team.motivation_letter %}
@ -140,6 +145,7 @@
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadMotivationLetterModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
{% endif %}
{% if user.registration.is_volunteer %}
{% if user.registration in self.team.participation.tournament.organizers or user.registration.is_admin %}
@ -234,10 +240,12 @@
{% endif %}
{% endif %}
{% if TFJM.MOTIVATION_LETTER_REQUIRED %}
{% trans "Upload motivation letter" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "participation:upload_team_motivation_letter" pk=team.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadMotivationLetter" modal_enctype="multipart/form-data" %}
{% endif %}
{% trans "Update team" as modal_title %}
{% trans "Update" as modal_button %}
@ -253,7 +261,9 @@
{% block extrajavascript %}
<script>
document.addEventListener('DOMContentLoaded', () => {
{% if TFJM.MOTIVATION_LETTER_REQUIRED %}
initModal("uploadMotivationLetter", "{% url "participation:upload_team_motivation_letter" pk=team.pk %}")
{% endif %}
initModal("updateTeam", "{% url "participation:update_team" pk=team.pk %}")
initModal("leaveTeam", "{% url "participation:team_leave" %}")
})

View File

@ -3,6 +3,7 @@
from datetime import date, datetime
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.mail import send_mail
from django.core.validators import MaxValueValidator, MinValueValidator
@ -307,7 +308,7 @@ class ParticipantRegistration(Registration):
"""
The team is selected for final.
"""
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
subject = "[TFJM²] " + str(_("Team selected for the final tournament"))
site = Site.objects.first()
from participation.models import Tournament
@ -425,7 +426,7 @@ class StudentRegistration(ParticipantRegistration):
'priority': 5,
'content': content,
})
if not self.health_sheet:
if settings.HEALTH_SHEET_REQUIRED and not self.health_sheet:
text = _("You have not uploaded your health sheet. "
"You can do it by clicking on <a href=\"{health_url}\">this link</a>.")
health_url = reverse_lazy("registration:upload_user_health_sheet", args=(self.id,))
@ -436,7 +437,7 @@ class StudentRegistration(ParticipantRegistration):
'priority': 5,
'content': content,
})
if not self.vaccine_sheet:
if settings.VACCINE_SHEET_REQUIRED and not self.vaccine_sheet:
text = _("You have not uploaded your vaccine sheet. "
"You can do it by clicking on <a href=\"{vaccine_url}\">this link</a>.")
vaccine_url = reverse_lazy("registration:upload_user_vaccine_sheet", args=(self.id,))
@ -801,7 +802,7 @@ class Payment(models.Model):
return checkout_intent
def send_remind_mail(self):
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
subject = "[TFJM²] " + str(_("Reminder for your payment"))
site = Site.objects.first()
for registration in self.registrations.all():
@ -812,7 +813,7 @@ class Payment(models.Model):
registration.user.email_user(subject, message, html_message=html)
def send_helloasso_payment_confirmation_mail(self):
translation.activate('fr')
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
subject = "[TFJM²] " + str(_("Payment confirmation"))
site = Site.objects.first()
for registration in self.registrations.all():

View File

@ -0,0 +1,66 @@
\documentclass[a4paper,french,11pt]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage[french]{babel}
\usepackage{fancyhdr}
\usepackage{graphicx}
\usepackage{amsmath}
\usepackage{amssymb}
%\usepackage{anyfontsize}
\usepackage{fancybox}
\usepackage{eso-pic,graphicx}
\usepackage{xcolor}
% Specials
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
% Page formating
\hoffset -1in
\voffset -1in
\textwidth 180 mm
\textheight 250 mm
\oddsidemargin 15mm
\evensidemargin 15mm
\pagestyle{fancy}
% Headers and footers
\fancyfoot{}
\lhead{}
\rhead{}
\renewcommand{\headrulewidth}{0pt}
% \lfoot{\footnotesize Address}
% \rfoot{\footnotesize todo association}
\begin{document}
\includegraphics[height=2cm]{/code/static/eteam.png}\hfill{\fontsize{55pt}{55pt}ETEAM Tournament}
\vfill
\begin{center}
\Large \bf Parental authorisation for minors
\end{center}
I, \hrulefill,\\
legal representative, residing at \writingsep\hrulefill\\
\writingsep\hrulefill,\\
\writingsep autorise {{ registration|default:"\hrulefill" }},\\
born on {{ registration.birth_date }},
to participate in the European Tournament of Enthusiastic Apprentice Mathematicians (ETEAM) organised in:
{{ tournament.place }}, from {{ tournament.date_start }} to {{ tournament.date_end }}.
The participant will travel to the abovementioned location on Monday morning and will leave the premises on Friday afternoon by independant means and under the responsibility of the legal representative.
\vspace{8ex}
Signature:
\vfill
\vfill
\end{document}

View File

@ -0,0 +1,112 @@
\documentclass[a4paper,french,11pt]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage[french]{babel}
\usepackage{fancyhdr}
\usepackage{graphicx}
\usepackage{amsmath}
\usepackage{amssymb}
%\usepackage{anyfontsize}
\usepackage{fancybox}
\usepackage{eso-pic,graphicx}
\usepackage{xcolor}
% Specials
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
% Page formating
\hoffset -1in
\voffset -1in
\textwidth 180 mm
\textheight 250 mm
\oddsidemargin 15mm
\evensidemargin 15mm
\pagestyle{fancy}
% Headers and footers
\fancyfoot{}
\lhead{}
\rhead{}
\renewcommand{\headrulewidth}{0pt}
%\lfoot{\footnotesize Address}
%\rfoot{\footnotesize todo association}
\begin{document}
\includegraphics[height=2cm]{/code/static/eteam.png}\hfill{\fontsize{55pt}{55pt}{ETEAM Tournament}}
\vfill
\begin{center}
\LARGE
Video and interview consent and release form
\end{center}
\normalsize
\thispagestyle{empty}
\bigskip
I, {{ registration|safe|default:"\dotfill" }}\\
residing at {{ registration.address|safe|default:"\dotfill" }} {{ registration.zip_code|safe|default:"" }} {{ registration.city|safe|default:"" }}
{{ registration.country|safe|default:"" }},\\
\medskip
Tick the appropriate box(es).\\
\medskip
\fbox{\textcolor{white}{A}} Authorise the ETEAM organizers, for the ETEAM tournament from {{ tournament.date_start }} to {{ tournament.date_end }} in: {{ tournament.place }}, to photograph or film me and to distribute the photos and/or videos taken on this occasion on its website and on partner websites. I hereby grant ETEAM the right to use my image free of charge on all its information media: brochures, websites, social networks. ETEAM hereby becomes the assignee of the rights for these photographs. There is no time limit on the validity of this release nor are there any geographic limitations on where these materials may be distributed.\\
\medskip
ETEAM commits itself, in accordance with the legal regulations in force relating to image rights, to ensuring that the publication and distribution of the image as well as the accompanying comments do not infringe on the private life, dignity and reputation of the person photographed.\\
\medskip
\fbox{\textcolor{white}{A}} Authorise the broadcasting in the media (Press, Television, Internet) of photographs taken during any media coverage of this event.\\
\medskip
\medskip
\fbox{\textcolor{white}{A}} By signing this form, I acknowledge that I have completely read and fully understand the above consent and release and agree to be bound thereby. I hereby release any and all claims against any person or organisation utilising this material for marketing, educational, promotional, and/or any other lawful purpose whatsoever.\\
\medskip
\fbox{\textcolor{white}{A}} I agree to be kept informed of other activities organised by ETEAM and its partners.\\
\bigskip
Signature preceded by the words \og read and approved \fg{}
\medskip
\begin{minipage}[c]{0.5\textwidth}
\underline{Legal representative:}\\
\end{minipage}
\begin{minipage}[c]{0.5\textwidth}
\underline{The participant:}\\
\end{minipage}
\vfill
\vfill
\begin{minipage}[c]{0.5\textwidth}
% \footnotesize Address
\end{minipage}
\begin{minipage}[c]{0.5\textwidth}
\footnotesize
% \begin{flushright}
% todo association
% \end{flushright}
\end{minipage}
\end{document}

View File

@ -89,6 +89,7 @@
{% if user_object.registration.studentregistration %}
{% if user_object.registration.under_18 and user_object.registration.team.participation.tournament and not user_object.registration.team.participation.tournament.remote %}
{% if TFJM.HEALTH_SHEET_REQUIRED %}
<dt class="col-sm-6 text-sm-end">{% trans "Health sheet:" %}</dt>
<dd class="col-sm-6">
{% if user_object.registration.health_sheet %}
@ -98,7 +99,9 @@
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadHealthSheetModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
{% endif %}
{% if TFJM.VACCINE_SHEET_REQUIRED %}
<dt class="col-sm-6 text-sm-end">{% trans "Vaccine sheet:" %}</dt>
<dd class="col-sm-6">
{% if user_object.registration.vaccine_sheet %}
@ -108,6 +111,7 @@
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadVaccineSheetModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
{% endif %}
<dt class="col-sm-6 text-sm-end">{% trans "Parental authorization:" %}</dt>
<dd class="col-sm-6">
@ -165,7 +169,7 @@
<dd class="col-sm-6">{{ user_object.registration.give_contact_to_animath|yesno }}</dd>
</dl>
{% if user_object.registration.participates and user_object.registration.team.participation.valid %}
{% if TFJM.PAYMENT_MANAGEMENT and user_object.registration.participates and user_object.registration.team.participation.valid %}
<hr>
{% for payment in user_object.registration.payments.all %}
<dl class="row">
@ -226,15 +230,19 @@
{% include "base_modal.html" with modal_id="uploadPhotoAuthorization" modal_enctype="multipart/form-data" %}
{% if user_object.registration.under_18 %}
{% if TFJM.HEALTH_SHEET_REQUIRED %}
{% 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" %}
{% endif %}
{% if TFJM.VACCINE_SHEET_REQUIRED %}
{% 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" %}
{% endif %}
{% trans "Upload parental authorization" as modal_title %}
{% trans "Upload" as modal_button %}

View File

@ -442,7 +442,10 @@ class AuthorizationTemplateView(TemplateView):
return context
def render_to_response(self, context, **response_kwargs):
tex = render_to_string(self.template_name, context=context, request=self.request)
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
template_name = self.get_template_names()[0]
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)
@ -451,20 +454,34 @@ class AuthorizationTemplateView(TemplateView):
process.wait()
return FileResponse(open(os.path.join(temp_dir, "texput.pdf"), "rb"),
content_type="application/pdf",
filename=self.template_name.split("/")[-1][:-3] + "pdf")
filename=template_name.split("/")[-1][:-3] + "pdf")
class AdultPhotoAuthorizationTemplateView(AuthorizationTemplateView):
template_name = "registration/tex/Autorisation_droit_image_majeur.tex"
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):
template_name = "registration/tex/Autorisation_droit_image_mineur.tex"
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"

View File

@ -11,5 +11,8 @@ def tfjm_context(request):
'PAYMENT_MANAGEMENT': settings.PAYMENT_MANAGEMENT,
'SINGLE_TOURNAMENT':
Tournament.objects.first() if Tournament.objects.exists() and settings.TFJM_APP else None,
'HEALTH_SHEET_REQUIRED': settings.HEALTH_SHEET_REQUIRED,
'VACCINE_SHEET_REQUIRED': settings.VACCINE_SHEET_REQUIRED,
'MOTIVATION_LETTER_REQUIRED': settings.MOTIVATION_LETTER_REQUIRED,
}
}

View File

@ -346,11 +346,15 @@ except ImportError:
pass
if TFJM_APP == "TFJM":
PREFERRED_LANGUAGE_CODE = 'fr'
TEAM_CODE_LENGTH = 3
RECOMMENDED_SOLUTIONS_COUNT = 5
NB_ROUNDS = 2
ML_MANAGEMENT = True
PAYMENT_MANAGEMENT = True
HEALTH_SHEET_REQUIRED = True
VACCINE_SHEET_REQUIRED = True
MOTIVATION_LETTER_REQUIRED = True
PROBLEMS = [
"Triominos",
@ -363,11 +367,16 @@ if TFJM_APP == "TFJM":
"Création d'un jeu",
]
elif TFJM_APP == "ETEAM":
PREFERRED_LANGUAGE_CODE = 'en'
TEAM_CODE_LENGTH = 4
RECOMMENDED_SOLUTIONS_COUNT = 6
NB_ROUNDS = 3
ML_MANAGEMENT = False
PAYMENT_MANAGEMENT = False
HEALTH_SHEET_REQUIRED = False
VACCINE_SHEET_REQUIRED = False
MOTIVATION_LETTER_REQUIRED = False
PROBLEMS = [
"Exploring Flatland",