From 29074c4bfd80523c2b083441130c001cb75d5790 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Fri, 19 May 2023 14:44:31 +0200 Subject: [PATCH] Add button to download all solutions and syntheses in a ZIP file Signed-off-by: Emmy D'Anello --- locale/fr/LC_MESSAGES/django.po | 239 ++++++++++-------- participation/models.py | 2 +- .../templates/participation/pool_detail.html | 24 +- participation/urls.py | 8 +- participation/views.py | 36 +++ 5 files changed, 193 insertions(+), 116 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index b152265..e700730 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-11 22:22+0200\n" +"POT-Creation-Date: 2023-05-19 14:40+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -22,19 +22,19 @@ msgid "API" msgstr "API" #: draw/admin.py:16 draw/admin.py:28 draw/admin.py:44 participation/admin.py:44 -#: participation/models.py:126 participation/tables.py:86 +#: participation/models.py:126 participation/tables.py:87 msgid "teams" msgstr "équipes" #: draw/admin.py:40 draw/admin.py:56 draw/models.py:24 #: participation/admin.py:16 participation/admin.py:73 #: participation/admin.py:104 participation/models.py:295 -#: participation/models.py:319 participation/models.py:351 +#: participation/models.py:319 participation/models.py:352 msgid "tournament" msgstr "tournoi" #: draw/admin.py:60 draw/models.py:231 draw/models.py:426 -#: participation/models.py:355 +#: participation/models.py:356 msgid "round" msgstr "tour" @@ -46,64 +46,64 @@ msgstr "Tirage au sort" msgid "You are not an organizer." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice." -#: draw/consumers.py:144 +#: draw/consumers.py:151 msgid "The draw is already started." msgstr "Le tirage a déjà commencé." -#: draw/consumers.py:150 +#: draw/consumers.py:157 msgid "Invalid format" msgstr "Format invalide" -#: draw/consumers.py:155 +#: draw/consumers.py:162 #, python-brace-format msgid "The sum must be equal to the number of teams: expected {len}, got {sum}" msgstr "" "La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}" -#: draw/consumers.py:160 +#: draw/consumers.py:167 msgid "There can be at most one pool with 5 teams." msgstr "Il ne peut y avoir au plus qu'une seule poule de 5 équipes." -#: draw/consumers.py:188 +#: draw/consumers.py:207 msgid "Draw started!" msgstr "Le tirage a commencé !" -#: draw/consumers.py:208 +#: draw/consumers.py:229 #, python-brace-format msgid "The draw for the tournament {tournament} will start." msgstr "Le tirage au sort du tournoi {tournament} va commencer." -#: draw/consumers.py:219 draw/consumers.py:244 draw/consumers.py:578 -#: draw/consumers.py:767 draw/consumers.py:849 draw/consumers.py:866 -#: draw/consumers.py:936 draw/templates/draw/tournament_content.html:5 +#: draw/consumers.py:240 draw/consumers.py:266 draw/consumers.py:639 +#: draw/consumers.py:856 draw/consumers.py:945 draw/consumers.py:963 +#: draw/consumers.py:1053 draw/templates/draw/tournament_content.html:5 msgid "The draw has not started yet." msgstr "Le tirage au sort n'a pas encore commencé." -#: draw/consumers.py:231 +#: draw/consumers.py:253 #, python-brace-format msgid "The draw for the tournament {tournament} is aborted." msgstr "Le tirage au sort du tournoi {tournament} est annulé." -#: draw/consumers.py:271 draw/consumers.py:292 draw/consumers.py:524 -#: draw/consumers.py:583 draw/consumers.py:772 +#: draw/consumers.py:293 draw/consumers.py:314 draw/consumers.py:579 +#: draw/consumers.py:644 draw/consumers.py:861 msgid "This is not the time for this." msgstr "Ce n'est pas le moment pour cela." -#: draw/consumers.py:284 draw/consumers.py:287 +#: draw/consumers.py:306 draw/consumers.py:309 msgid "You've already launched the dice." msgstr "Vous avez déjà lancé le dé." -#: draw/consumers.py:290 +#: draw/consumers.py:312 msgid "It is not your turn." msgstr "Ce n'est pas votre tour." -#: draw/consumers.py:371 +#: draw/consumers.py:395 #, python-brace-format msgid "Dices from teams {teams} are identical. Please relaunch your dices." msgstr "" "Les dés des équipes {teams} sont identiques. Merci de relancer vos dés." -#: draw/consumers.py:869 +#: draw/consumers.py:966 msgid "This is only available for the final tournament." msgstr "Cela n'est possible que pour la finale." @@ -168,7 +168,7 @@ msgstr "La poule en cours, où les équipes choisissent leurs problèmes" msgid "rounds" msgstr "tours" -#: draw/models.py:254 participation/models.py:369 +#: draw/models.py:254 participation/models.py:370 msgid "letter" msgstr "lettre" @@ -207,17 +207,17 @@ msgid "Pool {letter}{number}" msgstr "Poule {letter}{number}" #: draw/models.py:407 draw/models.py:434 participation/admin.py:69 -#: participation/admin.py:88 participation/models.py:421 -#: participation/models.py:430 participation/tables.py:82 +#: participation/admin.py:88 participation/models.py:423 +#: participation/models.py:432 participation/tables.py:83 msgid "pool" msgstr "poule" -#: draw/models.py:408 participation/models.py:422 +#: draw/models.py:408 participation/models.py:424 msgid "pools" msgstr "poules" -#: draw/models.py:420 participation/models.py:342 participation/models.py:591 -#: participation/models.py:621 participation/models.py:659 +#: draw/models.py:420 participation/models.py:342 participation/models.py:593 +#: participation/models.py:623 participation/models.py:661 msgid "participation" msgstr "participation" @@ -241,8 +241,8 @@ msgid "" msgstr "" "L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1." -#: draw/models.py:457 draw/models.py:480 participation/models.py:444 -#: participation/models.py:628 +#: draw/models.py:457 draw/models.py:480 participation/models.py:446 +#: participation/models.py:630 #, python-brace-format msgid "Problem #{problem}" msgstr "Problème n°{problem}" @@ -331,10 +331,10 @@ 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:62 -#: participation/templates/participation/pool_detail.html:66 -#: participation/templates/participation/pool_detail.html:72 -#: participation/templates/participation/pool_detail.html:76 +#: participation/templates/participation/pool_detail.html:84 +#: participation/templates/participation/pool_detail.html:88 +#: participation/templates/participation/pool_detail.html:94 +#: participation/templates/participation/pool_detail.html:98 msgid "Room" msgstr "Salle" @@ -443,21 +443,21 @@ msgid "selected for final" msgstr "sélectionnée pour la finale" #: participation/admin.py:57 participation/admin.py:116 -#: participation/models.py:451 participation/tables.py:109 +#: participation/models.py:453 participation/tables.py:111 msgid "defender" msgstr "défenseur⋅se" -#: participation/admin.py:61 participation/models.py:458 -#: participation/models.py:671 +#: participation/admin.py:61 participation/models.py:460 +#: participation/models.py:673 msgid "opponent" msgstr "opposant⋅e" -#: participation/admin.py:65 participation/models.py:465 -#: participation/models.py:672 +#: participation/admin.py:65 participation/models.py:467 +#: participation/models.py:674 msgid "reporter" msgstr "rapporteur⋅e" -#: participation/admin.py:120 participation/models.py:626 +#: participation/admin.py:120 participation/models.py:628 msgid "problem" msgstr "numéro de problème" @@ -515,7 +515,7 @@ msgid "Add new jury" msgstr "Ajouter un⋅e nouvelleau juré⋅e" #: participation/forms.py:227 -#: participation/templates/participation/pool_detail.html:101 +#: participation/templates/participation/pool_detail.html:123 #: participation/templates/participation/tournament_detail.html:111 msgid "Add" msgstr "Ajouter" @@ -676,32 +676,32 @@ msgstr "L'équipe est sélectionnée pour la finale." msgid "Participation of the team {name} ({trigram})" msgstr "Participation de l'équipe {name} ({trigram})" -#: participation/models.py:343 participation/models.py:375 +#: participation/models.py:343 participation/models.py:376 msgid "participations" msgstr "participations" -#: participation/models.py:357 participation/models.py:358 +#: participation/models.py:358 participation/models.py:359 #, python-brace-format msgid "Round {round}" msgstr "Tour {round}" -#: participation/models.py:381 +#: participation/models.py:382 msgid "juries" msgstr "jurys" -#: participation/models.py:388 +#: participation/models.py:389 msgid "BigBlueButton URL" msgstr "Lien BigBlueButton" -#: participation/models.py:389 +#: participation/models.py:390 msgid "The link of the BBB visio for this pool." msgstr "Le lien du salon BBB pour cette poule." -#: participation/models.py:394 +#: participation/models.py:395 msgid "results available" msgstr "résultats disponibles" -#: participation/models.py:395 +#: participation/models.py:396 msgid "" "Check this case when results become accessible to teams. They stay " "accessible to you. Only averages are given." @@ -710,28 +710,28 @@ msgstr "" "Ils restent toujours accessibles pour vous. Seules les moyennes sont " "communiquées." -#: participation/models.py:415 +#: participation/models.py:417 #, 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:435 +#: participation/models.py:437 msgid "position" msgstr "position" -#: participation/models.py:442 +#: participation/models.py:444 msgid "defended solution" msgstr "solution défendue" -#: participation/models.py:475 +#: participation/models.py:477 msgid "observer" msgstr "observateur⋅rice" -#: participation/models.py:480 +#: participation/models.py:482 msgid "penalties" msgstr "pénalités" -#: participation/models.py:482 +#: participation/models.py:484 msgid "" "Number of penalties for the defender. The defender will loose a 0.5 " "coefficient per penalty." @@ -739,124 +739,124 @@ msgstr "" "Nombre de pénalités pour læ défenseur⋅se. Læ défenseur⋅se perd un " "coefficient 0.5 sur sa présentation orale par pénalité." -#: participation/models.py:558 participation/models.py:561 -#: participation/models.py:564 participation/models.py:567 +#: participation/models.py:560 participation/models.py:563 +#: participation/models.py:566 participation/models.py:569 #, 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:572 +#: participation/models.py:574 #, python-brace-format msgid "Passage of {defender} for problem {problem}" msgstr "Passage de {defender} pour le problème {problem}" -#: participation/models.py:576 participation/models.py:585 -#: participation/models.py:666 participation/models.py:708 +#: participation/models.py:578 participation/models.py:587 +#: participation/models.py:668 participation/models.py:710 msgid "passage" msgstr "passage" -#: participation/models.py:577 +#: participation/models.py:579 msgid "passages" msgstr "passages" -#: participation/models.py:596 +#: participation/models.py:598 msgid "difference" msgstr "différence" -#: participation/models.py:597 +#: participation/models.py:599 msgid "Score to add/remove on the final score" msgstr "Score à ajouter/retrancher au score final" -#: participation/models.py:604 +#: participation/models.py:606 msgid "tweak" msgstr "harmonisation" -#: participation/models.py:605 +#: participation/models.py:607 msgid "tweaks" msgstr "harmonisations" -#: participation/models.py:633 +#: participation/models.py:635 msgid "solution for the final tournament" msgstr "solution pour la finale" -#: participation/models.py:638 participation/models.py:677 +#: participation/models.py:640 participation/models.py:679 msgid "file" msgstr "fichier" -#: participation/models.py:644 +#: participation/models.py:646 #, 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:646 +#: participation/models.py:648 msgid "for final" msgstr "pour la finale" -#: participation/models.py:649 +#: participation/models.py:651 msgid "solution" msgstr "solution" -#: participation/models.py:650 +#: participation/models.py:652 msgid "solutions" msgstr "solutions" -#: participation/models.py:683 +#: participation/models.py:685 #, 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:691 +#: participation/models.py:693 msgid "synthesis" msgstr "note de synthèse" -#: participation/models.py:692 +#: participation/models.py:694 msgid "syntheses" msgstr "notes de synthèse" -#: participation/models.py:701 +#: participation/models.py:703 msgid "jury" msgstr "jury" -#: participation/models.py:713 +#: participation/models.py:715 msgid "defender writing note" msgstr "note d'écrit de læ défenseur⋅se" -#: participation/models.py:719 +#: participation/models.py:721 msgid "defender oral note" msgstr "note d'oral de læ défenseur⋅se" -#: participation/models.py:725 +#: participation/models.py:727 msgid "opponent writing note" msgstr "note d'écrit de l'opposant⋅e" -#: participation/models.py:731 +#: participation/models.py:733 msgid "opponent oral note" msgstr "note d'oral de l'opposant⋅e" -#: participation/models.py:737 +#: participation/models.py:739 msgid "reporter writing note" msgstr "note d'écrit de læ rapporteur⋅e" -#: participation/models.py:743 +#: participation/models.py:745 msgid "reporter oral note" msgstr "note d'oral de læ rapporteur⋅e" -#: participation/models.py:749 +#: participation/models.py:751 msgid "observer note" msgstr "note de l'observateur⋅rice" -#: participation/models.py:778 +#: participation/models.py:780 #, python-brace-format msgid "Notes of {jury} for {passage}" msgstr "Notes de {jury} pour le {passage}" -#: participation/models.py:785 +#: participation/models.py:787 msgid "note" msgstr "note" -#: participation/models.py:786 +#: participation/models.py:788 msgid "notes" msgstr "notes" @@ -872,21 +872,21 @@ msgstr "Validation en attente" msgid "Not validated" msgstr "Non validée" -#: participation/tables.py:62 +#: participation/tables.py:63 msgid "date" msgstr "date" -#: participation/tables.py:65 +#: participation/tables.py:66 #, python-brace-format msgid "From {start} to {end}" msgstr "Du {start} au {end}" -#: participation/tables.py:91 +#: participation/tables.py:93 #, python-brace-format msgid "Pool {letter}{round}" msgstr "Poule {letter}{round}" -#: participation/tables.py:95 +#: participation/tables.py:97 msgid "No defined team" msgstr "Pas d'équipe définie" @@ -940,9 +940,9 @@ msgstr "Rejoindre" #: participation/templates/participation/passage_detail.html:120 #: participation/templates/participation/passage_detail.html:126 #: participation/templates/participation/pool_add_jurys.html:35 -#: participation/templates/participation/pool_detail.html:88 -#: participation/templates/participation/pool_detail.html:106 -#: participation/templates/participation/pool_detail.html:111 +#: participation/templates/participation/pool_detail.html:110 +#: participation/templates/participation/pool_detail.html:128 +#: participation/templates/participation/pool_detail.html:133 #: participation/templates/participation/team_detail.html:128 #: participation/templates/participation/team_detail.html:192 #: participation/templates/participation/tournament_form.html:12 @@ -1006,7 +1006,7 @@ msgstr "Envoyer une solution" #: participation/templates/participation/participation_detail.html:59 #: participation/templates/participation/passage_detail.html:132 -#: participation/templates/participation/pool_detail.html:116 +#: participation/templates/participation/pool_detail.html:138 #: participation/templates/participation/team_detail.html:187 #: participation/templates/participation/upload_motivation_letter.html:13 #: participation/templates/participation/upload_notes.html:17 @@ -1057,10 +1057,12 @@ msgid "Defender penalties count:" msgstr "Nombre de pénalités :" #: participation/templates/participation/passage_detail.html:39 +#: participation/templates/participation/pool_detail.html:46 msgid "Syntheses:" msgstr "Notes de synthèse :" #: participation/templates/participation/passage_detail.html:44 +#: participation/templates/participation/pool_detail.html:55 msgid "No synthesis was uploaded yet." msgstr "Aucune note de synthèse n'a encore été envoyée." @@ -1141,7 +1143,7 @@ msgstr "" #: participation/templates/participation/pool_add_jurys.html:11 #: participation/templates/participation/pool_add_jurys.html:34 -#: participation/templates/participation/pool_detail.html:105 +#: participation/templates/participation/pool_detail.html:127 #: participation/templates/participation/pool_form.html:11 msgid "Update pool" msgstr "Modifier la poule" @@ -1182,42 +1184,47 @@ msgstr "Ajouter des juré⋅es" msgid "Defended solutions:" msgstr "Solutions défendues :" -#: participation/templates/participation/pool_detail.html:43 +#: participation/templates/participation/pool_detail.html:42 +#: participation/templates/participation/pool_detail.html:61 +msgid "Download all" +msgstr "Tout télécharger" + +#: participation/templates/participation/pool_detail.html:65 msgid "BigBlueButton link:" msgstr "Lien BigBlueButton :" -#: participation/templates/participation/pool_detail.html:49 +#: participation/templates/participation/pool_detail.html:71 #: participation/templates/participation/tournament_detail.html:97 msgid "Ranking" msgstr "Classement" -#: participation/templates/participation/pool_detail.html:62 +#: participation/templates/participation/pool_detail.html:84 msgid "Download the scale sheet" msgstr "Télécharger la feuille de barème" -#: participation/templates/participation/pool_detail.html:72 +#: participation/templates/participation/pool_detail.html:94 msgid "Download the final notation sheet" msgstr "Télécharger la fiche de notation finale" -#: participation/templates/participation/pool_detail.html:80 +#: participation/templates/participation/pool_detail.html:102 msgid "Upload notes from a CSV file" msgstr "Soumettre les notes à partir d'un fichier CSV" -#: participation/templates/participation/pool_detail.html:87 -#: participation/templates/participation/pool_detail.html:100 +#: participation/templates/participation/pool_detail.html:109 +#: participation/templates/participation/pool_detail.html:122 msgid "Add passage" msgstr "Ajouter un passage" -#: participation/templates/participation/pool_detail.html:89 -#: participation/templates/participation/pool_detail.html:110 +#: participation/templates/participation/pool_detail.html:111 +#: participation/templates/participation/pool_detail.html:132 msgid "Update teams" msgstr "Modifier les équipes" -#: participation/templates/participation/pool_detail.html:96 +#: participation/templates/participation/pool_detail.html:118 msgid "Passages" msgstr "Passages" -#: participation/templates/participation/pool_detail.html:115 +#: participation/templates/participation/pool_detail.html:137 msgid "Upload notes" msgstr "Envoyer les notes" @@ -1582,33 +1589,43 @@ msgstr "L'équipe n'est pas encore validée." msgid "Participation of team {trigram}" msgstr "Participation de l'équipe {trigram}" -#: participation/views.py:647 +#: participation/views.py:655 msgid "You can't upload a solution after the deadline." msgstr "Vous ne pouvez pas envoyer de solution après la date limite." -#: participation/views.py:738 +#: participation/views.py:763 +#, python-brace-format +msgid "Solutions for pool {pool} of tournament {tournament}.zip" +msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip" + +#: participation/views.py:764 +#, python-brace-format +msgid "Syntheses for pool {pool} of tournament {tournament}.zip" +msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip" + +#: participation/views.py:782 #, python-brace-format msgid "Jurys of {pool}" msgstr "Juré⋅es de la {pool}" -#: participation/views.py:765 +#: participation/views.py:809 msgid "New TFJM² jury account" msgstr "Nouveau compte de juré⋅e pour le TFJM²" -#: participation/views.py:778 +#: participation/views.py:822 #, python-brace-format msgid "The jury {name} has been successfully added!" msgstr "Læ juré⋅e {name} a été ajouté⋅e avec succès !" -#: participation/views.py:815 +#: participation/views.py:859 msgid "The following user is not registered as a jury:" msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :" -#: participation/views.py:829 +#: participation/views.py:873 msgid "Notes were successfully uploaded." msgstr "Les notes ont bien été envoyées." -#: participation/views.py:1493 +#: participation/views.py:1537 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." @@ -2317,11 +2334,11 @@ msgstr "Autorisation parentale de {student}.{ext}" msgid "Scholarship attestation of {user}.{ext}" msgstr "Notification de bourse de {user}.{ext}" -#: tfjm/settings.py:165 +#: tfjm/settings.py:170 msgid "English" msgstr "Anglais" -#: tfjm/settings.py:166 +#: tfjm/settings.py:171 msgid "French" msgstr "Français" diff --git a/participation/models.py b/participation/models.py index 7e435f6..494f36a 100644 --- a/participation/models.py +++ b/participation/models.py @@ -399,7 +399,7 @@ class Pool(models.Model): @property def solutions(self): - return Solution.objects.filter(participation__in=self.participations, final_solution=self.tournament.final) + return [passage.defended_solution for passage in self.passages.all()] def average(self, participation): return sum(passage.average(participation) for passage in self.passages.all()) \ diff --git a/participation/templates/participation/pool_detail.html b/participation/templates/participation/pool_detail.html index 93704e0..9c7307f 100644 --- a/participation/templates/participation/pool_detail.html +++ b/participation/templates/participation/pool_detail.html @@ -36,8 +36,30 @@
{% trans "Defended solutions:" %}
{% for passage in pool.passages.all %} - {{ passage.defended_solution }}{% if not forloop.last %}, {% endif %} + {{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }}{% if not forloop.last %}, {% endif %} {% endfor %} + + {% trans "Download all" %} + +
+ +
{% trans "Syntheses:" %}
+
+
    + {% for passage in pool.passages.all %} +
  • + {{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }} : + {% for synthesis in passage.syntheses.all %} + {{ synthesis.participation.team.trigram }} ({{ synthesis.get_type_display }}){% if not forloop.last %}, {% endif %} + {% empty %} + {% trans "No synthesis was uploaded yet." %} + {% endfor %} +
  • + {% endfor %} +
+ + {% trans "Download all" %} +
{% trans "BigBlueButton link:" %}
diff --git a/participation/urls.py b/participation/urls.py index 0e01cf6..3881148 100644 --- a/participation/urls.py +++ b/participation/urls.py @@ -6,9 +6,9 @@ from django.views.generic import TemplateView from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, MyParticipationDetailView, \ MyTeamDetailView, NoteUpdateView, ParticipationDetailView, PassageCreateView, PassageDetailView, \ - PassageUpdateView, PoolAddJurysView, PoolCreateView, PoolDetailView, PoolNotesTemplateView, PoolUpdateTeamsView, \ - PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, SolutionUploadView, SynthesisUploadView, \ - TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \ + PassageUpdateView, PoolAddJurysView, PoolCreateView, PoolDetailView, PoolDownloadView, PoolNotesTemplateView, \ + PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, ScaleNotationSheetTemplateView, SolutionUploadView, \ + SynthesisUploadView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \ TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \ TournamentListView, TournamentUpdateView @@ -37,6 +37,8 @@ urlpatterns = [ path("pools/create/", PoolCreateView.as_view(), name="pool_create"), path("pools//", PoolDetailView.as_view(), name="pool_detail"), path("pools//update/", PoolUpdateView.as_view(), name="pool_update"), + path("pools//solutions/", PoolDownloadView.as_view(), name="pool_download_solutions"), + path("pools//syntheses/", PoolDownloadView.as_view(), name="pool_download_syntheses"), path("pools//notation/scale/", ScaleNotationSheetTemplateView.as_view(), name="pool_scale_note_sheet"), path("pools//notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"), path("pools//update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"), diff --git a/participation/views.py b/participation/views.py index c1736c1..3af432f 100644 --- a/participation/views.py +++ b/participation/views.py @@ -733,6 +733,42 @@ class PoolUpdateTeamsView(VolunteerMixin, UpdateView): return self.handle_no_permission() +class PoolDownloadView(VolunteerMixin, DetailView): + """ + Download all solutions or syntheses as a ZIP archive. + """ + model = Pool + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return self.handle_no_permission() + if request.user.registration.is_admin or request.user.registration.is_volunteer \ + and (self.get_object().tournament in request.user.registration.organized_tournaments.all() + or request.user.registration in self.get_object().juries.all()): + return super().dispatch(request, *args, **kwargs) + return self.handle_no_permission() + + def get(self, request, *args, **kwargs): + pool = self.get_object() + + is_solution = 'solutions' in request.path + + output = BytesIO() + zf = ZipFile(output, "w") + for s in (pool.solutions if is_solution else Synthesis.objects.filter(passage__pool=pool).all()): + zf.write("media/" + s.file.name, f"{s}.pdf") + + zf.close() + response = HttpResponse(content_type="application/zip") + filename = _("Solutions for pool {pool} of tournament {tournament}.zip") \ + if is_solution else _("Syntheses for pool {pool} of tournament {tournament}.zip") + filename = filename.format(pool=pool.get_letter_display() + str(pool.round), tournament=pool.tournament.name) + response["Content-Disposition"] = "attachment; filename=\"{filename}\"" \ + .format(filename=filename) + response.write(output.getvalue()) + return response + + class PoolAddJurysView(VolunteerMixin, FormView, DetailView): """ This view lets organizers set jurys for a pool, without multiplying clicks.