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 "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 #: 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 #: draw/models.py:27 participation/admin.py:79 participation/admin.py:144
#: participation/admin.py:176 participation/models.py:783 #: 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/models.py:763
#: registration/templates/registration/payment_form.html:53 #: registration/templates/registration/payment_form.html:53
msgid "tournament" msgid "tournament"
@ -95,7 +95,7 @@ msgstr ""
#: chat/models.py:73 draw/models.py:446 draw/models.py:473 #: chat/models.py:73 draw/models.py:446 draw/models.py:473
#: participation/admin.py:140 participation/admin.py:160 #: 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 #: participation/tables.py:84
msgid "pool" msgid "pool"
msgstr "poule" msgstr "poule"
@ -265,7 +265,7 @@ msgid "teams"
msgstr "équipes" msgstr "équipes"
#: draw/admin.py:92 draw/models.py:245 draw/models.py:465 #: draw/admin.py:92 draw/models.py:245 draw/models.py:465
#: participation/models.py:1120 #: participation/models.py:1135
msgid "round" msgid "round"
msgstr "tour" msgstr "tour"
@ -634,7 +634,7 @@ msgstr "Le numéro du tour doit être entre 1 et {nb}."
msgid "rounds" msgid "rounds"
msgstr "tours" msgstr "tours"
#: draw/models.py:268 participation/models.py:1128 #: draw/models.py:268 participation/models.py:1143
msgid "letter" msgid "letter"
msgstr "lettre" msgstr "lettre"
@ -672,12 +672,12 @@ msgstr "L'instance complète de la poule."
msgid "Pool {letter}{number}" msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}" msgstr "Poule {letter}{number}"
#: draw/models.py:447 participation/models.py:1652 #: draw/models.py:447 participation/models.py:1667
msgid "pools" msgid "pools"
msgstr "poules" msgstr "poules"
#: draw/models.py:459 participation/models.py:1106 participation/models.py:1871 #: draw/models.py:459 participation/models.py:1121 participation/models.py:1886
#: participation/models.py:1901 participation/models.py:1943 #: participation/models.py:1920 participation/models.py:1962
msgid "participation" msgid "participation"
msgstr "participation" msgstr "participation"
@ -701,9 +701,9 @@ msgid ""
msgstr "" msgstr ""
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1." "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 #: draw/models.py:496 draw/models.py:519 participation/models.py:1252
#: participation/models.py:1674 participation/models.py:1908 #: participation/models.py:1689 participation/models.py:1927
#: participation/views.py:1489 participation/views.py:1754 #: participation/views.py:1492 participation/views.py:1757
#, python-brace-format #, python-brace-format
msgid "Problem #{problem}" msgid "Problem #{problem}"
msgstr "Problème n°{problem}" msgstr "Problème n°{problem}"
@ -919,26 +919,26 @@ msgid "selected for final"
msgstr "sélectionnée pour la finale" msgstr "sélectionnée pour la finale"
#: participation/admin.py:124 participation/admin.py:188 #: 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" msgid "defender"
msgstr "défenseur⋅se" msgstr "défenseur⋅se"
#: participation/admin.py:128 participation/models.py:1688 #: participation/admin.py:128 participation/models.py:1703
#: participation/models.py:1955 #: participation/models.py:1974
msgid "opponent" msgid "opponent"
msgstr "opposant⋅e" msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:1695 #: participation/admin.py:132 participation/models.py:1710
#: participation/models.py:1956 #: participation/models.py:1975
msgid "reviewer" msgid "reviewer"
msgstr "rapporteur⋅rice" msgstr "rapporteur⋅rice"
#: participation/admin.py:136 participation/models.py:1702 #: participation/admin.py:136 participation/models.py:1717
#: participation/models.py:1957 #: participation/models.py:1976
msgid "observer" msgid "observer"
msgstr "observateur⋅rice" msgstr "observateur⋅rice"
#: participation/admin.py:192 participation/models.py:1906 #: participation/admin.py:192 participation/models.py:1925
msgid "problem" msgid "problem"
msgstr "numéro de problème" msgstr "numéro de problème"
@ -1239,7 +1239,7 @@ msgid "first phase date"
msgstr "date du premier tour" msgstr "date du premier tour"
#: participation/models.py:327 #: 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" msgstr "date limite pour envoyer les notes de synthèses pour la première phase"
#: participation/models.py:332 #: participation/models.py:332
@ -1252,7 +1252,7 @@ msgstr ""
"cocher la case lorsque les solutions pour le second tour sont accessibles" "cocher la case lorsque les solutions pour le second tour sont accessibles"
#: participation/models.py:342 #: 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" msgstr "date limite d'envoi des notes de synthèse pour la seconde phase"
#: participation/models.py:347 #: participation/models.py:347
@ -1265,7 +1265,7 @@ msgstr ""
"cocher la case lorsque les solutions pour le second tour sont accessibles" "cocher la case lorsque les solutions pour le second tour sont accessibles"
#: participation/models.py:357 #: 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 "" msgstr ""
"date limite pour envoyer les notes de synthèses pour la troisième phase" "date limite pour envoyer les notes de synthèses pour la troisième phase"
@ -1294,7 +1294,7 @@ msgid "Final ranking"
msgstr "Classement final" msgstr "Classement final"
#: participation/models.py:481 participation/models.py:553 #: 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" msgid "Team"
msgstr "Équipe" msgstr "Équipe"
@ -1326,15 +1326,15 @@ msgstr "Scores jour 3"
msgid "Tweaks day 3" msgid "Tweaks day 3"
msgstr "Ajustements 3" msgstr "Ajustements 3"
#: participation/models.py:485 participation/models.py:1312 #: participation/models.py:485 participation/models.py:1327
#: participation/views.py:1735 #: participation/views.py:1738
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: participation/models.py:485 participation/models.py:553 #: participation/models.py:485 participation/models.py:553
#: participation/models.py:1312 #: participation/models.py:1327
#: participation/templates/participation/tournament_harmonize.html:14 #: participation/templates/participation/tournament_harmonize.html:14
#: participation/views.py:1738 #: participation/views.py:1741
msgid "Rank" msgid "Rank"
msgstr "Rang" msgstr "Rang"
@ -1346,7 +1346,7 @@ msgstr "Score"
msgid "Mention" msgid "Mention"
msgstr "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." msgid "Don't update the table structure for a better automated integration."
msgstr "" msgstr ""
"Ne pas mettre à jour la structure de la table pour une meilleure intégration " "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 " "tour, vous défendrez <a href='{solution_url}'>votre solution du problème "
"{problem}</a>.</p>" "{problem}</a>.</p>"
#: participation/models.py:930 participation/models.py:988 #: participation/models.py:930 participation/models.py:993
#: participation/models.py:1047 #: participation/models.py:1057
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will oppose the solution of the team {opponent} on the <a " "<p>You will oppose the solution of the team {opponent} on the <a "
"href='{solution_url}'>problem {problem}</a>. You can upload your synthesis " "href='{solution_url}'>problem {problem}</a>. You can upload your written "
"sheet on <a href='{passage_url}'>this page</a>.</p>" "review on <a href='{passage_url}'>this page</a>.</p>"
msgstr "" msgstr ""
"<p>Vous opposerez la solution de l'équipe {opponent} sur le <a " "<p>Vous opposerez la solution de l'équipe {opponent} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:939 participation/models.py:997 #: participation/models.py:939 participation/models.py:1002
#: participation/models.py:1056 #: participation/models.py:1066
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will report the solution of the team {reviewer} on the <a " "<p>You will report the solution of the team {reviewer} on the <a "
"href='{solution_url}'>problem {problem}. You can upload your synthesis sheet " "href='{solution_url}'>problem {problem}</a>. You can upload your written "
"on <a href='{passage_url}'>this page</a>.</p>" "review on <a href='{passage_url}'>this page</a>.</p>"
msgstr "" msgstr ""
"<p>Vous rapporterez la solution de l'équipe {reviewer} sur le <a " "<p>Vous rapporterez la solution de l'équipe {reviewer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:949 participation/models.py:1007 #: participation/models.py:949 participation/models.py:1012
#: participation/models.py:1066 #: participation/models.py:1076
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will observe the solution of the team {observer} on the <a " "<p>You will observe the solution of the team {observer} on the <a "
"href='{solution_url}'>problem {problem}. You can upload your synthesis sheet " "href='{solution_url}'>problem {problem}</a>. You can upload your written "
"on <a href='{passage_url}'>this page</a>.</p>" "review on <a href='{passage_url}'>this page</a>.</p>"
msgstr "" msgstr ""
"<p>Vous observerez la solution de l'équipe {observer} sur le <a " "<p>Vous observerez la solution de l'équipe {observer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "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" msgid "First round"
msgstr "Premier tour" msgstr "Premier tour"
#: participation/models.py:981 #: participation/models.py:986
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the second round, you will defend <a href='{solution_url}'>your " "<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 " "<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>" "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 #: registration/models.py:640
msgid "Second round" msgid "Second round"
msgstr "Second tour" msgstr "Second tour"
#: participation/models.py:1040 #: participation/models.py:1050
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the third round, you will defend <a href='{solution_url}'>your " "<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 " "<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>" "solution du problème {problem}</a>.</p>"
#: participation/models.py:1092 #: participation/models.py:1107
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>The tournament {tournament} is ended. You can check the results on the <a " "<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 " "<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats "
"sur la <a href='{url}'>page du tournoi</a>.</p>" "sur la <a href='{url}'>page du tournoi</a>.</p>"
#: participation/models.py:1097 #: participation/models.py:1112
msgid "Tournament ended" msgid "Tournament ended"
msgstr "Tournoi terminé" msgstr "Tournoi terminé"
#: participation/models.py:1107 participation/models.py:1150 #: participation/models.py:1122 participation/models.py:1165
msgid "participations" msgid "participations"
msgstr "participations" msgstr "participations"
#: participation/models.py:1122 participation/models.py:1123 #: participation/models.py:1137 participation/models.py:1138
#: participation/models.py:1124 #: participation/models.py:1139
#, python-brace-format #, python-brace-format
msgid "Round {round}" msgid "Round {round}"
msgstr "Tour {round}" msgstr "Tour {round}"
#: participation/models.py:1138 #: participation/models.py:1153
msgid "room" msgid "room"
msgstr "salle" msgstr "salle"
#: participation/models.py:1140 #: participation/models.py:1155
msgid "Room 1" msgid "Room 1"
msgstr "Salle 1" msgstr "Salle 1"
#: participation/models.py:1141 #: participation/models.py:1156
msgid "Room 2" msgid "Room 2"
msgstr "Salle 2" msgstr "Salle 2"
#: participation/models.py:1144 #: participation/models.py:1159
msgid "For 5-teams pools only" msgid "For 5-teams pools only"
msgstr "Pour les poules de 5 équipe uniquement" msgstr "Pour les poules de 5 équipe uniquement"
#: participation/models.py:1156 #: participation/models.py:1171
msgid "juries" msgid "juries"
msgstr "jurys" msgstr "jurys"
#: participation/models.py:1165 #: participation/models.py:1180
msgid "president of the jury" msgid "president of the jury"
msgstr "président⋅e du jury" msgstr "président⋅e du jury"
#: participation/models.py:1172 #: participation/models.py:1187
msgid "BigBlueButton URL" msgid "BigBlueButton URL"
msgstr "Lien BigBlueButton" msgstr "Lien BigBlueButton"
#: participation/models.py:1173 #: participation/models.py:1188
msgid "The link of the BBB visio for this pool." msgid "The link of the BBB visio for this pool."
msgstr "Le lien du salon BBB pour cette poule." msgstr "Le lien du salon BBB pour cette poule."
#: participation/models.py:1178 #: participation/models.py:1193
msgid "results available" msgid "results available"
msgstr "résultats disponibles" msgstr "résultats disponibles"
#: participation/models.py:1179 #: participation/models.py:1194
msgid "" msgid ""
"Check this case when results become accessible to teams. They stay " "Check this case when results become accessible to teams. They stay "
"accessible to you. Only averages are given." "accessible to you. Only averages are given."
@ -1587,65 +1587,65 @@ msgstr ""
"Ils restent toujours accessibles pour vous. Seules les moyennes sont " "Ils restent toujours accessibles pour vous. Seules les moyennes sont "
"communiquées." "communiquées."
#: participation/models.py:1211 #: participation/models.py:1226
msgid "The president of the jury must be part of the jury." msgid "The president of the jury must be part of the jury."
msgstr "Læ président⋅e du jury doit faire partie du jury." msgstr "Læ président⋅e du jury doit faire partie du jury."
#: participation/models.py:1238 participation/models.py:1312 #: participation/models.py:1253 participation/models.py:1327
#: participation/views.py:1483 participation/views.py:1732 #: participation/views.py:1486 participation/views.py:1735
msgid "Problem" msgid "Problem"
msgstr "Problème" msgstr "Problème"
#: participation/models.py:1243 participation/views.py:1498 #: participation/models.py:1258 participation/views.py:1501
msgid "Role" msgid "Role"
msgstr "Rôle" msgstr "Rôle"
#: participation/models.py:1248 participation/views.py:1532 #: participation/models.py:1263 participation/views.py:1535
#: participation/views.py:1533 #: participation/views.py:1536
msgid "Juree" msgid "Juree"
msgstr "Juré⋅e" msgstr "Juré⋅e"
#: participation/models.py:1271 participation/models.py:1597 #: participation/models.py:1286 participation/models.py:1612
#: participation/models.py:1619 participation/views.py:1602 #: participation/models.py:1634 participation/views.py:1605
msgid "Average" msgid "Average"
msgstr "Moyenne" msgstr "Moyenne"
#: participation/models.py:1277 participation/views.py:1621 #: participation/models.py:1292 participation/views.py:1624
msgid "Coefficient" msgid "Coefficient"
msgstr "Coefficien" msgstr "Coefficien"
#: participation/models.py:1278 participation/views.py:1664 #: participation/models.py:1293 participation/views.py:1667
msgid "Subtotal" msgid "Subtotal"
msgstr "Sous-total" msgstr "Sous-total"
#: participation/models.py:1544 #: participation/models.py:1559
#, python-brace-format #, python-brace-format
msgid "Input must be a valid integer between {min_note} and {max_note}." 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}." 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 #, python-brace-format
msgid "The jury {jury} is not part of the jury for this pool." msgid "The jury {jury} is not part of the jury for this pool."
msgstr "{jury} ne fait pas partie du jury pour cette poule." msgstr "{jury} ne fait pas partie du jury pour cette poule."
#: participation/models.py:1645 #: participation/models.py:1660
#, python-brace-format #, python-brace-format
msgid "Pool {code} for tournament {tournament} with teams {teams}" msgid "Pool {code} for tournament {tournament} with teams {teams}"
msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}" msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}"
#: participation/models.py:1665 #: participation/models.py:1680
msgid "position" msgid "position"
msgstr "position" msgstr "position"
#: participation/models.py:1672 #: participation/models.py:1687
msgid "defended solution" msgid "defended solution"
msgstr "solution défendue" msgstr "solution défendue"
#: participation/models.py:1710 #: participation/models.py:1725
msgid "penalties" msgid "penalties"
msgstr "pénalités" msgstr "pénalités"
#: participation/models.py:1712 #: participation/models.py:1727
msgid "" msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 " "Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty." "coefficient per penalty."
@ -1653,128 +1653,128 @@ msgstr ""
"Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 " "Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 "
"sur sa présentation orale par pénalité." "sur sa présentation orale par pénalité."
#: participation/models.py:1838 participation/models.py:1841 #: participation/models.py:1853 participation/models.py:1856
#: participation/models.py:1844 participation/models.py:1847 #: participation/models.py:1859 participation/models.py:1862
#, python-brace-format #, python-brace-format
msgid "Team {trigram} is not registered in the pool." msgid "Team {trigram} is not registered in the pool."
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule." msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: participation/models.py:1852 #: participation/models.py:1867
#, python-brace-format #, python-brace-format
msgid "Passage of {defender} for problem {problem}" msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}" msgstr "Passage de {defender} pour le problème {problem}"
#: participation/models.py:1856 participation/models.py:1865 #: participation/models.py:1871 participation/models.py:1880
#: participation/models.py:1950 participation/models.py:1993 #: participation/models.py:1969 participation/models.py:2012
msgid "passage" msgid "passage"
msgstr "passage" msgstr "passage"
#: participation/models.py:1857 #: participation/models.py:1872
msgid "passages" msgid "passages"
msgstr "passages" msgstr "passages"
#: participation/models.py:1876 #: participation/models.py:1891
msgid "difference" msgid "difference"
msgstr "différence" msgstr "différence"
#: participation/models.py:1877 #: participation/models.py:1892
msgid "Score to add/remove on the final score" msgid "Score to add/remove on the final score"
msgstr "Score à ajouter/retrancher au score final" msgstr "Score à ajouter/retrancher au score final"
#: participation/models.py:1884 #: participation/models.py:1899
msgid "tweak" msgid "tweak"
msgstr "harmonisation" msgstr "harmonisation"
#: participation/models.py:1885 #: participation/models.py:1900
msgid "tweaks" msgid "tweaks"
msgstr "harmonisations" msgstr "harmonisations"
#: participation/models.py:1913 #: participation/models.py:1932
msgid "solution for the final tournament" msgid "solution for the final tournament"
msgstr "solution pour la finale" msgstr "solution pour la finale"
#: participation/models.py:1918 participation/models.py:1962 #: participation/models.py:1937 participation/models.py:1981
msgid "file" msgid "file"
msgstr "fichier" msgstr "fichier"
#: participation/models.py:1928 #: participation/models.py:1947
#, python-brace-format #, python-brace-format
msgid "Solution of team {team} for problem {problem}" msgid "Solution of team {team} for problem {problem}"
msgstr "Solution de l'équipe {team} pour le problème {problem}" msgstr "Solution de l'équipe {team} pour le problème {problem}"
#: participation/models.py:1930 #: participation/models.py:1949
msgid "for final" msgid "for final"
msgstr "pour la finale" msgstr "pour la finale"
#: participation/models.py:1933 #: participation/models.py:1952
msgid "solution" msgid "solution"
msgstr "solution" msgstr "solution"
#: participation/models.py:1934 #: participation/models.py:1953
msgid "solutions" msgid "solutions"
msgstr "solutions" msgstr "solutions"
#: participation/models.py:1968 #: participation/models.py:1987
#, python-brace-format #, 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 "" msgstr ""
"Note de synthèse de l'équipe {team} en tant que {type} pour le problème " "Note de synthèse de l'équipe {team} en tant que {type} pour le problème "
"{problem} de {defender}" "{problem} de {defender}"
#: participation/models.py:1976 #: participation/models.py:1995
msgid "synthesis" msgid "written review"
msgstr "note de synthèse" msgstr "note de synthèse"
#: participation/models.py:1977 #: participation/models.py:1996
msgid "syntheses" msgid "written reviews"
msgstr "notes de synthèse" msgstr "notes de synthèse"
#: participation/models.py:1986 #: participation/models.py:2005
msgid "jury" msgid "jury"
msgstr "jury" msgstr "jury"
#: participation/models.py:1998 #: participation/models.py:2017
msgid "defender writing note" msgid "defender writing note"
msgstr "note d'écrit défenseur⋅se" msgstr "note d'écrit défenseur⋅se"
#: participation/models.py:2004 #: participation/models.py:2023
msgid "defender oral note" msgid "defender oral note"
msgstr "note d'oral défenseur⋅se" msgstr "note d'oral défenseur⋅se"
#: participation/models.py:2010 #: participation/models.py:2029
msgid "opponent writing note" msgid "opponent writing note"
msgstr "note d'écrit opposant⋅e" msgstr "note d'écrit opposant⋅e"
#: participation/models.py:2016 #: participation/models.py:2035
msgid "opponent oral note" msgid "opponent oral note"
msgstr "note d'oral opposant⋅e" msgstr "note d'oral opposant⋅e"
#: participation/models.py:2022 #: participation/models.py:2041
msgid "reviewer writing note" msgid "reviewer writing note"
msgstr "note d'écrit rapporteur⋅rice" msgstr "note d'écrit rapporteur⋅rice"
#: participation/models.py:2028 #: participation/models.py:2047
msgid "reviewer oral note" msgid "reviewer oral note"
msgstr "note d'oral du rapporteur⋅rice" msgstr "note d'oral du rapporteur⋅rice"
#: participation/models.py:2034 #: participation/models.py:2053
msgid "observer writing note" msgid "observer writing note"
msgstr "note d'écrit de l'observateur⋅rice" msgstr "note d'écrit de l'observateur⋅rice"
#: participation/models.py:2040 #: participation/models.py:2059
msgid "observer oral note" msgid "observer oral note"
msgstr "note d'oral de l'observateur⋅rice" msgstr "note d'oral de l'observateur⋅rice"
#: participation/models.py:2105 #: participation/models.py:2124
#, python-brace-format #, python-brace-format
msgid "Notes of {jury} for {passage}" msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}" msgstr "Notes de {jury} pour le {passage}"
#: participation/models.py:2108 #: participation/models.py:2127
msgid "note" msgid "note"
msgstr "note" msgstr "note"
#: participation/models.py:2109 #: participation/models.py:2128
msgid "notes" msgid "notes"
msgstr "notes" msgstr "notes"
@ -1910,7 +1910,7 @@ msgstr "Envoyer une solution"
#: participation/templates/participation/upload_motivation_letter.html:13 #: participation/templates/participation/upload_motivation_letter.html:13
#: participation/templates/participation/upload_notes.html:24 #: participation/templates/participation/upload_notes.html:24
#: participation/templates/participation/upload_solution.html:11 #: 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_health_sheet.html:17
#: registration/templates/registration/upload_parental_authorization.html:17 #: registration/templates/registration/upload_parental_authorization.html:17
#: registration/templates/registration/upload_photo_authorization.html:18 #: 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/passage_detail.html:53
#: participation/templates/participation/pool_detail.html:68 #: 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." msgstr "Aucune note de synthèse n'a encore été envoyée."
#: participation/templates/participation/passage_detail.html:61 #: 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:66
#: participation/templates/participation/passage_detail.html:187 #: participation/templates/participation/passage_detail.html:187
msgid "Upload synthesis" msgid "Upload review"
msgstr "Envoyer une note de synthèse" msgstr "Envoyer la note de synthèse"
#: participation/templates/participation/passage_detail.html:74 #: participation/templates/participation/passage_detail.html:74
msgid "Notes detail" msgid "Notes detail"
@ -2374,15 +2374,15 @@ msgid "date of the random draw"
msgstr "date du tirage au sort" msgstr "date du tirage au sort"
#: participation/templates/participation/tournament_detail.html:41 #: 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" msgstr "date limite de soumission des notes de synthèse pour le premier tour"
#: participation/templates/participation/tournament_detail.html:44 #: 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" msgstr "date limite de soumission des notes de synthèse pour le second tour"
#: participation/templates/participation/tournament_detail.html:48 #: 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" msgstr "date limite de soumission des notes de synthèse pour le troisième tour"
#: participation/templates/participation/tournament_detail.html:56 #: 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" msgid "Files available for download"
msgstr "Fichiers disponibles au téléchargement" 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 #: participation/templates/participation/tournament_harmonize.html:16
#: registration/models.py:655 #: registration/models.py:655
msgid "Note" msgid "Note"
@ -2504,11 +2540,11 @@ msgstr ""
msgid "Download empty notation sheet" msgid "Download empty notation sheet"
msgstr "Télécharger la fiche de notation vierge" 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:" msgid "Templates:"
msgstr "Modèles :" msgstr "Modèles :"
#: participation/templates/participation/upload_synthesis.html:13 #: participation/templates/participation/upload_written_review.html:14
msgid "Warning: non-free format" msgid "Warning: non-free format"
msgstr "Attention : format non libre" 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" msgid "Solutions of team {trigram}.zip"
msgstr "Solutions de l'équipe {trigram}.zip" msgstr "Solutions de l'équipe {trigram}.zip"
#: participation/views.py:1022 #: participation/views.py:1023
#, python-brace-format #, 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" 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 #, python-brace-format
msgid "Solutions of {tournament}.zip" msgid "Solutions of {tournament}.zip"
msgstr "Solutions de {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 #, python-brace-format
msgid "Syntheses of {tournament}.zip" msgid "Written reviews of {tournament}.zip"
msgstr "Notes de synthèse de {tournament}.zip" msgstr "Notes de synthèse de {tournament}.zip"
#: participation/views.py:1063 #: participation/views.py:1066
#, python-brace-format #, python-brace-format
msgid "Solutions for pool {pool} of tournament {tournament}.zip" msgid "Solutions for pool {pool} of tournament {tournament}.zip"
msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip" msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:1064 #: participation/views.py:1067
#, python-brace-format #, 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" msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:1106 #: participation/views.py:1109
#, python-brace-format #, python-brace-format
msgid "Jury of pool {pool} for {tournament} with teams {teams}" msgid "Jury of pool {pool} for {tournament} with teams {teams}"
msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}" msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
#: participation/views.py:1122 #: participation/views.py:1125
#, python-brace-format #, python-brace-format
msgid "The jury {name} is already in the pool!" msgid "The jury {name} is already in the pool!"
msgstr "{name} est déjà dans la poule !" msgstr "{name} est déjà dans la poule !"
#: participation/views.py:1142 #: participation/views.py:1145
msgid "New jury account" msgid "New jury account"
msgstr "Nouveau compte de juré⋅e" msgstr "Nouveau compte de juré⋅e"
#: participation/views.py:1163 #: participation/views.py:1166
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully added!" msgid "The jury {name} has been successfully added!"
msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !" 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 #, python-brace-format
msgid "The jury {name} has been successfully removed!" msgid "The jury {name} has been successfully removed!"
msgstr "{name} a été retiré⋅e avec succès du jury !" msgstr "{name} a été retiré⋅e avec succès du jury !"
#: participation/views.py:1225 #: participation/views.py:1228
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully promoted president!" msgid "The jury {name} has been successfully promoted president!"
msgstr "{name} a été nommé⋅e président⋅e du jury !" 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:" 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 :" 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." msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées." msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1504 #: participation/views.py:1507
msgid "Defender" msgid "Defender"
msgstr "Défenseur⋅se" msgstr "Défenseur⋅se"
#: participation/views.py:1510 #: participation/views.py:1513
msgid "Opponent" msgid "Opponent"
msgstr "Opposant⋅e" msgstr "Opposant⋅e"
#: participation/views.py:1517 #: participation/views.py:1520
msgid "Reviewer" msgid "Reviewer"
msgstr "Rapporteur⋅rice" msgstr "Rapporteur⋅rice"
#: participation/views.py:1524 #: participation/views.py:1527
msgid "Observer" msgid "Observer"
msgstr "Observateur⋅rice" msgstr "Observateur⋅rice"
#: participation/views.py:1895 #: participation/views.py:1898
#, python-brace-format #, python-brace-format
msgid "Notation sheets of pool {pool} of {tournament}.zip" msgid "Notation sheets of pool {pool} of {tournament}.zip"
msgstr "Feuilles de notations pour la poule {pool} du tournoi {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 #, python-brace-format
msgid "Notation sheets of {tournament}.zip" msgid "Notation sheets of {tournament}.zip"
msgstr "Feuilles de notation de {tournament}.zip" msgstr "Feuilles de notation de {tournament}.zip"
#: participation/views.py:2067 #: participation/views.py:2070
msgid "You can't upload a synthesis after the deadline." 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." 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 #: 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.contrib import admin
from django.utils.translation import gettext_lazy as _ 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): class ParticipationInline(admin.StackedInline):
@ -32,8 +32,8 @@ class SolutionInline(admin.TabularInline):
show_change_link = True show_change_link = True
class SynthesisInline(admin.TabularInline): class WrittenReviewInline(admin.TabularInline):
model = Synthesis model = WrittenReview
extra = 0 extra = 0
ordering = ('passage__solution_number', 'type',) ordering = ('passage__solution_number', 'type',)
autocomplete_fields = ('passage',) autocomplete_fields = ('passage',)
@ -95,7 +95,7 @@ class ParticipationAdmin(admin.ModelAdmin):
search_fields = ('team__name', 'team__trigram',) search_fields = ('team__name', 'team__trigram',)
list_filter = ('valid', 'tournament',) list_filter = ('valid', 'tournament',)
autocomplete_fields = ('team', 'tournament',) autocomplete_fields = ('team', 'tournament',)
inlines = (SolutionInline, SynthesisInline,) inlines = (SolutionInline, WrittenReviewInline,)
@admin.register(Pool) @admin.register(Pool)
@ -178,19 +178,19 @@ class SolutionAdmin(admin.ModelAdmin):
return Tournament.final_tournament() if record.final_solution else record.participation.tournament return Tournament.final_tournament() if record.final_solution else record.participation.tournament
@admin.register(Synthesis) @admin.register(WrittenReview)
class SynthesisAdmin(admin.ModelAdmin): class WrittenReviewAdmin(admin.ModelAdmin):
list_display = ('participation', 'type', 'defender', 'passage',) list_display = ('participation', 'type', 'defender', 'passage',)
list_filter = ('participation__tournament', 'type', 'passage__solution_number',) list_filter = ('participation__tournament', 'type', 'passage__solution_number',)
search_fields = ('participation__team__name', 'participation__team__trigram',) search_fields = ('participation__team__name', 'participation__team__trigram',)
autocomplete_fields = ('participation', 'passage',) autocomplete_fields = ('participation', 'passage',)
@admin.display(description=_("defender")) @admin.display(description=_("defender"))
def defender(self, record: Synthesis): def defender(self, record: WrittenReview):
return record.passage.defender return record.passage.defender
@admin.display(description=_("problem")) @admin.display(description=_("problem"))
def problem(self, record: Synthesis): def problem(self, record: WrittenReview):
return record.passage.solution_number return record.passage.solution_number

View File

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

View File

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from .views import NoteViewSet, ParticipationViewSet, PassageViewSet, PoolViewSet, \ from .views import NoteViewSet, ParticipationViewSet, PassageViewSet, PoolViewSet, \
SolutionViewSet, SynthesisViewSet, TeamViewSet, TournamentViewSet, TweakViewSet SolutionViewSet, TeamViewSet, TournamentViewSet, TweakViewSet, WrittenReviewViewSet
def register_participation_urls(router, path): def register_participation_urls(router, path):
@ -13,8 +13,8 @@ def register_participation_urls(router, path):
router.register(path + "/participation", ParticipationViewSet) router.register(path + "/participation", ParticipationViewSet)
router.register(path + "/passage", PassageViewSet) router.register(path + "/passage", PassageViewSet)
router.register(path + "/pool", PoolViewSet) router.register(path + "/pool", PoolViewSet)
router.register(path + "/review", WrittenReviewViewSet)
router.register(path + "/solution", SolutionViewSet) router.register(path + "/solution", SolutionViewSet)
router.register(path + "/synthesis", SynthesisViewSet)
router.register(path + "/team", TeamViewSet) router.register(path + "/team", TeamViewSet)
router.register(path + "/tournament", TournamentViewSet) router.register(path + "/tournament", TournamentViewSet)
router.register(path + "/tweak", TweakViewSet) 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 rest_framework.viewsets import ModelViewSet
from .serializers import NoteSerializer, ParticipationSerializer, PassageSerializer, PoolSerializer, \ from .serializers import NoteSerializer, ParticipationSerializer, PassageSerializer, PoolSerializer, \
SolutionSerializer, SynthesisSerializer, TeamSerializer, TournamentSerializer, TweakSerializer SolutionSerializer, TeamSerializer, TournamentSerializer, TweakSerializer, WrittenReviewSerializer
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 NoteViewSet(ModelViewSet): class NoteViewSet(ModelViewSet):
@ -44,9 +44,9 @@ class SolutionViewSet(ModelViewSet):
filterset_fields = ['participation', 'number', 'problem', 'final_solution', ] filterset_fields = ['participation', 'number', 'problem', 'final_solution', ]
class SynthesisViewSet(ModelViewSet): class WrittenReviewViewSet(ModelViewSet):
queryset = Synthesis.objects.all() queryset = WrittenReview.objects.all()
serializer_class = SynthesisSerializer serializer_class = WrittenReviewSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['participation', 'number', 'passage', 'type', ] filterset_fields = ['participation', 'number', 'passage', 'type', ]
@ -64,9 +64,9 @@ class TournamentViewSet(ModelViewSet):
serializer_class = TournamentSerializer serializer_class = TournamentSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote', filterset_fields = ['name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote',
'inscription_limit', 'solution_limit', 'solutions_draw', 'syntheses_first_phase_limit', 'inscription_limit', 'solution_limit', 'solutions_draw', 'reviews_first_phase_limit',
'solutions_available_second_phase', 'syntheses_second_phase_limit', 'solutions_available_second_phase', 'reviews_second_phase_limit',
'solutions_available_third_phase', 'syntheses_third_phase_limit', 'solutions_available_third_phase', 'reviews_third_phase_limit',
'description', 'organizers', 'final', ] 'description', 'organizers', 'final', ]

View File

@ -16,7 +16,7 @@ from pypdf import PdfReader
from registration.models import VolunteerRegistration from registration.models import VolunteerRegistration
from tfjm import settings 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): class TeamForm(forms.ModelForm):
@ -137,7 +137,7 @@ class TournamentForm(forms.ModelForm):
if settings.NB_ROUNDS < 3: if settings.NB_ROUNDS < 3:
del self.fields['date_third_phase'] del self.fields['date_third_phase']
del self.fields['solutions_available_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: if not settings.PAYMENT_MANAGEMENT:
del self.fields['price'] 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'), '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'), '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'), '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'), format='%Y-%m-%d %H:%M'),
'date_second_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'), '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'), format='%Y-%m-%d %H:%M'),
'date_third_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'), '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'), format='%Y-%m-%d %H:%M'),
'organizers': forms.SelectMultiple(attrs={ 'organizers': forms.SelectMultiple(attrs={
'class': 'selectpicker', 'class': 'selectpicker',
@ -359,7 +359,7 @@ class PassageForm(forms.ModelForm):
fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',) fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',)
class SynthesisForm(forms.ModelForm): class WrittenReviewForm(forms.ModelForm):
def clean_file(self): def clean_file(self):
if "file" in self.files: if "file" in self.files:
file = self.files["file"] file = self.files["file"]
@ -375,11 +375,11 @@ class SynthesisForm(forms.ModelForm):
def save(self, commit=True): 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: class Meta:
model = Synthesis model = WrittenReview
fields = ('file',) 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, default=date.today,
) )
syntheses_first_phase_limit = models.DateTimeField( reviews_first_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the syntheses for the first phase"), verbose_name=_("limit date to upload the written reviews for the first phase"),
default=timezone.now, default=timezone.now,
) )
@ -338,8 +338,8 @@ class Tournament(models.Model):
default=False, default=False,
) )
syntheses_second_phase_limit = models.DateTimeField( reviews_second_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the syntheses for the second phase"), verbose_name=_("limit date to upload the written reviews for the second phase"),
default=timezone.now, default=timezone.now,
) )
@ -353,8 +353,8 @@ class Tournament(models.Model):
default=False, default=False,
) )
syntheses_third_phase_limit = models.DateTimeField( reviews_third_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the syntheses for the third phase"), verbose_name=_("limit date to upload the written reviews for the third phase"),
default=timezone.now, default=timezone.now,
) )
@ -442,10 +442,10 @@ class Tournament(models.Model):
return Solution.objects.filter(participation__tournament=self) return Solution.objects.filter(participation__tournament=self)
@property @property
def syntheses(self): def written_reviews(self):
if self.final: if self.final:
return Synthesis.objects.filter(final_solution=True) return WrittenReview.objects.filter(final_solution=True)
return Synthesis.objects.filter(participation__tournament=self) return WrittenReview.objects.filter(participation__tournament=self)
@property @property
def best_format(self): def best_format(self):
@ -911,7 +911,7 @@ class Participation(models.Model):
'priority': 1, 'priority': 1,
'content': content, '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) 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) 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) 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 " opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram, 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 " reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
@ -948,7 +948,7 @@ class Participation(models.Model):
if observer_passage: if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the " observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text, observer_content = format_lazy(observer_text,
@ -959,24 +959,24 @@ class Participation(models.Model):
observer_content = "" observer_content = ""
if settings.TFJM_APP == "TFJM": if settings.TFJM_APP == "TFJM":
syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse." reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
else: else:
syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review." reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"]) 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 \ content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content + reviews_templates_content
informations.append({ informations.append({
'title': _("First round"), 'title': _("First round"),
'type': "info", 'type': "info",
'priority': 1, 'priority': 1,
'content': content, '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) 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) 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) 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 " opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram, 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 " reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
@ -1011,7 +1011,7 @@ class Participation(models.Model):
if observer_passage: if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the " observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text, observer_content = format_lazy(observer_text,
@ -1022,17 +1022,17 @@ class Participation(models.Model):
observer_content = "" observer_content = ""
if settings.TFJM_APP == "TFJM": if settings.TFJM_APP == "TFJM":
syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse." reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
else: else:
syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review." reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"]) 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 \ content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content + reviews_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
'type': "info", 'type': "info",
@ -1040,7 +1040,7 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
elif settings.TFJM_APP == "ETEAM" \ 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) 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) 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) 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 " opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram, 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 " reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
@ -1075,7 +1075,7 @@ class Participation(models.Model):
if observer_passage: if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the " observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<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 solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text, observer_content = format_lazy(observer_text,
@ -1086,17 +1086,17 @@ class Participation(models.Model):
observer_content = "" observer_content = ""
if settings.TFJM_APP == "TFJM": if settings.TFJM_APP == "TFJM":
syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse." reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
else: else:
syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review." reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"]) 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 \ content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content + reviews_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
'type': "info", 'type': "info",
@ -1256,7 +1256,7 @@ class Pool(models.Model):
f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""] f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""]
+ ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else []) + ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else [])
for passage in passages), start=[str(_("Role")), ""]), 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"{_('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)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"]
+ ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else []) + ([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 "") + ("_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): 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): class Solution(models.Model):
@ -1951,7 +1955,7 @@ class Solution(models.Model):
ordering = ('participation__team__trigram', 'final_solution', 'problem',) ordering = ('participation__team__trigram', 'final_solution', 'problem',)
class Synthesis(models.Model): class WrittenReview(models.Model):
participation = models.ForeignKey( participation = models.ForeignKey(
Participation, Participation,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -1961,7 +1965,7 @@ class Synthesis(models.Model):
passage = models.ForeignKey( passage = models.ForeignKey(
Passage, Passage,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="syntheses", related_name="written_reviews",
verbose_name=_("passage"), verbose_name=_("passage"),
) )
@ -1980,7 +1984,7 @@ class Synthesis(models.Model):
) )
def __str__(self): 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, team=self.participation.team.trigram,
type=self.get_type_display(), type=self.get_type_display(),
problem=self.passage.solution_number, problem=self.passage.solution_number,
@ -1988,8 +1992,8 @@ class Synthesis(models.Model):
) )
class Meta: class Meta:
verbose_name = _("synthesis") verbose_name = _("written review")
verbose_name_plural = _("syntheses") verbose_name_plural = _("written reviews")
unique_together = (('participation', 'passage', 'type', ), ) unique_together = (('participation', 'passage', 'type', ), )
ordering = ('passage__pool__round', 'type',) ordering = ('passage__pool__round', 'type',)

View File

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

View File

@ -62,15 +62,15 @@
{% for passage in pool.passages.all %} {% for passage in pool.passages.all %}
<li class="list-group-item"> <li class="list-group-item">
{{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }} : {{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }} :
{% for synthesis in passage.syntheses.all %} {% for review in passage.written_reviews.all %}
<a href="{{ synthesis.file.url }}">{{ synthesis.participation.team.trigram }} ({{ synthesis.get_type_display }})</a>{% if not forloop.last %}, {% endif %} <a href="{{ review.file.url }}">{{ review.participation.team.trigram }} ({{ review.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
{% empty %} {% empty %}
{% trans "No synthesis was uploaded yet." %} {% trans "No review was uploaded yet." %}
{% endfor %} {% endfor %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </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" %} <i class="fas fa-download"></i> {% trans "Download all" %}
</a> </a>
</dd> </dd>

View File

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

View File

@ -8,11 +8,11 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, GSheetNotific
PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \ PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \ PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \
ScaleNotationSheetTemplateView, SelectTeamFinalView, \ ScaleNotationSheetTemplateView, SelectTeamFinalView, \
SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \ SolutionsDownloadView, SolutionUploadView, \
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \ TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \ TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
TournamentHarmonizeNoteView, TournamentHarmonizeView, TournamentListView, TournamentPaymentsView, \ TournamentHarmonizeNoteView, TournamentHarmonizeView, TournamentListView, TournamentPaymentsView, \
TournamentPublishNotesView, TournamentUpdateView TournamentPublishNotesView, TournamentUpdateView, WrittenReviewUploadView
app_name = "participation" app_name = "participation"
@ -42,8 +42,8 @@ urlpatterns = [
name="tournament_authorizations"), name="tournament_authorizations"),
path("tournament/<int:tournament_id>/solutions/", SolutionsDownloadView.as_view(), path("tournament/<int:tournament_id>/solutions/", SolutionsDownloadView.as_view(),
name="tournament_solutions"), name="tournament_solutions"),
path("tournament/<int:tournament_id>/syntheses/", SolutionsDownloadView.as_view(), path("tournament/<int:tournament_id>/written_reviews/", SolutionsDownloadView.as_view(),
name="tournament_syntheses"), name="tournament_written_reviews"),
path("tournament/<int:tournament_id>/notation/sheets/", NotationSheetsArchiveView.as_view(), path("tournament/<int:tournament_id>/notation/sheets/", NotationSheetsArchiveView.as_view(),
name="tournament_notation_sheets"), name="tournament_notation_sheets"),
path("tournament/<int:pk>/notation/notifications/", GSheetNotificationsView.as_view(), 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>/", PoolDetailView.as_view(), name="pool_detail"),
path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"), 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>/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/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:pk>/notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"),
path("pools/<int:pool_id>/notation/sheets/", NotationSheetsArchiveView.as_view(), name="pool_notation_sheets"), path("pools/<int: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/<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>/", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"), 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"), 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 tfjm.views import AdminMixin, VolunteerMixin
from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \ from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \
PoolForm, RequestValidationForm, SolutionForm, SynthesisForm, TeamForm, TournamentForm, \ PoolForm, RequestValidationForm, SolutionForm, TeamForm, TournamentForm, UploadNotesForm, \
UploadNotesForm, ValidateParticipationForm ValidateParticipationForm, WrittenReviewForm
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview
from .tables import NoteTable, ParticipationTable, PassageTable, PoolTable, TeamTable, TournamentTable from .tables import NoteTable, ParticipationTable, PassageTable, PoolTable, TeamTable, TournamentTable
@ -977,7 +977,7 @@ class PoolUpdateView(VolunteerMixin, UpdateView):
class SolutionsDownloadView(VolunteerMixin, View): 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): def dispatch(self, request, *args, **kwargs):
@ -1018,11 +1018,12 @@ class SolutionsDownloadView(VolunteerMixin, View):
if 'team_id' in kwargs: if 'team_id' in kwargs:
team = Team.objects.get(pk=kwargs["team_id"]) team = Team.objects.get(pk=kwargs["team_id"])
solutions = Solution.objects.filter(participation=team.participation).all() solutions = Solution.objects.filter(participation=team.participation).all()
syntheses = Synthesis.objects.filter(participation=team.participation).all() written_reviews = WrittenReview.objects.filter(participation=team.participation).all()
filename = _("Solutions of team {trigram}.zip") if is_solution else _("Syntheses of team {trigram}.zip") filename = _("Solutions of team {trigram}.zip") if is_solution \
else _("Written reviews of team {trigram}.zip")
filename = filename.format(trigram=team.trigram) filename = filename.format(trigram=team.trigram)
def prefix(s: Solution | Synthesis) -> str: def prefix(s: Solution | WrittenReview) -> str:
return "" return ""
elif 'tournament_id' in kwargs: elif 'tournament_id' in kwargs:
tournament = Tournament.objects.get(pk=kwargs["tournament_id"]) tournament = Tournament.objects.get(pk=kwargs["tournament_id"])
@ -1035,11 +1036,12 @@ class SolutionsDownloadView(VolunteerMixin, View):
for sol in pool.solutions: for sol in pool.solutions:
sol.pool = pool sol.pool = pool
solutions.append(sol) solutions.append(sol)
syntheses = Synthesis.objects.filter(passage__pool__tournament=tournament).all() written_reviews = WrittenReview.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution else _("Syntheses of {tournament}.zip") filename = _("Solutions of {tournament}.zip") if is_solution \
else _("Written reviews of {tournament}.zip")
filename = filename.format(tournament=tournament.name) 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 pool = s.pool if is_solution else s.passage.pool
p = f"Poule {pool.short_name}/" p = f"Poule {pool.short_name}/"
if not is_solution: if not is_solution:
@ -1050,27 +1052,28 @@ class SolutionsDownloadView(VolunteerMixin, View):
solutions = Solution.objects.filter(participation__tournament=tournament).all() solutions = Solution.objects.filter(participation__tournament=tournament).all()
else: else:
solutions = Solution.objects.filter(final_solution=True).all() solutions = Solution.objects.filter(final_solution=True).all()
syntheses = Synthesis.objects.filter(passage__pool__tournament=tournament).all() written_reviews = WrittenReview.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution else _("Syntheses of {tournament}.zip") filename = _("Solutions of {tournament}.zip") if is_solution \
else _("Written reviews of {tournament}.zip")
filename = filename.format(tournament=tournament.name) 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}/" return f"{s.participation.team.trigram}/" if sort_by == "team" else f"Problème {s.problem}/"
else: else:
pool = Pool.objects.get(pk=kwargs["pool_id"]) pool = Pool.objects.get(pk=kwargs["pool_id"])
solutions = pool.solutions 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") \ 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, filename = filename.format(pool=pool.short_name,
tournament=pool.tournament.name) tournament=pool.tournament.name)
def prefix(s: Solution | Synthesis) -> str: def prefix(s: Solution | WrittenReview) -> str:
return "" return ""
output = BytesIO() output = BytesIO()
zf = ZipFile(output, "w") 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): if s.file.storage.exists(s.file.path):
zf.write("media/" + s.file.name, prefix(s) + f"{s}.pdf") zf.write("media/" + s.file.name, prefix(s) + f"{s}.pdf")
@ -2028,9 +2031,9 @@ class PassageUpdateView(VolunteerMixin, UpdateView):
return self.handle_no_permission() return self.handle_no_permission()
class SynthesisUploadView(LoginRequiredMixin, FormView): class WrittenReviewUploadView(LoginRequiredMixin, FormView):
template_name = "participation/upload_synthesis.html" template_name = "participation/upload_written_review.html"
form_class = SynthesisForm form_class = WrittenReviewForm
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.registration.participates: 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 = form.instance
form_syn.type = 1 if self.participation == self.passage.opponent \ form_syn.type = 1 if self.participation == self.passage.opponent \
else 2 if self.participation == self.passage.reviewer else 3 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, passage=self.passage,
type=form_syn.type).all() type=form_syn.type).all()
deadline = self.passage.pool.tournament.syntheses_first_phase_limit if self.passage.pool.round == 1 \ deadline = self.passage.pool.tournament.reviews_first_phase_limit if self.passage.pool.round == 1 \
else self.passage.pool.tournament.syntheses_second_phase_limit else self.passage.pool.tournament.reviews_second_phase_limit
if syn_qs.exists() and timezone.now() > deadline: 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) return self.form_invalid(form)
# Drop previous solution if existing # 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.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from magic import Magic 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.tokens import email_validation_token
from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin 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) 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): def get(self, request, *args, **kwargs):
filename = kwargs["filename"] filename = kwargs["filename"]
path = f"media/syntheses/{filename}" path = f"media/reviews/{filename}"
if not os.path.exists(path): if not os.path.exists(path):
raise Http404 raise Http404
synthesis = Synthesis.objects.get(file__endswith=filename) reviews = WrittenReview.objects.get(file__endswith=filename)
user = request.user user = request.user
if not (user.registration.is_admin or user.registration.is_volunteer if not (user.registration.is_admin or user.registration.is_volunteer
and (user.registration in synthesis.passage.pool.juries.all() and (user.registration in reviews.passage.pool.juries.all()
or user.registration in synthesis.passage.pool.tournament.organizers.all() or user.registration in reviews.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=synthesis.passage.pool.tournament).exists()) or user.registration.pools_presided.filter(tournament=reviews.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == synthesis.participation.team): or user.registration.participates and user.registration.team == reviews.participation.team):
raise PermissionDenied raise PermissionDenied
# Guess mime type of the file # Guess mime type of the file
mime = Magic(mime=True) mime = Magic(mime=True)
mime_type = mime.from_file(path) mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg") ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name # 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) 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 django.views.generic import TemplateView
from participation.views import MotivationLetterView from participation.views import MotivationLetterView
from registration.views import HealthSheetView, ParentalAuthorizationView, PhotoAuthorizationView, \ from registration.views import HealthSheetView, ParentalAuthorizationView, PhotoAuthorizationView, \
ReceiptView, SolutionView, SynthesisView, VaccineSheetView ReceiptView, SolutionView, VaccineSheetView, WrittenReviewView
from .views import AdminSearchView from .views import AdminSearchView
@ -61,8 +61,8 @@ urlpatterns = [
path('media/solutions/<str:filename>/', SolutionView.as_view(), path('media/solutions/<str:filename>/', SolutionView.as_view(),
name='solution'), name='solution'),
path('media/syntheses/<str:filename>/', SynthesisView.as_view(), path('media/reviews/<str:filename>/', WrittenReviewView.as_view(),
name='synthesis'), name='reviews'),
] ]
if settings.DEBUG: if settings.DEBUG: