Add archive with all notation sheets

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-03-29 18:59:37 +01:00
parent a44439671e
commit 6b16ed3cc8
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
7 changed files with 222 additions and 99 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-27 00:47+0100\n"
"POT-Creation-Date: 2024-03-29 18:56+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -214,17 +214,17 @@ msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}"
#: draw/models.py:408 draw/models.py:435 participation/admin.py:136
#: participation/admin.py:155 participation/models.py:597
#: participation/models.py:606 participation/tables.py:84
#: participation/admin.py:155 participation/models.py:601
#: participation/models.py:610 participation/tables.py:84
msgid "pool"
msgstr "poule"
#: draw/models.py:409 participation/models.py:598
#: draw/models.py:409 participation/models.py:602
msgid "pools"
msgstr "poules"
#: draw/models.py:421 participation/models.py:503 participation/models.py:767
#: participation/models.py:797 participation/models.py:839
#: draw/models.py:421 participation/models.py:503 participation/models.py:771
#: participation/models.py:801 participation/models.py:843
msgid "participation"
msgstr "participation"
@ -248,8 +248,8 @@ msgid ""
msgstr ""
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
#: draw/models.py:458 draw/models.py:481 participation/models.py:620
#: participation/models.py:804
#: draw/models.py:458 draw/models.py:481 participation/models.py:624
#: participation/models.py:808
#, python-brace-format
msgid "Problem #{problem}"
msgstr "Problème n°{problem}"
@ -344,8 +344,8 @@ msgstr "équipe"
#: draw/templates/draw/tournament_content.html:228
#: draw/templates/draw/tournament_content.html:229
#: draw/templates/draw/tournament_content.html:230
#: participation/templates/participation/pool_detail.html:84
#: participation/templates/participation/pool_detail.html:88
#: participation/templates/participation/pool_detail.html:85
#: participation/templates/participation/pool_detail.html:89
#: participation/templates/participation/pool_detail.html:94
#: participation/templates/participation/pool_detail.html:98
msgid "Room"
@ -456,21 +456,21 @@ msgid "selected for final"
msgstr "sélectionnée pour la finale"
#: participation/admin.py:124 participation/admin.py:183
#: participation/models.py:627 participation/tables.py:112
#: participation/models.py:631 participation/tables.py:112
msgid "defender"
msgstr "défenseur⋅se"
#: participation/admin.py:128 participation/models.py:634
#: participation/models.py:851
#: participation/admin.py:128 participation/models.py:638
#: participation/models.py:855
msgid "opponent"
msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:641
#: participation/models.py:852
#: participation/admin.py:132 participation/models.py:645
#: participation/models.py:856
msgid "reporter"
msgstr "rapporteur⋅e"
#: participation/admin.py:187 participation/models.py:802
#: participation/admin.py:187 participation/models.py:806
msgid "problem"
msgstr "numéro de problème"
@ -529,7 +529,7 @@ msgid "The PDF file must not have more than 30 pages."
msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
#: participation/forms.py:236
#: participation/templates/participation/pool_detail.html:123
#: participation/templates/participation/pool_detail.html:130
msgid "Add"
msgstr "Ajouter"
@ -888,32 +888,32 @@ msgstr ""
"Ils restent toujours accessibles pour vous. Seules les moyennes sont "
"communiquées."
#: participation/models.py:587
#: participation/models.py:591
msgid "The president of the jury must be part of the jury."
msgstr "Læ président⋅e du jury doit faire partie du jury."
#: participation/models.py:591
#: participation/models.py:595
#, python-brace-format
msgid "Pool of day {round} for tournament {tournament} with teams {teams}"
msgstr "Poule du jour {round} du tournoi {tournament} avec les équipes {teams}"
#: participation/models.py:611
#: participation/models.py:615
msgid "position"
msgstr "position"
#: participation/models.py:618
#: participation/models.py:622
msgid "defended solution"
msgstr "solution défendue"
#: participation/models.py:651
#: participation/models.py:655
msgid "observer"
msgstr "observateur⋅rice"
#: participation/models.py:656
#: participation/models.py:660
msgid "penalties"
msgstr "pénalités"
#: participation/models.py:658
#: participation/models.py:662
msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty."
@ -921,124 +921,124 @@ msgstr ""
"Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 "
"sur sa présentation orale par pénalité."
#: participation/models.py:734 participation/models.py:737
#: participation/models.py:740 participation/models.py:743
#: participation/models.py:738 participation/models.py:741
#: participation/models.py:744 participation/models.py:747
#, python-brace-format
msgid "Team {trigram} is not registered in the pool."
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: participation/models.py:748
#: participation/models.py:752
#, python-brace-format
msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}"
#: participation/models.py:752 participation/models.py:761
#: participation/models.py:846 participation/models.py:888
#: participation/models.py:756 participation/models.py:765
#: participation/models.py:850 participation/models.py:892
msgid "passage"
msgstr "passage"
#: participation/models.py:753
#: participation/models.py:757
msgid "passages"
msgstr "passages"
#: participation/models.py:772
#: participation/models.py:776
msgid "difference"
msgstr "différence"
#: participation/models.py:773
#: participation/models.py:777
msgid "Score to add/remove on the final score"
msgstr "Score à ajouter/retrancher au score final"
#: participation/models.py:780
#: participation/models.py:784
msgid "tweak"
msgstr "harmonisation"
#: participation/models.py:781
#: participation/models.py:785
msgid "tweaks"
msgstr "harmonisations"
#: participation/models.py:809
#: participation/models.py:813
msgid "solution for the final tournament"
msgstr "solution pour la finale"
#: participation/models.py:814 participation/models.py:857
#: participation/models.py:818 participation/models.py:861
msgid "file"
msgstr "fichier"
#: participation/models.py:824
#: participation/models.py:828
#, python-brace-format
msgid "Solution of team {team} for problem {problem}"
msgstr "Solution de l'équipe {team} pour le problème {problem}"
#: participation/models.py:826
#: participation/models.py:830
msgid "for final"
msgstr "pour la finale"
#: participation/models.py:829
#: participation/models.py:833
msgid "solution"
msgstr "solution"
#: participation/models.py:830
#: participation/models.py:834
msgid "solutions"
msgstr "solutions"
#: participation/models.py:863
#: participation/models.py:867
#, python-brace-format
msgid "Synthesis of {team} as {type} for problem {problem} of {defender}"
msgstr ""
"Note de synthèse de l'équipe {team} en tant que {type} pour le problème "
"{problem} de {defender}"
#: participation/models.py:871
#: participation/models.py:875
msgid "synthesis"
msgstr "note de synthèse"
#: participation/models.py:872
#: participation/models.py:876
msgid "syntheses"
msgstr "notes de synthèse"
#: participation/models.py:881
#: participation/models.py:885
msgid "jury"
msgstr "jury"
#: participation/models.py:893
#: participation/models.py:897
msgid "defender writing note"
msgstr "note d'écrit de la défense"
#: participation/models.py:899
#: participation/models.py:903
msgid "defender oral note"
msgstr "note d'oral de la défense"
#: participation/models.py:905
#: participation/models.py:909
msgid "opponent writing note"
msgstr "note d'écrit de l'opposition"
#: participation/models.py:911
#: participation/models.py:915
msgid "opponent oral note"
msgstr "note d'oral de l'opposition"
#: participation/models.py:917
#: participation/models.py:921
msgid "reporter writing note"
msgstr "note d'écrit du rapportage"
#: participation/models.py:923
#: participation/models.py:927
msgid "reporter oral note"
msgstr "note d'oral du rapportage"
#: participation/models.py:929
#: participation/models.py:933
msgid "observer note"
msgstr "note de l'observation"
#: participation/models.py:965
#: participation/models.py:969
#, python-brace-format
msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}"
#: participation/models.py:968
#: participation/models.py:972
msgid "note"
msgstr "note"
#: participation/models.py:969
#: participation/models.py:973
msgid "notes"
msgstr "notes"
@ -1077,9 +1077,9 @@ msgstr "Pas d'équipe définie"
#: participation/templates/participation/passage_detail.html:54
#: participation/templates/participation/passage_detail.html:120
#: participation/templates/participation/passage_detail.html:126
#: participation/templates/participation/pool_detail.html:110
#: participation/templates/participation/pool_detail.html:128
#: participation/templates/participation/pool_detail.html:133
#: participation/templates/participation/pool_detail.html:117
#: participation/templates/participation/pool_detail.html:135
#: participation/templates/participation/pool_detail.html:140
#: participation/templates/participation/team_detail.html:151
#: participation/templates/participation/team_detail.html:215
#: participation/templates/participation/tournament_form.html:12
@ -1172,7 +1172,7 @@ msgstr "Envoyer une solution"
#: participation/templates/participation/participation_detail.html:65
#: participation/templates/participation/passage_detail.html:132
#: participation/templates/participation/pool_detail.html:138
#: participation/templates/participation/pool_detail.html:145
#: participation/templates/participation/team_detail.html:210
#: participation/templates/participation/upload_motivation_letter.html:13
#: participation/templates/participation/upload_notes.html:17
@ -1333,7 +1333,7 @@ msgstr "Lien BigBlueButton :"
msgid "Ranking"
msgstr "Classement"
#: participation/templates/participation/pool_detail.html:84
#: participation/templates/participation/pool_detail.html:85
msgid "Download the scale sheet"
msgstr "Télécharger la feuille de barème"
@ -1341,30 +1341,34 @@ msgstr "Télécharger la feuille de barème"
msgid "Download the final notation sheet"
msgstr "Télécharger la fiche de notation finale"
#: participation/templates/participation/pool_detail.html:102
#: participation/templates/participation/pool_detail.html:103
msgid "Download all notation sheets"
msgstr "Télécharger toutes les fiches de notation"
#: participation/templates/participation/pool_detail.html:108
msgid "Upload notes from a CSV file"
msgstr "Soumettre les notes à partir d'un fichier CSV"
#: participation/templates/participation/pool_detail.html:109
#: participation/templates/participation/pool_detail.html:122
#: participation/templates/participation/pool_detail.html:116
#: participation/templates/participation/pool_detail.html:129
msgid "Add passage"
msgstr "Ajouter un passage"
#: participation/templates/participation/pool_detail.html:111
#: participation/templates/participation/pool_detail.html:132
#: participation/templates/participation/pool_detail.html:118
#: participation/templates/participation/pool_detail.html:139
msgid "Update teams"
msgstr "Modifier les équipes"
#: participation/templates/participation/pool_detail.html:118
#: participation/templates/participation/pool_detail.html:125
msgid "Passages"
msgstr "Passages"
#: participation/templates/participation/pool_detail.html:127
#: participation/templates/participation/pool_detail.html:134
#: participation/templates/participation/pool_form.html:11
msgid "Update pool"
msgstr "Modifier la poule"
#: participation/templates/participation/pool_detail.html:137
#: participation/templates/participation/pool_detail.html:144
msgid "Upload notes"
msgstr "Envoyer les notes"
@ -1876,7 +1880,17 @@ msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e
msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1811
#: participation/views.py:1739
#, python-brace-format
msgid "Notation sheets of pool {pool} of {tournament}.zip"
msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:1744
#, python-brace-format
msgid "Notation sheets of {tournament}.zip"
msgstr "Feuilles de notation de {tournament}.zip"
#: participation/views.py:1911
msgid "You can't upload a synthesis after the deadline."
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."

View File

@ -567,6 +567,10 @@ class Pool(models.Model):
"They stay accessible to you. Only averages are given."),
)
@property
def short_name(self):
return f"{self.get_letter_display()}{self.round}"
@property
def solutions(self):
return [passage.defended_solution for passage in self.passages.all()]

View File

@ -80,7 +80,8 @@
{% if user.registration.is_volunteer %}
<div class="card-footer text-center">
<div class="btn-group">
<a class="btn btn-info" href="{% url 'participation:pool_scale_note_sheet' pk=pool.pk %}">
<a class="btn btn-sm btn-info" href="{% url 'participation:pool_scale_note_sheet' pk=pool.pk %}">
<i class="fas fa-download"></i>
{% trans "Download the scale sheet" %}{% if pool.passages.count == 5 %} — {% trans "Room" %} 1{% endif %}
</a>
{% if pool.passages.count == 5 %}
@ -88,18 +89,24 @@
{% trans "Room" %} 2
</a>
{% endif %}
</div>
<div class="btn-group">
<a class="btn btn-info" href="{% url 'participation:pool_final_note_sheet' pk=pool.pk %}">
<a class="btn btn-sm btn-info" href="{% url 'participation:pool_final_note_sheet' pk=pool.pk %}">
<i class="fas fa-download"></i>
{% trans "Download the final notation sheet" %}{% if pool.passages.count == 5 %} — {% trans "Room" %} 1{% endif %}
</a>
{% if pool.passages.count == 5 %}
<a class="btn btn-info" href="{% url 'participation:pool_final_note_sheet' pk=pool.pk %}?page=2">
<a class="btn btn-sm btn-info" href="{% url 'participation:pool_final_note_sheet' pk=pool.pk %}?page=2">
{% trans "Room" %} 2
</a>
{% endif %}
<a class="btn btn-sm btn-info" href="{% url 'participation:pool_notation_sheets' pool_id=pool.id %}">
<i class="fas fa-archive"></i>
{% trans "Download all notation sheets" %}
</a>
</div>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadNotesModal">{% trans "Upload notes from a CSV file" %}</button>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#uploadNotesModal">
<i class="fas fa-upload"></i>
{% trans "Upload notes from a CSV file" %}
</button>
</div>
{% endif %}
</div>

View File

@ -82,7 +82,7 @@ Tour {{ pool.round }} \;-- Poule {{ pool.get_letter_display }}{{ page }} \;-- {%
\vspace{15mm}
\LARGE Nom jur\'e\textperiodcentered{}e :
{% if is_jury %}\underline{ {{ user.first_name|safe }} {{ user.last_name|safe }} }{% else %}\underline{\phantom{Phrase suffisamment longue pour le nom}}{% endif %}
{% if jury %}\underline{ {{ jury.user.first_name|safe }} {{ jury.user.last_name|safe }} }{% else %}\underline{\phantom{Phrase suffisamment longue pour le nom}}{% endif %}
$\qquad$ Signature : \underline{\phantom{Phrase moins longue}}
\newpage

View File

@ -195,13 +195,8 @@
</a>
</li>
<li>
<a>
Archive de tous les barèmes de notes à imprimer triés par poule
</a>
</li>
<li>
<a>
Archive de tous les tableurs de notes à saisir triés par poule
<a href="{% url "participation:tournament_notation_sheets" tournament_id=tournament.id %}">
Archive de toutes les feuilles de notes à imprimer triées par poule
</a>
</li>
</ul>

View File

@ -5,8 +5,8 @@ from django.urls import path
from django.views.generic import TemplateView
from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, MyParticipationDetailView, \
MyTeamDetailView, NoteUpdateView, ParticipationDetailView, PassageCreateView, PassageDetailView, \
PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
MyTeamDetailView, NotationSheetsArchiveView, NoteUpdateView, ParticipationDetailView, PassageCreateView, \
PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, \
ScaleNotationSheetTemplateView, SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
@ -43,6 +43,8 @@ urlpatterns = [
name="tournament_solutions"),
path("tournament/<int:tournament_id>/syntheses/", SolutionsDownloadView.as_view(),
name="tournament_syntheses"),
path("tournament/<int:tournament_id>/notation/sheets/", NotationSheetsArchiveView.as_view(),
name="tournament_notation_sheets"),
path("tournament/<int:pk>/publish-notes/<int:round>/", TournamentPublishNotesView.as_view(),
name="tournament_publish_notes"),
path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
@ -52,6 +54,7 @@ urlpatterns = [
path("pools/<int:pool_id>/syntheses/", SolutionsDownloadView.as_view(), name="pool_download_syntheses"),
path("pools/<int:pk>/notation/scale/", ScaleNotationSheetTemplateView.as_view(), name="pool_scale_note_sheet"),
path("pools/<int:pk>/notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"),
path("pools/<int:pool_id>/notation/sheets/", NotationSheetsArchiveView.as_view(), name="pool_notation_sheets"),
path("pools/<int:pk>/update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"),
path("pools/<int:pk>/jury/", PoolJuryView.as_view(), name="pool_jury"),
path("pools/<int:pk>/jury/remove/<int:jury_id>/", PoolRemoveJuryView.as_view(), name="pool_remove_jury"),

View File

@ -650,7 +650,7 @@ class TournamentExportCSVView(VolunteerMixin, DetailView):
if 'all' not in request.GET:
participations = participations.filter(valid=True)
for participation in participations.order_by('-valid', 'team__trigram').all():
for registration in participation.team.participants\
for registration in participation.team.participants \
.order_by('coachregistration', 'user__last_name').all():
writer.writerow({
'Tournoi': tournament.name,
@ -1652,6 +1652,17 @@ class NotationSheetTemplateView(VolunteerMixin, DetailView):
"""
model = Pool
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
if request.user.is_authenticated and \
(request.user.registration.is_admin or request.user.registration.is_volunteer
and (self.object.tournament in request.user.registration.organized_tournaments.all()
or request.user.registration == self.object.jury_president)):
return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -1666,8 +1677,8 @@ class NotationSheetTemplateView(VolunteerMixin, DetailView):
context['passages'] = passages
context['esp'] = passages.count() * '&'
context['is_jury'] = self.request.user.registration in self.object.juries.all() \
and 'blank' not in self.request.GET
if self.request.user.registration in self.object.juries.all() and 'blank' not in self.request.GET:
context['jury'] = self.request.user.registration
context['tfjm_number'] = timezone.now().year - 2010
return context
@ -1692,6 +1703,95 @@ class FinalNotationSheetTemplateView(NotationSheetTemplateView):
template_name = 'participation/tex/finale.tex'
class NotationSheetsArchiveView(VolunteerMixin, DetailView):
@property
def model(self):
return Pool if 'pool_id' in self.kwargs else Tournament
@property
def pk_url_kwarg(self):
return 'pool_id' if 'pool_id' in self.kwargs else 'tournament_id'
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
reg = request.user.registration
if 'pool_id' in kwargs:
pool = self.get_object()
tournament = pool.tournament
if reg.is_admin or reg.is_volunteer \
and (tournament in reg.organized_tournaments.all() or reg in pool.juries.all()):
return super().dispatch(request, *args, **kwargs)
else:
tournament = self.get_object()
if reg.is_admin or reg.is_volunteer and tournament in reg.organized_tournaments.all():
return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission()
def get(self, request, *args, **kwargs):
if 'pool_id' in kwargs:
pool = self.get_object()
tournament = pool.tournament
pools = [pool]
filename = _("Notation sheets of pool {pool} of {tournament}.zip") \
.format(pool=pool.short_name, tournament=tournament.name)
else:
tournament = self.get_object()
pools = tournament.pools.all()
filename = _("Notation sheets of {tournament}.zip").format(tournament=tournament.name)
output = BytesIO()
with ZipFile(output, "w") as zf:
for pool in pools:
prefix = f"{pool.short_name}/" if len(pools) > 1 else ""
for template_name in ['bareme', 'finale']:
pages = [1] if pool.participations.count() < 5 else [1, 2]
for page in pages:
juries = list(pool.juries.all()) + [None]
for jury in juries:
if jury is not None and template_name == "bareme":
continue
context = {'jury': jury, 'page': page, 'pool': pool,
'tfjm_number': timezone.now().year - 2010}
passages = pool.passages.all()
if passages.count() == 5:
passages = passages.filter(id__in=[passages[0].id, passages[2].id, passages[4].id]
if page == '1' else [passages[1].id, passages[3].id])
context['passages'] = passages
context['esp'] = passages.count() * '&'
tex = render_to_string(f"participation/tex/{template_name}.tex",
context=context, request=self.request)
temp_dir = mkdtemp()
with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
f.write(tex)
process = subprocess.Popen(
["pdflatex", "-interaction=nonstopmode", f"-output-directory={temp_dir}",
os.path.join(temp_dir, "texput.tex"), ])
process.wait()
sheet_name = f"Barème pour la poule {pool.short_name}" if template_name == "bareme" \
else (f"Feuille de notation pour la poule {pool.short_name}"
f" - {str(jury) if jury else 'Vierge'}")
sheet_name += " - page 2" if page == 2 else ""
zf.write(os.path.join(temp_dir, "texput.pdf"),
f"{prefix}{sheet_name}.pdf")
response = HttpResponse(content_type="application/zip")
response["Content-Disposition"] = f"attachment; filename=\"{filename}\""
response.write(output.getvalue())
return response
class PassageCreateView(VolunteerMixin, CreateView):
model = Passage
form_class = PassageForm