Rename synthesis to written review

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-07-06 21:26:54 +02:00
parent 696863f6c3
commit 12205f953b
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
16 changed files with 422 additions and 304 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-06 10:16+0200\n"
"POT-Creation-Date: 2024-07-06 21:27+0200\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"
@ -79,7 +79,7 @@ msgstr "Type de permission nécessaire pour écrire un message dans un canal."
#: chat/models.py:62 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88
#: draw/models.py:27 participation/admin.py:79 participation/admin.py:144
#: participation/admin.py:176 participation/models.py:783
#: participation/models.py:807 participation/models.py:1116
#: participation/models.py:807 participation/models.py:1131
#: registration/models.py:763
#: registration/templates/registration/payment_form.html:53
msgid "tournament"
@ -95,7 +95,7 @@ msgstr ""
#: chat/models.py:73 draw/models.py:446 draw/models.py:473
#: participation/admin.py:140 participation/admin.py:160
#: participation/models.py:1651 participation/models.py:1660
#: participation/models.py:1666 participation/models.py:1675
#: participation/tables.py:84
msgid "pool"
msgstr "poule"
@ -265,7 +265,7 @@ msgid "teams"
msgstr "équipes"
#: draw/admin.py:92 draw/models.py:245 draw/models.py:465
#: participation/models.py:1120
#: participation/models.py:1135
msgid "round"
msgstr "tour"
@ -634,7 +634,7 @@ msgstr "Le numéro du tour doit être entre 1 et {nb}."
msgid "rounds"
msgstr "tours"
#: draw/models.py:268 participation/models.py:1128
#: draw/models.py:268 participation/models.py:1143
msgid "letter"
msgstr "lettre"
@ -672,12 +672,12 @@ msgstr "L'instance complète de la poule."
msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}"
#: draw/models.py:447 participation/models.py:1652
#: draw/models.py:447 participation/models.py:1667
msgid "pools"
msgstr "poules"
#: draw/models.py:459 participation/models.py:1106 participation/models.py:1871
#: participation/models.py:1901 participation/models.py:1943
#: draw/models.py:459 participation/models.py:1121 participation/models.py:1886
#: participation/models.py:1920 participation/models.py:1962
msgid "participation"
msgstr "participation"
@ -701,9 +701,9 @@ msgid ""
msgstr ""
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
#: draw/models.py:496 draw/models.py:519 participation/models.py:1237
#: participation/models.py:1674 participation/models.py:1908
#: participation/views.py:1489 participation/views.py:1754
#: draw/models.py:496 draw/models.py:519 participation/models.py:1252
#: participation/models.py:1689 participation/models.py:1927
#: participation/views.py:1492 participation/views.py:1757
#, python-brace-format
msgid "Problem #{problem}"
msgstr "Problème n°{problem}"
@ -919,26 +919,26 @@ msgid "selected for final"
msgstr "sélectionnée pour la finale"
#: participation/admin.py:124 participation/admin.py:188
#: participation/models.py:1681 participation/tables.py:114
#: participation/models.py:1696 participation/tables.py:114
msgid "defender"
msgstr "défenseur⋅se"
#: participation/admin.py:128 participation/models.py:1688
#: participation/models.py:1955
#: participation/admin.py:128 participation/models.py:1703
#: participation/models.py:1974
msgid "opponent"
msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:1695
#: participation/models.py:1956
#: participation/admin.py:132 participation/models.py:1710
#: participation/models.py:1975
msgid "reviewer"
msgstr "rapporteur⋅rice"
#: participation/admin.py:136 participation/models.py:1702
#: participation/models.py:1957
#: participation/admin.py:136 participation/models.py:1717
#: participation/models.py:1976
msgid "observer"
msgstr "observateur⋅rice"
#: participation/admin.py:192 participation/models.py:1906
#: participation/admin.py:192 participation/models.py:1925
msgid "problem"
msgstr "numéro de problème"
@ -1239,7 +1239,7 @@ msgid "first phase date"
msgstr "date du premier tour"
#: participation/models.py:327
msgid "limit date to upload the syntheses for the first phase"
msgid "limit date to upload the written reviews for the first phase"
msgstr "date limite pour envoyer les notes de synthèses pour la première phase"
#: participation/models.py:332
@ -1252,7 +1252,7 @@ msgstr ""
"cocher la case lorsque les solutions pour le second tour sont accessibles"
#: participation/models.py:342
msgid "limit date to upload the syntheses for the second phase"
msgid "limit date to upload the written reviews for the second phase"
msgstr "date limite d'envoi des notes de synthèse pour la seconde phase"
#: participation/models.py:347
@ -1265,7 +1265,7 @@ msgstr ""
"cocher la case lorsque les solutions pour le second tour sont accessibles"
#: participation/models.py:357
msgid "limit date to upload the syntheses for the third phase"
msgid "limit date to upload the written reviews for the third phase"
msgstr ""
"date limite pour envoyer les notes de synthèses pour la troisième phase"
@ -1294,7 +1294,7 @@ msgid "Final ranking"
msgstr "Classement final"
#: participation/models.py:481 participation/models.py:553
#: participation/models.py:1312 participation/views.py:1728
#: participation/models.py:1327 participation/views.py:1731
msgid "Team"
msgstr "Équipe"
@ -1326,15 +1326,15 @@ msgstr "Scores jour 3"
msgid "Tweaks day 3"
msgstr "Ajustements 3"
#: participation/models.py:485 participation/models.py:1312
#: participation/views.py:1735
#: participation/models.py:485 participation/models.py:1327
#: participation/views.py:1738
msgid "Total"
msgstr "Total"
#: participation/models.py:485 participation/models.py:553
#: participation/models.py:1312
#: participation/models.py:1327
#: participation/templates/participation/tournament_harmonize.html:14
#: participation/views.py:1738
#: participation/views.py:1741
msgid "Rank"
msgstr "Rang"
@ -1346,7 +1346,7 @@ msgstr "Score"
msgid "Mention"
msgstr "Mention"
#: participation/models.py:698 participation/models.py:1581
#: participation/models.py:698 participation/models.py:1596
msgid "Don't update the table structure for a better automated integration."
msgstr ""
"Ne pas mettre à jour la structure de la table pour une meilleure intégration "
@ -1456,47 +1456,47 @@ msgstr ""
"tour, vous défendrez <a href='{solution_url}'>votre solution du problème "
"{problem}</a>.</p>"
#: participation/models.py:930 participation/models.py:988
#: participation/models.py:1047
#: participation/models.py:930 participation/models.py:993
#: participation/models.py:1057
#, python-brace-format
msgid ""
"<p>You will oppose the solution of the team {opponent} on the <a "
"href='{solution_url}'>problem {problem}</a>. You can upload your synthesis "
"sheet on <a href='{passage_url}'>this page</a>.</p>"
"href='{solution_url}'>problem {problem}</a>. You can upload your written "
"review on <a href='{passage_url}'>this page</a>.</p>"
msgstr ""
"<p>Vous opposerez la solution de l'équipe {opponent} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:939 participation/models.py:997
#: participation/models.py:1056
#: participation/models.py:939 participation/models.py:1002
#: participation/models.py:1066
#, python-brace-format
msgid ""
"<p>You will report the solution of the team {reviewer} on the <a "
"href='{solution_url}'>problem {problem}. You can upload your synthesis sheet "
"on <a href='{passage_url}'>this page</a>.</p>"
"href='{solution_url}'>problem {problem}</a>. You can upload your written "
"review on <a href='{passage_url}'>this page</a>.</p>"
msgstr ""
"<p>Vous rapporterez la solution de l'équipe {reviewer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:949 participation/models.py:1007
#: participation/models.py:1066
#: participation/models.py:949 participation/models.py:1012
#: participation/models.py:1076
#, python-brace-format
msgid ""
"<p>You will observe the solution of the team {observer} on the <a "
"href='{solution_url}'>problem {problem}. You can upload your synthesis sheet "
"on <a href='{passage_url}'>this page</a>.</p>"
"href='{solution_url}'>problem {problem}</a>. You can upload your written "
"review on <a href='{passage_url}'>this page</a>.</p>"
msgstr ""
"<p>Vous observerez la solution de l'équipe {observer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:969 registration/models.py:629
#: participation/models.py:974 registration/models.py:629
msgid "First round"
msgstr "Premier tour"
#: participation/models.py:981
#: participation/models.py:986
#, python-brace-format
msgid ""
"<p>For the second round, you will defend <a href='{solution_url}'>your "
@ -1505,12 +1505,12 @@ msgstr ""
"<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>"
#: participation/models.py:1027 participation/models.py:1086
#: participation/models.py:1037 participation/models.py:1101
#: registration/models.py:640
msgid "Second round"
msgstr "Second tour"
#: participation/models.py:1040
#: participation/models.py:1050
#, python-brace-format
msgid ""
"<p>For the third round, you will defend <a href='{solution_url}'>your "
@ -1519,7 +1519,7 @@ msgstr ""
"<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>"
#: participation/models.py:1092
#: participation/models.py:1107
#, python-brace-format
msgid ""
"<p>The tournament {tournament} is ended. You can check the results on the <a "
@ -1528,57 +1528,57 @@ msgstr ""
"<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats "
"sur la <a href='{url}'>page du tournoi</a>.</p>"
#: participation/models.py:1097
#: participation/models.py:1112
msgid "Tournament ended"
msgstr "Tournoi terminé"
#: participation/models.py:1107 participation/models.py:1150
#: participation/models.py:1122 participation/models.py:1165
msgid "participations"
msgstr "participations"
#: participation/models.py:1122 participation/models.py:1123
#: participation/models.py:1124
#: participation/models.py:1137 participation/models.py:1138
#: participation/models.py:1139
#, python-brace-format
msgid "Round {round}"
msgstr "Tour {round}"
#: participation/models.py:1138
#: participation/models.py:1153
msgid "room"
msgstr "salle"
#: participation/models.py:1140
#: participation/models.py:1155
msgid "Room 1"
msgstr "Salle 1"
#: participation/models.py:1141
#: participation/models.py:1156
msgid "Room 2"
msgstr "Salle 2"
#: participation/models.py:1144
#: participation/models.py:1159
msgid "For 5-teams pools only"
msgstr "Pour les poules de 5 équipe uniquement"
#: participation/models.py:1156
#: participation/models.py:1171
msgid "juries"
msgstr "jurys"
#: participation/models.py:1165
#: participation/models.py:1180
msgid "president of the jury"
msgstr "président⋅e du jury"
#: participation/models.py:1172
#: participation/models.py:1187
msgid "BigBlueButton URL"
msgstr "Lien BigBlueButton"
#: participation/models.py:1173
#: participation/models.py:1188
msgid "The link of the BBB visio for this pool."
msgstr "Le lien du salon BBB pour cette poule."
#: participation/models.py:1178
#: participation/models.py:1193
msgid "results available"
msgstr "résultats disponibles"
#: participation/models.py:1179
#: participation/models.py:1194
msgid ""
"Check this case when results become accessible to teams. They stay "
"accessible to you. Only averages are given."
@ -1587,65 +1587,65 @@ msgstr ""
"Ils restent toujours accessibles pour vous. Seules les moyennes sont "
"communiquées."
#: participation/models.py:1211
#: participation/models.py:1226
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:1238 participation/models.py:1312
#: participation/views.py:1483 participation/views.py:1732
#: participation/models.py:1253 participation/models.py:1327
#: participation/views.py:1486 participation/views.py:1735
msgid "Problem"
msgstr "Problème"
#: participation/models.py:1243 participation/views.py:1498
#: participation/models.py:1258 participation/views.py:1501
msgid "Role"
msgstr "Rôle"
#: participation/models.py:1248 participation/views.py:1532
#: participation/views.py:1533
#: participation/models.py:1263 participation/views.py:1535
#: participation/views.py:1536
msgid "Juree"
msgstr "Juré⋅e"
#: participation/models.py:1271 participation/models.py:1597
#: participation/models.py:1619 participation/views.py:1602
#: participation/models.py:1286 participation/models.py:1612
#: participation/models.py:1634 participation/views.py:1605
msgid "Average"
msgstr "Moyenne"
#: participation/models.py:1277 participation/views.py:1621
#: participation/models.py:1292 participation/views.py:1624
msgid "Coefficient"
msgstr "Coefficien"
#: participation/models.py:1278 participation/views.py:1664
#: participation/models.py:1293 participation/views.py:1667
msgid "Subtotal"
msgstr "Sous-total"
#: participation/models.py:1544
#: participation/models.py:1559
#, python-brace-format
msgid "Input must be a valid integer between {min_note} and {max_note}."
msgstr "L'entrée doit être un entier valide entre {min_note} et {max_note}."
#: participation/models.py:1632
#: participation/models.py:1647
#, python-brace-format
msgid "The jury {jury} is not part of the jury for this pool."
msgstr "{jury} ne fait pas partie du jury pour cette poule."
#: participation/models.py:1645
#: participation/models.py:1660
#, python-brace-format
msgid "Pool {code} for tournament {tournament} with teams {teams}"
msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}"
#: participation/models.py:1665
#: participation/models.py:1680
msgid "position"
msgstr "position"
#: participation/models.py:1672
#: participation/models.py:1687
msgid "defended solution"
msgstr "solution défendue"
#: participation/models.py:1710
#: participation/models.py:1725
msgid "penalties"
msgstr "pénalités"
#: participation/models.py:1712
#: participation/models.py:1727
msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty."
@ -1653,128 +1653,128 @@ 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:1838 participation/models.py:1841
#: participation/models.py:1844 participation/models.py:1847
#: participation/models.py:1853 participation/models.py:1856
#: participation/models.py:1859 participation/models.py:1862
#, 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:1852
#: participation/models.py:1867
#, python-brace-format
msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}"
#: participation/models.py:1856 participation/models.py:1865
#: participation/models.py:1950 participation/models.py:1993
#: participation/models.py:1871 participation/models.py:1880
#: participation/models.py:1969 participation/models.py:2012
msgid "passage"
msgstr "passage"
#: participation/models.py:1857
#: participation/models.py:1872
msgid "passages"
msgstr "passages"
#: participation/models.py:1876
#: participation/models.py:1891
msgid "difference"
msgstr "différence"
#: participation/models.py:1877
#: participation/models.py:1892
msgid "Score to add/remove on the final score"
msgstr "Score à ajouter/retrancher au score final"
#: participation/models.py:1884
#: participation/models.py:1899
msgid "tweak"
msgstr "harmonisation"
#: participation/models.py:1885
#: participation/models.py:1900
msgid "tweaks"
msgstr "harmonisations"
#: participation/models.py:1913
#: participation/models.py:1932
msgid "solution for the final tournament"
msgstr "solution pour la finale"
#: participation/models.py:1918 participation/models.py:1962
#: participation/models.py:1937 participation/models.py:1981
msgid "file"
msgstr "fichier"
#: participation/models.py:1928
#: participation/models.py:1947
#, 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:1930
#: participation/models.py:1949
msgid "for final"
msgstr "pour la finale"
#: participation/models.py:1933
#: participation/models.py:1952
msgid "solution"
msgstr "solution"
#: participation/models.py:1934
#: participation/models.py:1953
msgid "solutions"
msgstr "solutions"
#: participation/models.py:1968
#: participation/models.py:1987
#, python-brace-format
msgid "Synthesis of {team} as {type} for problem {problem} of {defender}"
msgid "Written review 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:1976
msgid "synthesis"
#: participation/models.py:1995
msgid "written review"
msgstr "note de synthèse"
#: participation/models.py:1977
msgid "syntheses"
#: participation/models.py:1996
msgid "written reviews"
msgstr "notes de synthèse"
#: participation/models.py:1986
#: participation/models.py:2005
msgid "jury"
msgstr "jury"
#: participation/models.py:1998
#: participation/models.py:2017
msgid "defender writing note"
msgstr "note d'écrit défenseur⋅se"
#: participation/models.py:2004
#: participation/models.py:2023
msgid "defender oral note"
msgstr "note d'oral défenseur⋅se"
#: participation/models.py:2010
#: participation/models.py:2029
msgid "opponent writing note"
msgstr "note d'écrit opposant⋅e"
#: participation/models.py:2016
#: participation/models.py:2035
msgid "opponent oral note"
msgstr "note d'oral opposant⋅e"
#: participation/models.py:2022
#: participation/models.py:2041
msgid "reviewer writing note"
msgstr "note d'écrit rapporteur⋅rice"
#: participation/models.py:2028
#: participation/models.py:2047
msgid "reviewer oral note"
msgstr "note d'oral du rapporteur⋅rice"
#: participation/models.py:2034
#: participation/models.py:2053
msgid "observer writing note"
msgstr "note d'écrit de l'observateur⋅rice"
#: participation/models.py:2040
#: participation/models.py:2059
msgid "observer oral note"
msgstr "note d'oral de l'observateur⋅rice"
#: participation/models.py:2105
#: participation/models.py:2124
#, python-brace-format
msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}"
#: participation/models.py:2108
#: participation/models.py:2127
msgid "note"
msgstr "note"
#: participation/models.py:2109
#: participation/models.py:2128
msgid "notes"
msgstr "notes"
@ -1910,7 +1910,7 @@ msgstr "Envoyer une solution"
#: participation/templates/participation/upload_motivation_letter.html:13
#: participation/templates/participation/upload_notes.html:24
#: participation/templates/participation/upload_solution.html:11
#: participation/templates/participation/upload_synthesis.html:18
#: participation/templates/participation/upload_written_review.html:23
#: registration/templates/registration/upload_health_sheet.html:17
#: registration/templates/registration/upload_parental_authorization.html:17
#: registration/templates/registration/upload_photo_authorization.html:18
@ -1963,7 +1963,7 @@ msgstr "Notes de synthèse :"
#: participation/templates/participation/passage_detail.html:53
#: participation/templates/participation/pool_detail.html:68
msgid "No synthesis was uploaded yet."
msgid "No review was uploaded yet."
msgstr "Aucune note de synthèse n'a encore été envoyée."
#: participation/templates/participation/passage_detail.html:61
@ -1973,8 +1973,8 @@ msgstr "Modifier les notes"
#: participation/templates/participation/passage_detail.html:66
#: participation/templates/participation/passage_detail.html:187
msgid "Upload synthesis"
msgstr "Envoyer une note de synthèse"
msgid "Upload review"
msgstr "Envoyer la note de synthèse"
#: participation/templates/participation/passage_detail.html:74
msgid "Notes detail"
@ -2374,15 +2374,15 @@ msgid "date of the random draw"
msgstr "date du tirage au sort"
#: participation/templates/participation/tournament_detail.html:41
msgid "date of maximal syntheses submission for the first round"
msgid "date of maximal written reviews submission for the first round"
msgstr "date limite de soumission des notes de synthèse pour le premier tour"
#: participation/templates/participation/tournament_detail.html:44
msgid "date of maximal syntheses submission for the second round"
msgid "date of maximal written reviews submission for the second round"
msgstr "date limite de soumission des notes de synthèse pour le second tour"
#: participation/templates/participation/tournament_detail.html:48
msgid "date of maximal syntheses submission for the third round"
msgid "date of maximal written reviews submission for the third round"
msgstr "date limite de soumission des notes de synthèse pour le troisième tour"
#: participation/templates/participation/tournament_detail.html:56
@ -2462,6 +2462,42 @@ msgstr "Dépublier les notes pour le troisième tour"
msgid "Files available for download"
msgstr "Fichiers disponibles au téléchargement"
#: participation/templates/participation/tournament_detail.html:236
msgid "Validated team participant data spreadsheet"
msgstr "Tableur des données des équipes validées"
#: participation/templates/participation/tournament_detail.html:241
msgid "All teams participant data spreadsheet"
msgstr "Tableur des données de toutes les équipes"
#: participation/templates/participation/tournament_detail.html:246
msgid "Archive of all authorisations sorted by team and person"
msgstr "Archive de toutes les autorisations triées par équipe et personne"
#: participation/templates/participation/tournament_detail.html:251
msgid "Archive of all submitted solutions sorted by team"
msgstr "Archive de toutes les solutions envoyées triées par équipe"
#: participation/templates/participation/tournament_detail.html:256
msgid "Archive of all sent solutions sorted by problem"
msgstr "Archive de toutes les solutions envoyées triées par problème"
#: participation/templates/participation/tournament_detail.html:261
msgid "Archive of all sent solutions sorted by pool"
msgstr "Archive de toutes les solutions envoyées triées par poule"
#: participation/templates/participation/tournament_detail.html:266
msgid "Archive of all summary notes sorted by pool and passage"
msgstr "Archive de toutes les notes de synthèse triées par poule et passage"
#: participation/templates/participation/tournament_detail.html:272
msgid "Note spreadsheet on Google Sheets"
msgstr "Tableur de notes sur Google Sheets"
#: participation/templates/participation/tournament_detail.html:277
msgid "Archive of all printable note sheets sorted by pool"
msgstr "Archive de toutes les fiches de notes imprimables triées par poule"
#: participation/templates/participation/tournament_harmonize.html:16
#: registration/models.py:655
msgid "Note"
@ -2504,11 +2540,11 @@ msgstr ""
msgid "Download empty notation sheet"
msgstr "Télécharger la fiche de notation vierge"
#: participation/templates/participation/upload_synthesis.html:9
#: participation/templates/participation/upload_written_review.html:9
msgid "Templates:"
msgstr "Modèles :"
#: participation/templates/participation/upload_synthesis.html:13
#: participation/templates/participation/upload_written_review.html:14
msgid "Warning: non-free format"
msgstr "Attention : format non libre"
@ -2656,96 +2692,96 @@ msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
msgid "Solutions of team {trigram}.zip"
msgstr "Solutions de l'équipe {trigram}.zip"
#: participation/views.py:1022
#: participation/views.py:1023
#, python-brace-format
msgid "Syntheses of team {trigram}.zip"
msgid "Written reviews of team {trigram}.zip"
msgstr "Notes de synthèse de l'équipe {trigram}.zip"
#: participation/views.py:1039 participation/views.py:1054
#: participation/views.py:1040 participation/views.py:1056
#, python-brace-format
msgid "Solutions of {tournament}.zip"
msgstr "Solutions de {tournament}.zip"
#: participation/views.py:1039 participation/views.py:1054
#: participation/views.py:1041 participation/views.py:1057
#, python-brace-format
msgid "Syntheses of {tournament}.zip"
msgid "Written reviews of {tournament}.zip"
msgstr "Notes de synthèse de {tournament}.zip"
#: participation/views.py:1063
#: participation/views.py:1066
#, 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:1064
#: participation/views.py:1067
#, python-brace-format
msgid "Syntheses for pool {pool} of tournament {tournament}.zip"
msgid "Written reviews for pool {pool} of tournament {tournament}.zip"
msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:1106
#: participation/views.py:1109
#, python-brace-format
msgid "Jury of pool {pool} for {tournament} with teams {teams}"
msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
#: participation/views.py:1122
#: participation/views.py:1125
#, python-brace-format
msgid "The jury {name} is already in the pool!"
msgstr "{name} est déjà dans la poule !"
#: participation/views.py:1142
#: participation/views.py:1145
msgid "New jury account"
msgstr "Nouveau compte de juré⋅e"
#: participation/views.py:1163
#: participation/views.py:1166
#, python-brace-format
msgid "The jury {name} has been successfully added!"
msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !"
#: participation/views.py:1199
#: participation/views.py:1202
#, python-brace-format
msgid "The jury {name} has been successfully removed!"
msgstr "{name} a été retiré⋅e avec succès du jury !"
#: participation/views.py:1225
#: participation/views.py:1228
#, python-brace-format
msgid "The jury {name} has been successfully promoted president!"
msgstr "{name} a été nommé⋅e président⋅e du jury !"
#: participation/views.py:1253
#: participation/views.py:1256
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:1269
#: participation/views.py:1272
msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1504
#: participation/views.py:1507
msgid "Defender"
msgstr "Défenseur⋅se"
#: participation/views.py:1510
#: participation/views.py:1513
msgid "Opponent"
msgstr "Opposant⋅e"
#: participation/views.py:1517
#: participation/views.py:1520
msgid "Reviewer"
msgstr "Rapporteur⋅rice"
#: participation/views.py:1524
#: participation/views.py:1527
msgid "Observer"
msgstr "Observateur⋅rice"
#: participation/views.py:1895
#: participation/views.py:1898
#, 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:1900
#: participation/views.py:1903
#, python-brace-format
msgid "Notation sheets of {tournament}.zip"
msgstr "Feuilles de notation de {tournament}.zip"
#: participation/views.py:2067
msgid "You can't upload a synthesis after the deadline."
#: participation/views.py:2070
msgid "You can't upload a written review after the deadline."
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
#: registration/admin.py:53 registration/admin.py:69 registration/admin.py:85

View File

@ -4,7 +4,7 @@
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview
class ParticipationInline(admin.StackedInline):
@ -32,8 +32,8 @@ class SolutionInline(admin.TabularInline):
show_change_link = True
class SynthesisInline(admin.TabularInline):
model = Synthesis
class WrittenReviewInline(admin.TabularInline):
model = WrittenReview
extra = 0
ordering = ('passage__solution_number', 'type',)
autocomplete_fields = ('passage',)
@ -95,7 +95,7 @@ class ParticipationAdmin(admin.ModelAdmin):
search_fields = ('team__name', 'team__trigram',)
list_filter = ('valid', 'tournament',)
autocomplete_fields = ('team', 'tournament',)
inlines = (SolutionInline, SynthesisInline,)
inlines = (SolutionInline, WrittenReviewInline,)
@admin.register(Pool)
@ -178,19 +178,19 @@ class SolutionAdmin(admin.ModelAdmin):
return Tournament.final_tournament() if record.final_solution else record.participation.tournament
@admin.register(Synthesis)
class SynthesisAdmin(admin.ModelAdmin):
@admin.register(WrittenReview)
class WrittenReviewAdmin(admin.ModelAdmin):
list_display = ('participation', 'type', 'defender', 'passage',)
list_filter = ('participation__tournament', 'type', 'passage__solution_number',)
search_fields = ('participation__team__name', 'participation__team__trigram',)
autocomplete_fields = ('participation', 'passage',)
@admin.display(description=_("defender"))
def defender(self, record: Synthesis):
def defender(self, record: WrittenReview):
return record.passage.defender
@admin.display(description=_("problem"))
def problem(self, record: Synthesis):
def problem(self, record: WrittenReview):
return record.passage.solution_number

View File

@ -3,7 +3,7 @@
from rest_framework import serializers
from ..models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
from ..models import Note, Participation, Passage, Pool, Solution, Team, Tournament, WrittenReview
class NoteSerializer(serializers.ModelSerializer):
@ -38,9 +38,9 @@ class SolutionSerializer(serializers.ModelSerializer):
fields = '__all__'
class SynthesisSerializer(serializers.ModelSerializer):
class WrittenReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Synthesis
model = WrittenReview
fields = '__all__'
@ -58,9 +58,9 @@ class TournamentSerializer(serializers.ModelSerializer):
class Meta:
model = Tournament
fields = ('id', 'pk', 'name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote',
'inscription_limit', 'solution_limit', 'solutions_draw', 'syntheses_first_phase_limit',
'solutions_available_second_phase', 'syntheses_second_phase_limit',
'solutions_available_third_phase', 'syntheses_third_phase_limit',
'inscription_limit', 'solution_limit', 'solutions_draw', 'reviews_first_phase_limit',
'solutions_available_second_phase', 'reviews_second_phase_limit',
'solutions_available_third_phase', 'reviews_third_phase_limit',
'description', 'organizers', 'final', 'participations',)

View File

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from .views import NoteViewSet, ParticipationViewSet, PassageViewSet, PoolViewSet, \
SolutionViewSet, SynthesisViewSet, TeamViewSet, TournamentViewSet, TweakViewSet
SolutionViewSet, TeamViewSet, TournamentViewSet, TweakViewSet, WrittenReviewViewSet
def register_participation_urls(router, path):
@ -13,8 +13,8 @@ def register_participation_urls(router, path):
router.register(path + "/participation", ParticipationViewSet)
router.register(path + "/passage", PassageViewSet)
router.register(path + "/pool", PoolViewSet)
router.register(path + "/review", WrittenReviewViewSet)
router.register(path + "/solution", SolutionViewSet)
router.register(path + "/synthesis", SynthesisViewSet)
router.register(path + "/team", TeamViewSet)
router.register(path + "/tournament", TournamentViewSet)
router.register(path + "/tweak", TweakViewSet)

View File

@ -4,8 +4,8 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ModelViewSet
from .serializers import NoteSerializer, ParticipationSerializer, PassageSerializer, PoolSerializer, \
SolutionSerializer, SynthesisSerializer, TeamSerializer, TournamentSerializer, TweakSerializer
from ..models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak
SolutionSerializer, TeamSerializer, TournamentSerializer, TweakSerializer, WrittenReviewSerializer
from ..models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview
class NoteViewSet(ModelViewSet):
@ -44,9 +44,9 @@ class SolutionViewSet(ModelViewSet):
filterset_fields = ['participation', 'number', 'problem', 'final_solution', ]
class SynthesisViewSet(ModelViewSet):
queryset = Synthesis.objects.all()
serializer_class = SynthesisSerializer
class WrittenReviewViewSet(ModelViewSet):
queryset = WrittenReview.objects.all()
serializer_class = WrittenReviewSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['participation', 'number', 'passage', 'type', ]
@ -64,9 +64,9 @@ class TournamentViewSet(ModelViewSet):
serializer_class = TournamentSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote',
'inscription_limit', 'solution_limit', 'solutions_draw', 'syntheses_first_phase_limit',
'solutions_available_second_phase', 'syntheses_second_phase_limit',
'solutions_available_third_phase', 'syntheses_third_phase_limit',
'inscription_limit', 'solution_limit', 'solutions_draw', 'reviews_first_phase_limit',
'solutions_available_second_phase', 'reviews_second_phase_limit',
'solutions_available_third_phase', 'reviews_third_phase_limit',
'description', 'organizers', 'final', ]

View File

@ -16,7 +16,7 @@ from pypdf import PdfReader
from registration.models import VolunteerRegistration
from tfjm import settings
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, WrittenReview
class TeamForm(forms.ModelForm):
@ -137,7 +137,7 @@ class TournamentForm(forms.ModelForm):
if settings.NB_ROUNDS < 3:
del self.fields['date_third_phase']
del self.fields['solutions_available_third_phase']
del self.fields['syntheses_third_phase_limit']
del self.fields['reviews_third_phase_limit']
if not settings.PAYMENT_MANAGEMENT:
del self.fields['price']
@ -151,13 +151,13 @@ class TournamentForm(forms.ModelForm):
'solution_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%d %H:%M'),
'solutions_draw': forms.DateTimeInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%d %H:%M'),
'date_first_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'syntheses_first_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
'reviews_first_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
format='%Y-%m-%d %H:%M'),
'date_second_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'syntheses_second_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
'reviews_second_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
format='%Y-%m-%d %H:%M'),
'date_third_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'syntheses_third_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
'reviews_third_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
format='%Y-%m-%d %H:%M'),
'organizers': forms.SelectMultiple(attrs={
'class': 'selectpicker',
@ -359,7 +359,7 @@ class PassageForm(forms.ModelForm):
fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',)
class SynthesisForm(forms.ModelForm):
class WrittenReviewForm(forms.ModelForm):
def clean_file(self):
if "file" in self.files:
file = self.files["file"]
@ -375,11 +375,11 @@ class SynthesisForm(forms.ModelForm):
def save(self, commit=True):
"""
Don't save a synthesis with this way. Use a view instead
Don't save a written review with this way. Use a view instead
"""
class Meta:
model = Synthesis
model = WrittenReview
fields = ('file',)

View File

@ -0,0 +1,75 @@
# Generated by Django 5.0.6 on 2024-07-06 19:19
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("participation", "0019_note_observer_oral_note_observer_writing_and_more"),
]
operations = [
migrations.RenameModel(
old_name="Synthesis",
new_name="WrittenReview",
),
migrations.AlterModelOptions(
name="writtenreview",
options={
"ordering": ("passage__pool__round", "type"),
"verbose_name": "written review",
"verbose_name_plural": "written reviews",
},
),
migrations.RenameField(
model_name="tournament",
old_name="syntheses_first_phase_limit",
new_name="reviews_first_phase_limit",
),
migrations.RenameField(
model_name="tournament",
old_name="syntheses_second_phase_limit",
new_name="reviews_second_phase_limit",
),
migrations.RenameField(
model_name="tournament",
old_name="syntheses_third_phase_limit",
new_name="reviews_third_phase_limit",
),
migrations.AlterField(
model_name="tournament",
name="reviews_first_phase_limit",
field=models.DateTimeField(
default=django.utils.timezone.now,
verbose_name="limit date to upload the written reviews for the first phase",
),
),
migrations.AlterField(
model_name="tournament",
name="reviews_second_phase_limit",
field=models.DateTimeField(
default=django.utils.timezone.now,
verbose_name="limit date to upload the written reviews for the second phase",
),
),
migrations.AlterField(
model_name="tournament",
name="reviews_third_phase_limit",
field=models.DateTimeField(
default=django.utils.timezone.now,
verbose_name="limit date to upload the written reviews for the third phase",
),
),
migrations.AlterField(
model_name="writtenreview",
name="passage",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="written_reviews",
to="participation.passage",
verbose_name="passage",
),
),
]

View File

@ -323,8 +323,8 @@ class Tournament(models.Model):
default=date.today,
)
syntheses_first_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the syntheses for the first phase"),
reviews_first_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the written reviews for the first phase"),
default=timezone.now,
)
@ -338,8 +338,8 @@ class Tournament(models.Model):
default=False,
)
syntheses_second_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the syntheses for the second phase"),
reviews_second_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the written reviews for the second phase"),
default=timezone.now,
)
@ -353,8 +353,8 @@ class Tournament(models.Model):
default=False,
)
syntheses_third_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the syntheses for the third phase"),
reviews_third_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the written reviews for the third phase"),
default=timezone.now,
)
@ -442,10 +442,10 @@ class Tournament(models.Model):
return Solution.objects.filter(participation__tournament=self)
@property
def syntheses(self):
def written_reviews(self):
if self.final:
return Synthesis.objects.filter(final_solution=True)
return Synthesis.objects.filter(participation__tournament=self)
return WrittenReview.objects.filter(final_solution=True)
return WrittenReview.objects.filter(participation__tournament=self)
@property
def best_format(self):
@ -911,7 +911,7 @@ class Participation(models.Model):
'priority': 1,
'content': content,
})
elif timezone.now() <= tournament.syntheses_first_phase_limit + timedelta(hours=2):
elif timezone.now() <= tournament.reviews_first_phase_limit + timedelta(hours=2):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self)
@ -929,7 +929,7 @@ class Participation(models.Model):
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram,
@ -938,7 +938,7 @@ class Participation(models.Model):
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
@ -948,7 +948,7 @@ class Participation(models.Model):
if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text,
@ -959,24 +959,24 @@ class Participation(models.Model):
observer_content = ""
if settings.TFJM_APP == "TFJM":
syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"])
else:
syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"])
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>"
content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content
+ reviews_templates_content
informations.append({
'title': _("First round"),
'type': "info",
'priority': 1,
'content': content,
})
elif timezone.now() <= tournament.syntheses_second_phase_limit + timedelta(hours=2):
elif timezone.now() <= tournament.reviews_second_phase_limit + timedelta(hours=2):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self)
@ -992,7 +992,7 @@ class Participation(models.Model):
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram,
@ -1001,7 +1001,7 @@ class Participation(models.Model):
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
@ -1011,7 +1011,7 @@ class Participation(models.Model):
if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text,
@ -1022,17 +1022,17 @@ class Participation(models.Model):
observer_content = ""
if settings.TFJM_APP == "TFJM":
syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"])
else:
syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"])
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>"
content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content
+ reviews_templates_content
informations.append({
'title': _("Second round"),
'type': "info",
@ -1040,7 +1040,7 @@ class Participation(models.Model):
'content': content,
})
elif settings.TFJM_APP == "ETEAM" \
and timezone.now() <= tournament.syntheses_third_phase_limit + timedelta(hours=2):
and timezone.now() <= tournament.reviews_third_phase_limit + timedelta(hours=2):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self)
@ -1056,7 +1056,7 @@ class Participation(models.Model):
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram,
@ -1065,7 +1065,7 @@ class Participation(models.Model):
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
@ -1075,7 +1075,7 @@ class Participation(models.Model):
if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text,
@ -1086,17 +1086,17 @@ class Participation(models.Model):
observer_content = ""
if settings.TFJM_APP == "TFJM":
syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"])
else:
syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"])
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>"
content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content
+ reviews_templates_content
informations.append({
'title': _("Second round"),
'type': "info",
@ -1256,7 +1256,7 @@ class Pool(models.Model):
f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""]
+ ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else [])
for passage in passages), start=[str(_("Role")), ""]),
sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == "TFJM" else 10})",
sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})",
f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})",
f"{_('Writing')} (/10)", f"{_('Oral')} (/10)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"]
+ ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else [])
@ -1905,8 +1905,12 @@ def get_solution_filename(instance, filename):
+ ("_final" if instance.final_solution else "")
def get_review_filename(instance, filename):
return f"reviews/{instance.participation.team.trigram}_{instance.type}_{instance.passage.pk}"
def get_synthesis_filename(instance, filename):
return f"syntheses/{instance.participation.team.trigram}_{instance.type}_{instance.passage.pk}"
return get_review_filename(instance, filename)
class Solution(models.Model):
@ -1951,7 +1955,7 @@ class Solution(models.Model):
ordering = ('participation__team__trigram', 'final_solution', 'problem',)
class Synthesis(models.Model):
class WrittenReview(models.Model):
participation = models.ForeignKey(
Participation,
on_delete=models.CASCADE,
@ -1961,7 +1965,7 @@ class Synthesis(models.Model):
passage = models.ForeignKey(
Passage,
on_delete=models.CASCADE,
related_name="syntheses",
related_name="written_reviews",
verbose_name=_("passage"),
)
@ -1980,7 +1984,7 @@ class Synthesis(models.Model):
)
def __str__(self):
return _("Synthesis of {team} as {type} for problem {problem} of {defender}").format(
return _("Written review of {team} as {type} for problem {problem} of {defender}").format(
team=self.participation.team.trigram,
type=self.get_type_display(),
problem=self.passage.solution_number,
@ -1988,8 +1992,8 @@ class Synthesis(models.Model):
)
class Meta:
verbose_name = _("synthesis")
verbose_name_plural = _("syntheses")
verbose_name = _("written review")
verbose_name_plural = _("written reviews")
unique_together = (('participation', 'passage', 'type', ), )
ordering = ('passage__pool__round', 'type',)

View File

@ -47,10 +47,10 @@
<dt class="col-sm-3">{% trans "Syntheses:" %}</dt>
<dd class="col-sm-9">
{% for synthesis in passage.syntheses.all %}
<a href="{{ synthesis.file.url }}">{{ synthesis }}{% if not forloop.last %}, {% endif %}</a>
{% for review in passage.written_reviews.all %}
<a href="{{ review.file.url }}">{{ review }}{% if not forloop.last %}, {% endif %}</a>
{% empty %}
{% trans "No synthesis was uploaded yet." %}
{% trans "No review was uploaded yet." %}
{% endfor %}
</dd>
</dl>
@ -63,7 +63,7 @@
</div>
{% elif user.registration.participates %}
<div class="card-footer text-center">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadSynthesisModal">{% trans "Upload synthesis" %}</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadWrittenReviewModal">{% trans "Upload review" %}</button>
</div>
{% endif %}
</div>
@ -184,10 +184,10 @@
{% include "base_modal.html" with modal_id=note.modal_name %}
{% endfor %}
{% elif user.registration.participates %}
{% trans "Upload synthesis" as modal_title %}
{% trans "Upload review" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "participation:upload_synthesis" pk=passage.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadSynthesis" modal_enctype="multipart/form-data" %}
{% url "participation:upload_review" pk=passage.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadWrittenReview" modal_enctype="multipart/form-data" %}
{% endif %}
{% endblock %}
@ -201,8 +201,8 @@
initModal("{{ note.modal_name }}", "{% url "participation:update_notes" pk=note.pk %}")
{% endfor %}
{% elif user.registration.participates %}
initModal("uploadSynthesis", "{% url "participation:upload_synthesis" pk=passage.pk %}")
initModal("uploadWrittenReview", "{% url "participation:upload_review" pk=passage.pk %}")
{% endif %}
});
})
</script>
{% endblock %}

View File

@ -62,15 +62,15 @@
{% for passage in pool.passages.all %}
<li class="list-group-item">
{{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }} :
{% for synthesis in passage.syntheses.all %}
<a href="{{ synthesis.file.url }}">{{ synthesis.participation.team.trigram }} ({{ synthesis.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
{% for review in passage.written_reviews.all %}
<a href="{{ review.file.url }}">{{ review.participation.team.trigram }} ({{ review.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
{% empty %}
{% trans "No synthesis was uploaded yet." %}
{% trans "No review was uploaded yet." %}
{% endfor %}
</li>
{% endfor %}
</ul>
<a href="{% url 'participation:pool_download_syntheses' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary">
<a href="{% url 'participation:pool_download_written_review' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary">
<i class="fas fa-download"></i> {% trans "Download all" %}
</a>
</dd>

View File

@ -38,15 +38,15 @@
<dt class="col-sm-6 text-sm-end">{% trans 'date of the random draw'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.solutions_draw }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal syntheses submission for the first round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.syntheses_first_phase_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal written reviews submission for the first round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_first_phase_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal syntheses submission for the second round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.syntheses_second_phase_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal written reviews submission for the second round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_second_phase_limit }}</dd>
{% if TFJM.APP == "ETEAM" %}
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal syntheses submission for the third round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.syntheses_third_phase_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal written reviews submission for the third round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_third_phase_limit }}</dd>
{% endif %}
<dt class="col-sm-6 text-sm-end">{% trans 'description'|capfirst %}</dt>
@ -233,48 +233,48 @@
<ul>
<li>
<a href="{% url "participation:tournament_csv" pk=tournament.pk %}">
Validated team participant data spreadsheet
{% trans "Validated team participant data spreadsheet" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_csv" pk=tournament.pk %}?all">
All teams participant data spreadsheet
{% trans "All teams participant data spreadsheet" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_authorizations" tournament_id=tournament.id %}">
Archive of all authorisations sorted by team and person
{% trans "Archive of all authorisations sorted by team and person" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}">
Archive of all submitted solutions sorted by team
{% trans "Archive of all submitted solutions sorted by team" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}?sort_by=problem">
Archive of all sent solutions sorted by problem
{% trans "Archive of all sent solutions sorted by problem" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}?sort_by=pool">
Archive of all sent solutions sorted by pool
{% trans "Archive of all sent solutions sorted by pool" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_syntheses" tournament_id=tournament.id %}?sort_by=pool">
Archive of all summary notes sorted by pool and passage
<a href="{% url "participation:tournament_written_reviews" tournament_id=tournament.id %}?sort_by=pool">
{% trans "Archive of all summary notes sorted by pool and passage" %}
</a>
</li>
<li>
<a href="https://docs.google.com/spreadsheets/d/{{ tournament.notes_sheet_id }}/edit">
<i class="fas fa-table"></i>
Note spreadsheet on Google Sheets
{% trans "Note spreadsheet on Google Sheets" %}
</a>
</li>
<li>
<a href="{% url "participation:tournament_notation_sheets" tournament_id=tournament.id %}">
Archive of all printable note sheets sorted by pool
{% trans "Archive of all printable note sheets sorted by pool" %}
</a>
</li>
</ul>

View File

@ -8,11 +8,11 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, GSheetNotific
PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \
ScaleNotationSheetTemplateView, SelectTeamFinalView, \
SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \
SolutionsDownloadView, SolutionUploadView, \
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
TournamentHarmonizeNoteView, TournamentHarmonizeView, TournamentListView, TournamentPaymentsView, \
TournamentPublishNotesView, TournamentUpdateView
TournamentPublishNotesView, TournamentUpdateView, WrittenReviewUploadView
app_name = "participation"
@ -42,8 +42,8 @@ urlpatterns = [
name="tournament_authorizations"),
path("tournament/<int:tournament_id>/solutions/", SolutionsDownloadView.as_view(),
name="tournament_solutions"),
path("tournament/<int:tournament_id>/syntheses/", SolutionsDownloadView.as_view(),
name="tournament_syntheses"),
path("tournament/<int:tournament_id>/written_reviews/", SolutionsDownloadView.as_view(),
name="tournament_written_reviews"),
path("tournament/<int:tournament_id>/notation/sheets/", NotationSheetsArchiveView.as_view(),
name="tournament_notation_sheets"),
path("tournament/<int:pk>/notation/notifications/", GSheetNotificationsView.as_view(),
@ -60,7 +60,7 @@ urlpatterns = [
path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),
path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"),
path("pools/<int:pool_id>/solutions/", SolutionsDownloadView.as_view(), name="pool_download_solutions"),
path("pools/<int:pool_id>/syntheses/", SolutionsDownloadView.as_view(), name="pool_download_syntheses"),
path("pools/<int:pool_id>/written_reviews/", SolutionsDownloadView.as_view(), name="pool_download_written_reviews"),
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"),
@ -71,6 +71,6 @@ urlpatterns = [
path("pools/<int:pk>/upload-notes/template/", PoolNotesTemplateView.as_view(), name="pool_notes_template"),
path("pools/passages/<int:pk>/", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"),
path("pools/passages/<int:pk>/solution/", SynthesisUploadView.as_view(), name="upload_synthesis"),
path("pools/passages/<int:pk>/written_review/", WrittenReviewUploadView.as_view(), name="upload_written_review"),
path("pools/passages/notes/<int:pk>/", NoteUpdateView.as_view(), name="update_notes"),
]

View File

@ -46,9 +46,9 @@ from tfjm.lists import get_sympa_client
from tfjm.views import AdminMixin, VolunteerMixin
from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \
PoolForm, RequestValidationForm, SolutionForm, SynthesisForm, TeamForm, TournamentForm, \
UploadNotesForm, ValidateParticipationForm
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak
PoolForm, RequestValidationForm, SolutionForm, TeamForm, TournamentForm, UploadNotesForm, \
ValidateParticipationForm, WrittenReviewForm
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview
from .tables import NoteTable, ParticipationTable, PassageTable, PoolTable, TeamTable, TournamentTable
@ -977,7 +977,7 @@ class PoolUpdateView(VolunteerMixin, UpdateView):
class SolutionsDownloadView(VolunteerMixin, View):
"""
Download all solutions or syntheses as a ZIP archive.
Download all solutions or written reviews as a ZIP archive.
"""
def dispatch(self, request, *args, **kwargs):
@ -1018,11 +1018,12 @@ class SolutionsDownloadView(VolunteerMixin, View):
if 'team_id' in kwargs:
team = Team.objects.get(pk=kwargs["team_id"])
solutions = Solution.objects.filter(participation=team.participation).all()
syntheses = Synthesis.objects.filter(participation=team.participation).all()
filename = _("Solutions of team {trigram}.zip") if is_solution else _("Syntheses of team {trigram}.zip")
written_reviews = WrittenReview.objects.filter(participation=team.participation).all()
filename = _("Solutions of team {trigram}.zip") if is_solution \
else _("Written reviews of team {trigram}.zip")
filename = filename.format(trigram=team.trigram)
def prefix(s: Solution | Synthesis) -> str:
def prefix(s: Solution | WrittenReview) -> str:
return ""
elif 'tournament_id' in kwargs:
tournament = Tournament.objects.get(pk=kwargs["tournament_id"])
@ -1035,11 +1036,12 @@ class SolutionsDownloadView(VolunteerMixin, View):
for sol in pool.solutions:
sol.pool = pool
solutions.append(sol)
syntheses = Synthesis.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution else _("Syntheses of {tournament}.zip")
written_reviews = WrittenReview.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution \
else _("Written reviews of {tournament}.zip")
filename = filename.format(tournament=tournament.name)
def prefix(s: Solution | Synthesis) -> str:
def prefix(s: Solution | WrittenReview) -> str:
pool = s.pool if is_solution else s.passage.pool
p = f"Poule {pool.short_name}/"
if not is_solution:
@ -1050,27 +1052,28 @@ class SolutionsDownloadView(VolunteerMixin, View):
solutions = Solution.objects.filter(participation__tournament=tournament).all()
else:
solutions = Solution.objects.filter(final_solution=True).all()
syntheses = Synthesis.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution else _("Syntheses of {tournament}.zip")
written_reviews = WrittenReview.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution \
else _("Written reviews of {tournament}.zip")
filename = filename.format(tournament=tournament.name)
def prefix(s: Solution | Synthesis) -> str:
def prefix(s: Solution | WrittenReview) -> str:
return f"{s.participation.team.trigram}/" if sort_by == "team" else f"Problème {s.problem}/"
else:
pool = Pool.objects.get(pk=kwargs["pool_id"])
solutions = pool.solutions
syntheses = Synthesis.objects.filter(passage__pool=pool).all()
written_reviews = WrittenReview.objects.filter(passage__pool=pool).all()
filename = _("Solutions for pool {pool} of tournament {tournament}.zip") \
if is_solution else _("Syntheses for pool {pool} of tournament {tournament}.zip")
if is_solution else _("Written reviews for pool {pool} of tournament {tournament}.zip")
filename = filename.format(pool=pool.short_name,
tournament=pool.tournament.name)
def prefix(s: Solution | Synthesis) -> str:
def prefix(s: Solution | WrittenReview) -> str:
return ""
output = BytesIO()
zf = ZipFile(output, "w")
for s in (solutions if is_solution else syntheses):
for s in (solutions if is_solution else written_reviews):
if s.file.storage.exists(s.file.path):
zf.write("media/" + s.file.name, prefix(s) + f"{s}.pdf")
@ -2028,9 +2031,9 @@ class PassageUpdateView(VolunteerMixin, UpdateView):
return self.handle_no_permission()
class SynthesisUploadView(LoginRequiredMixin, FormView):
template_name = "participation/upload_synthesis.html"
form_class = SynthesisForm
class WrittenReviewUploadView(LoginRequiredMixin, FormView):
template_name = "participation/upload_written_review.html"
form_class = WrittenReviewForm
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.registration.participates:
@ -2057,14 +2060,14 @@ class SynthesisUploadView(LoginRequiredMixin, FormView):
form_syn = form.instance
form_syn.type = 1 if self.participation == self.passage.opponent \
else 2 if self.participation == self.passage.reviewer else 3
syn_qs = Synthesis.objects.filter(participation=self.participation,
syn_qs = WrittenReview.objects.filter(participation=self.participation,
passage=self.passage,
type=form_syn.type).all()
deadline = self.passage.pool.tournament.syntheses_first_phase_limit if self.passage.pool.round == 1 \
else self.passage.pool.tournament.syntheses_second_phase_limit
deadline = self.passage.pool.tournament.reviews_first_phase_limit if self.passage.pool.round == 1 \
else self.passage.pool.tournament.reviews_second_phase_limit
if syn_qs.exists() and timezone.now() > deadline:
form.add_error(None, _("You can't upload a synthesis after the deadline."))
form.add_error(None, _("You can't upload a written review after the deadline."))
return self.form_invalid(form)
# Drop previous solution if existing

View File

@ -26,7 +26,7 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from django_tables2 import SingleTableView
from magic import Magic
from participation.models import Passage, Solution, Synthesis, Tournament
from participation.models import Passage, Solution, Tournament, WrittenReview
from tfjm.tokens import email_validation_token
from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin
@ -871,30 +871,30 @@ class SolutionView(LoginRequiredMixin, View):
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
class SynthesisView(LoginRequiredMixin, View):
class WrittenReviewView(LoginRequiredMixin, View):
"""
Display the sent synthesis.
Display the sent written reviews.
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
path = f"media/syntheses/{filename}"
path = f"media/reviews/{filename}"
if not os.path.exists(path):
raise Http404
synthesis = Synthesis.objects.get(file__endswith=filename)
reviews = WrittenReview.objects.get(file__endswith=filename)
user = request.user
if not (user.registration.is_admin or user.registration.is_volunteer
and (user.registration in synthesis.passage.pool.juries.all()
or user.registration in synthesis.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=synthesis.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == synthesis.participation.team):
and (user.registration in reviews.passage.pool.juries.all()
or user.registration in reviews.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=reviews.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == reviews.participation.team):
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 = str(synthesis) + f".{ext}"
true_file_name = str(reviews) + f".{ext}"
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)

View File

@ -24,7 +24,7 @@ from django.views.defaults import bad_request, page_not_found, permission_denied
from django.views.generic import TemplateView
from participation.views import MotivationLetterView
from registration.views import HealthSheetView, ParentalAuthorizationView, PhotoAuthorizationView, \
ReceiptView, SolutionView, SynthesisView, VaccineSheetView
ReceiptView, SolutionView, VaccineSheetView, WrittenReviewView
from .views import AdminSearchView
@ -61,8 +61,8 @@ urlpatterns = [
path('media/solutions/<str:filename>/', SolutionView.as_view(),
name='solution'),
path('media/syntheses/<str:filename>/', SynthesisView.as_view(),
name='synthesis'),
path('media/reviews/<str:filename>/', WrittenReviewView.as_view(),
name='reviews'),
]
if settings.DEBUG: