Update GSheets for ETEAM
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
44302a9ff4
commit
d13ae89267
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: TFJM\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-07-05 11:45+0200\n"
|
||||
"POT-Creation-Date: 2024-07-05 16:44+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -78,8 +78,8 @@ msgstr "Type de permission nécessaire pour écrire un message dans un canal."
|
|||
|
||||
#: chat/models.py:62 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88
|
||||
#: draw/models.py:27 participation/admin.py:79 participation/admin.py:144
|
||||
#: participation/admin.py:176 participation/models.py:727
|
||||
#: participation/models.py:751 participation/models.py:1060
|
||||
#: participation/admin.py:176 participation/models.py:781
|
||||
#: participation/models.py:805 participation/models.py:1114
|
||||
#: registration/models.py:763
|
||||
#: registration/templates/registration/payment_form.html:53
|
||||
msgid "tournament"
|
||||
|
@ -95,7 +95,7 @@ msgstr ""
|
|||
|
||||
#: chat/models.py:73 draw/models.py:446 draw/models.py:473
|
||||
#: participation/admin.py:140 participation/admin.py:160
|
||||
#: participation/models.py:1563 participation/models.py:1572
|
||||
#: participation/models.py:1642 participation/models.py:1651
|
||||
#: participation/tables.py:84
|
||||
msgid "pool"
|
||||
msgstr "poule"
|
||||
|
@ -109,7 +109,7 @@ msgstr ""
|
|||
|
||||
#: chat/models.py:84 draw/templates/draw/tournament_content.html:277
|
||||
#: participation/admin.py:172 participation/models.py:261
|
||||
#: participation/models.py:742
|
||||
#: participation/models.py:796
|
||||
#: participation/templates/participation/tournament_harmonize.html:15
|
||||
#: registration/models.py:158 registration/models.py:754
|
||||
#: registration/tables.py:39
|
||||
|
@ -265,11 +265,11 @@ msgid "teams"
|
|||
msgstr "équipes"
|
||||
|
||||
#: draw/admin.py:92 draw/models.py:245 draw/models.py:465
|
||||
#: participation/models.py:1064
|
||||
#: participation/models.py:1118
|
||||
msgid "round"
|
||||
msgstr "tour"
|
||||
|
||||
#: draw/apps.py:10 draw/consumers.py:1037 tfjm/templates/navbar.html:68
|
||||
#: draw/apps.py:10 draw/consumers.py:1038 tfjm/templates/navbar.html:68
|
||||
msgid "Draw"
|
||||
msgstr "Tirage au sort"
|
||||
|
||||
|
@ -309,9 +309,9 @@ msgstr "Le tirage au sort du tournoi {tournament} a commencé !"
|
|||
msgid "The draw for the tournament {tournament} will start."
|
||||
msgstr "Le tirage au sort du tournoi {tournament} va commencer."
|
||||
|
||||
#: draw/consumers.py:256 draw/consumers.py:282 draw/consumers.py:691
|
||||
#: draw/consumers.py:909 draw/consumers.py:999 draw/consumers.py:1021
|
||||
#: draw/consumers.py:1112 draw/templates/draw/tournament_content.html:5
|
||||
#: draw/consumers.py:256 draw/consumers.py:282 draw/consumers.py:692
|
||||
#: draw/consumers.py:910 draw/consumers.py:1000 draw/consumers.py:1022
|
||||
#: draw/consumers.py:1113 draw/templates/draw/tournament_content.html:5
|
||||
msgid "The draw has not started yet."
|
||||
msgstr "Le tirage au sort n'a pas encore commencé."
|
||||
|
||||
|
@ -320,8 +320,8 @@ msgstr "Le tirage au sort n'a pas encore commencé."
|
|||
msgid "The draw for the tournament {tournament} is aborted."
|
||||
msgstr "Le tirage au sort du tournoi {tournament} est annulé."
|
||||
|
||||
#: draw/consumers.py:309 draw/consumers.py:330 draw/consumers.py:625
|
||||
#: draw/consumers.py:696 draw/consumers.py:914
|
||||
#: draw/consumers.py:309 draw/consumers.py:330 draw/consumers.py:626
|
||||
#: draw/consumers.py:697 draw/consumers.py:915
|
||||
msgid "This is not the time for this."
|
||||
msgstr "Ce n'est pas le moment pour cela."
|
||||
|
||||
|
@ -362,21 +362,21 @@ msgstr ""
|
|||
"de dés, par ordre croissant. Pour le deuxième tour, les ordres de passage "
|
||||
"sont déterminés à partir des ordres de passage du premier tour."
|
||||
|
||||
#: draw/consumers.py:614 draw/consumers.py:754 draw/consumers.py:831
|
||||
#: draw/consumers.py:865 draw/consumers.py:990 draw/consumers.py:1088
|
||||
#: draw/consumers.py:615 draw/consumers.py:755 draw/consumers.py:832
|
||||
#: draw/consumers.py:866 draw/consumers.py:991 draw/consumers.py:1089
|
||||
msgid "Your turn!"
|
||||
msgstr "À votre tour !"
|
||||
|
||||
#: draw/consumers.py:615 draw/consumers.py:755 draw/consumers.py:991
|
||||
#: draw/consumers.py:1089
|
||||
#: draw/consumers.py:616 draw/consumers.py:756 draw/consumers.py:992
|
||||
#: draw/consumers.py:1090
|
||||
msgid "It's your turn to draw a problem!"
|
||||
msgstr "C'est à vous de tirer un problème !"
|
||||
|
||||
#: draw/consumers.py:635 draw/consumers.py:706 draw/consumers.py:924
|
||||
#: draw/consumers.py:636 draw/consumers.py:707 draw/consumers.py:925
|
||||
msgid "This is not your turn."
|
||||
msgstr "Ce n'est pas votre tour."
|
||||
|
||||
#: draw/consumers.py:713
|
||||
#: draw/consumers.py:714
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"The team <strong>{trigram}</strong> accepted the problem <string>{problem}</"
|
||||
|
@ -385,33 +385,33 @@ msgstr ""
|
|||
"L'équipe <strong>{trigram}</strong> a accepté le problème <strong>{problem}</"
|
||||
"strong> : {problem_name}. "
|
||||
|
||||
#: draw/consumers.py:717
|
||||
#: draw/consumers.py:718
|
||||
msgid "One team more can accept this problem."
|
||||
msgstr "Une équipe de plus peut accepter ce problème."
|
||||
|
||||
#: draw/consumers.py:719
|
||||
#: draw/consumers.py:720
|
||||
msgid "No team can accept this problem anymore."
|
||||
msgstr "Aucune autre équipe ne peut accepter ce problème."
|
||||
|
||||
#: draw/consumers.py:813
|
||||
#: draw/consumers.py:814
|
||||
#, python-brace-format
|
||||
msgid "The draw of the pool {pool} is ended. The summary is below."
|
||||
msgstr "Le tirage de la poule {pool} est terminé. Le résumé est ci-dessous."
|
||||
|
||||
#: draw/consumers.py:832 draw/consumers.py:866
|
||||
#: draw/consumers.py:833 draw/consumers.py:867
|
||||
msgid "It's your turn to launch the dice!"
|
||||
msgstr "C'est à vous de lancer le dé !"
|
||||
|
||||
#: draw/consumers.py:852
|
||||
#: draw/consumers.py:853
|
||||
#, python-brace-format
|
||||
msgid "The draw of the round {round} is ended."
|
||||
msgstr "Le tirage au sort du tour {round} est annulé."
|
||||
|
||||
#: draw/consumers.py:895
|
||||
#: draw/consumers.py:896
|
||||
msgid "The draw of the first round is ended."
|
||||
msgstr "Le tirage au sort du premier tour est terminé."
|
||||
|
||||
#: draw/consumers.py:938
|
||||
#: draw/consumers.py:939
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"The team <strong>{trigram}</strong> refused the problem <strong>{problem}</"
|
||||
|
@ -420,26 +420,26 @@ msgstr ""
|
|||
"L'équipe <strong>{trigram}</strong> a refusé le problème <strong>{problem}</"
|
||||
"strong> : {problem_name}."
|
||||
|
||||
#: draw/consumers.py:942
|
||||
#: draw/consumers.py:943
|
||||
#, python-brace-format
|
||||
msgid "It remains {remaining} refusals without penalty."
|
||||
msgstr "Il reste {remaining} refus sans pénalité."
|
||||
|
||||
#: draw/consumers.py:945
|
||||
#: draw/consumers.py:946
|
||||
msgid "This problem was already refused by this team."
|
||||
msgstr "Ce problème a déjà été refusé par cette équipe."
|
||||
|
||||
#: draw/consumers.py:947
|
||||
#: draw/consumers.py:948
|
||||
msgid "It adds a 25% penalty on the coefficient of the oral defense."
|
||||
msgstr ""
|
||||
"Cela ajoute une pénalité de 25 % sur le coefficient de l'oral de la "
|
||||
"défense."
|
||||
|
||||
#: draw/consumers.py:1024
|
||||
#: draw/consumers.py:1025
|
||||
msgid "This is only available for the final tournament."
|
||||
msgstr "Cela n'est possible que pour la finale."
|
||||
|
||||
#: draw/consumers.py:1028
|
||||
#: draw/consumers.py:1029
|
||||
msgid ""
|
||||
"The draw of the round 2 is starting. The passage order is determined from "
|
||||
"the ranking of the first round, in order to mix the teams between the two "
|
||||
|
@ -449,7 +449,7 @@ msgstr ""
|
|||
"partir du classement du premier tour, afin de mélanger les équipes entre les "
|
||||
"deux jours."
|
||||
|
||||
#: draw/consumers.py:1038
|
||||
#: draw/consumers.py:1039
|
||||
msgid "The draw of the second round is starting!"
|
||||
msgstr "Le tirage au sort du deuxième tour commence !"
|
||||
|
||||
|
@ -626,7 +626,7 @@ msgid "The current pool where teams select their problems."
|
|||
msgstr "La poule en cours, où les équipes choisissent leurs problèmes"
|
||||
|
||||
#: draw/models.py:239
|
||||
#, python-brace-format
|
||||
#, python-brace-format
|
||||
msgid "The number of the round must be between 1 and {nb}."
|
||||
msgstr "Le numéro du tour doit être entre 1 et {nb}."
|
||||
|
||||
|
@ -634,7 +634,7 @@ msgstr "Le numéro du tour doit être entre 1 et {nb}."
|
|||
msgid "rounds"
|
||||
msgstr "tours"
|
||||
|
||||
#: draw/models.py:268 participation/models.py:1072
|
||||
#: draw/models.py:268 participation/models.py:1126
|
||||
msgid "letter"
|
||||
msgstr "lettre"
|
||||
|
||||
|
@ -672,12 +672,12 @@ msgstr "L'instance complète de la poule."
|
|||
msgid "Pool {letter}{number}"
|
||||
msgstr "Poule {letter}{number}"
|
||||
|
||||
#: draw/models.py:447 participation/models.py:1564
|
||||
#: draw/models.py:447 participation/models.py:1643
|
||||
msgid "pools"
|
||||
msgstr "poules"
|
||||
|
||||
#: draw/models.py:459 participation/models.py:1050 participation/models.py:1754
|
||||
#: participation/models.py:1784 participation/models.py:1826
|
||||
#: draw/models.py:459 participation/models.py:1104 participation/models.py:1862
|
||||
#: participation/models.py:1892 participation/models.py:1934
|
||||
msgid "participation"
|
||||
msgstr "participation"
|
||||
|
||||
|
@ -701,8 +701,9 @@ msgid ""
|
|||
msgstr ""
|
||||
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
|
||||
|
||||
#: draw/models.py:496 draw/models.py:519 participation/models.py:1586
|
||||
#: participation/models.py:1791
|
||||
#: draw/models.py:496 draw/models.py:519 participation/models.py:1234
|
||||
#: participation/models.py:1665 participation/models.py:1899
|
||||
#: participation/views.py:1487 participation/views.py:1752
|
||||
#, python-brace-format
|
||||
msgid "Problem #{problem}"
|
||||
msgstr "Problème n°{problem}"
|
||||
|
@ -913,31 +914,31 @@ msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}"
|
|||
msgid "valid"
|
||||
msgstr "valide"
|
||||
|
||||
#: participation/admin.py:87 participation/models.py:763
|
||||
#: participation/admin.py:87 participation/models.py:817
|
||||
msgid "selected for final"
|
||||
msgstr "sélectionnée pour la finale"
|
||||
|
||||
#: participation/admin.py:124 participation/admin.py:188
|
||||
#: participation/models.py:1593 participation/tables.py:114
|
||||
#: participation/models.py:1672 participation/tables.py:114
|
||||
msgid "defender"
|
||||
msgstr "défenseur⋅se"
|
||||
|
||||
#: participation/admin.py:128 participation/models.py:1600
|
||||
#: participation/models.py:1838
|
||||
#: participation/admin.py:128 participation/models.py:1679
|
||||
#: participation/models.py:1946
|
||||
msgid "opponent"
|
||||
msgstr "opposant⋅e"
|
||||
|
||||
#: participation/admin.py:132 participation/models.py:1607
|
||||
#: participation/models.py:1839
|
||||
#: participation/admin.py:132 participation/models.py:1686
|
||||
#: participation/models.py:1947
|
||||
msgid "reviewer"
|
||||
msgstr "rapporteur⋅rice"
|
||||
|
||||
#: participation/admin.py:136 participation/models.py:1614
|
||||
#: participation/models.py:1840
|
||||
#: participation/admin.py:136 participation/models.py:1693
|
||||
#: participation/models.py:1948
|
||||
msgid "observer"
|
||||
msgstr "observateur⋅rice"
|
||||
|
||||
#: participation/admin.py:192 participation/models.py:1789
|
||||
#: participation/admin.py:192 participation/models.py:1897
|
||||
msgid "problem"
|
||||
msgstr "numéro de problème"
|
||||
|
||||
|
@ -961,7 +962,7 @@ msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
|
|||
msgid "The team is already validated or the validation is pending."
|
||||
msgstr "La validation de l'équipe est déjà faite ou en cours."
|
||||
|
||||
#: participation/forms.py:94 participation/forms.py:366
|
||||
#: participation/forms.py:94 participation/forms.py:367
|
||||
#: registration/forms.py:126 registration/forms.py:148
|
||||
#: registration/forms.py:170 registration/forms.py:192
|
||||
#: registration/forms.py:214 registration/forms.py:236
|
||||
|
@ -989,7 +990,7 @@ msgstr "Message à adresser à l'équipe :"
|
|||
msgid "The uploaded file size must be under 5 Mo."
|
||||
msgstr "Le fichier envoyé doit peser moins de 5 Mo."
|
||||
|
||||
#: participation/forms.py:178 participation/forms.py:368
|
||||
#: participation/forms.py:178 participation/forms.py:369
|
||||
msgid "The uploaded file must be a PDF file."
|
||||
msgstr "Le fichier envoyé doit être au format PDF."
|
||||
|
||||
|
@ -1017,24 +1018,24 @@ msgstr ""
|
|||
"Ce fichier contient des éléments non-UTF-8 et non-ISO-8859-1. Merci "
|
||||
"d'envoyer votre tableur au format CSV."
|
||||
|
||||
#: participation/forms.py:327
|
||||
#: participation/forms.py:328
|
||||
msgid "The following note is higher of the maximum expected value:"
|
||||
msgstr "La note suivante est supérieure au maximum attendu :"
|
||||
|
||||
#: participation/forms.py:333
|
||||
#: participation/forms.py:334
|
||||
msgid "The following user was not found:"
|
||||
msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :"
|
||||
|
||||
#: participation/forms.py:349
|
||||
#: participation/forms.py:350
|
||||
msgid "The defender, the opponent and the reviewer must be different."
|
||||
msgstr ""
|
||||
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
|
||||
|
||||
#: participation/forms.py:353
|
||||
#: participation/forms.py:354
|
||||
msgid "This defender did not work on this problem."
|
||||
msgstr "Ce⋅tte défenseur⋅se ne travaille pas sur ce problème."
|
||||
|
||||
#: participation/forms.py:372
|
||||
#: participation/forms.py:373
|
||||
msgid "The PDF file must not have more than 2 pages."
|
||||
msgstr "Le fichier PDF ne doit pas avoir plus de 2 pages."
|
||||
|
||||
|
@ -1287,36 +1288,100 @@ msgstr "finale"
|
|||
msgid "Google Sheet ID"
|
||||
msgstr "ID de la feuille Google Sheets"
|
||||
|
||||
#: participation/models.py:728 registration/admin.py:125
|
||||
#: participation/models.py:473 participation/models.py:474
|
||||
#: participation/models.py:476 participation/models.py:714
|
||||
msgid "Final ranking"
|
||||
msgstr "Classement final"
|
||||
|
||||
#: participation/models.py:481 participation/models.py:551
|
||||
#: participation/models.py:1309 participation/views.py:1726
|
||||
msgid "Team"
|
||||
msgstr "Équipe"
|
||||
|
||||
#: participation/models.py:481
|
||||
msgid "Scores day 1"
|
||||
msgstr "Scores jour 1"
|
||||
|
||||
#: participation/models.py:481
|
||||
msgid "Tweaks day 1"
|
||||
msgstr "Ajustements 1"
|
||||
|
||||
#: participation/models.py:481
|
||||
msgid "Scores day 2"
|
||||
msgstr "Scores jour 2"
|
||||
|
||||
#: participation/models.py:481
|
||||
msgid "Tweaks day 2"
|
||||
msgstr "Ajustements 2"
|
||||
|
||||
#: participation/models.py:482
|
||||
msgid "Total D1 + D2"
|
||||
msgstr "Total J1 + J2"
|
||||
|
||||
#: participation/models.py:482
|
||||
msgid "Scores day 3"
|
||||
msgstr "Scores jour 3"
|
||||
|
||||
#: participation/models.py:482
|
||||
msgid "Tweaks day 3"
|
||||
msgstr "Ajustements 3"
|
||||
|
||||
#: participation/models.py:484 participation/models.py:1309
|
||||
#: participation/views.py:1733
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: participation/models.py:484 participation/models.py:551
|
||||
#: participation/models.py:1309
|
||||
#: participation/templates/participation/tournament_harmonize.html:14
|
||||
#: participation/views.py:1736
|
||||
msgid "Rank"
|
||||
msgstr "Rang"
|
||||
|
||||
#: participation/models.py:551 participation/models.py:716
|
||||
msgid "Score"
|
||||
msgstr "Score"
|
||||
|
||||
#: participation/models.py:551
|
||||
msgid "Mention"
|
||||
msgstr "Mention"
|
||||
|
||||
#: participation/models.py:696 participation/models.py:1572
|
||||
msgid "Don't update the table structure for a better automated integration."
|
||||
msgstr ""
|
||||
"Ne pas mettre à jour la structure de la table pour une meilleure intégration "
|
||||
"automatisée."
|
||||
|
||||
#: participation/models.py:782 registration/admin.py:125
|
||||
msgid "tournaments"
|
||||
msgstr "tournois"
|
||||
|
||||
#: participation/models.py:757
|
||||
#: participation/models.py:811
|
||||
msgid "valid team"
|
||||
msgstr "équipe valide"
|
||||
|
||||
#: participation/models.py:758
|
||||
#: participation/models.py:812
|
||||
msgid "The participation got the validation of the organizers."
|
||||
msgstr "La participation a été validée par les organisateur⋅rices."
|
||||
|
||||
#: participation/models.py:764
|
||||
#: participation/models.py:818
|
||||
msgid "The team is selected for the final tournament."
|
||||
msgstr "L'équipe est sélectionnée pour la finale."
|
||||
|
||||
#: participation/models.py:768
|
||||
#: participation/models.py:822
|
||||
msgid "mention"
|
||||
msgstr "mention"
|
||||
|
||||
#: participation/models.py:775
|
||||
#: participation/models.py:829
|
||||
msgid "mention (final)"
|
||||
msgstr "Mention (pour la finale) :"
|
||||
|
||||
#: participation/models.py:785
|
||||
#: participation/models.py:839
|
||||
#, python-brace-format
|
||||
msgid "Participation of the team {name} ({trigram})"
|
||||
msgstr "Participation de l'équipe {name} ({trigram})"
|
||||
|
||||
#: participation/models.py:792
|
||||
#: participation/models.py:846
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>The team {trigram} has {nb_missing_payments} missing payments. Each "
|
||||
|
@ -1329,11 +1394,11 @@ msgstr ""
|
|||
"notification de bourse) pour participer au tournoi.</p><p>Les participant⋅es "
|
||||
"qui n'ont pas encore payé sont : {participants}.</p>"
|
||||
|
||||
#: participation/models.py:800
|
||||
#: participation/models.py:854
|
||||
msgid "Missing payments"
|
||||
msgstr "Paiements manquants"
|
||||
|
||||
#: participation/models.py:817
|
||||
#: participation/models.py:871
|
||||
msgid ""
|
||||
"<p>The solutions for the tournament of {tournament} are due on the {date:%Y-"
|
||||
"%m-%d %H:%M}.</p><p>You have currently sent <strong>{nb_solutions}</strong> "
|
||||
|
@ -1348,11 +1413,11 @@ msgstr ""
|
|||
"pouvez envoyer vos solutions sur <a href='{url}'>votre page de "
|
||||
"participation</a>.</p>"
|
||||
|
||||
#: participation/models.py:827 participation/models.py:841
|
||||
#: participation/models.py:881 participation/models.py:895
|
||||
msgid "Solutions due"
|
||||
msgstr "Rendu des solutions"
|
||||
|
||||
#: participation/models.py:833
|
||||
#: participation/models.py:887
|
||||
msgid ""
|
||||
"<p>The solutions for the tournament of {tournament} are due on the {date:%Y-"
|
||||
"%m-%d %H:%M}.</p><p>Remember that you can only fix minor changes to your "
|
||||
|
@ -1365,7 +1430,7 @@ msgstr ""
|
|||
"parties.</p><p>Vous pouvez envoyer vos solutions sur <a href='{url}'>votre "
|
||||
"page de participation</a>.</p>"
|
||||
|
||||
#: participation/models.py:847 registration/models.py:607
|
||||
#: participation/models.py:901 registration/models.py:607
|
||||
msgid ""
|
||||
"<p>The draw of the solutions for the tournament {tournament} is planned on "
|
||||
"the {date:%Y-%m-%d %H:%M}. You can join it on <a href='{url}'>this link</a>."
|
||||
|
@ -1375,11 +1440,11 @@ msgstr ""
|
|||
"{date:%d/%m/%Y %H:%M}. Vous pouvez y participer sur <a href='{url}'>ce lien</"
|
||||
"a>.</p>"
|
||||
|
||||
#: participation/models.py:853 registration/models.py:614
|
||||
#: participation/models.py:907 registration/models.py:614
|
||||
msgid "Draw of solutions"
|
||||
msgstr "Tirage au sort des solutions"
|
||||
|
||||
#: participation/models.py:865
|
||||
#: participation/models.py:919
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>The solutions draw is ended. You can check the result on <a "
|
||||
|
@ -1391,8 +1456,8 @@ msgstr ""
|
|||
"tour, vous défendrez <a href='{solution_url}'>votre solution du problème "
|
||||
"{problem}</a>.</p>"
|
||||
|
||||
#: participation/models.py:874 participation/models.py:932
|
||||
#: participation/models.py:991
|
||||
#: participation/models.py:928 participation/models.py:986
|
||||
#: participation/models.py:1045
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>You will oppose the solution of the team {opponent} on the <a "
|
||||
|
@ -1403,8 +1468,8 @@ msgstr ""
|
|||
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
|
||||
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
|
||||
|
||||
#: participation/models.py:883 participation/models.py:941
|
||||
#: participation/models.py:1000
|
||||
#: participation/models.py:937 participation/models.py:995
|
||||
#: participation/models.py:1054
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>You will report the solution of the team {reviewer} on the <a "
|
||||
|
@ -1415,8 +1480,8 @@ msgstr ""
|
|||
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
|
||||
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
|
||||
|
||||
#: participation/models.py:893 participation/models.py:951
|
||||
#: participation/models.py:1010
|
||||
#: participation/models.py:947 participation/models.py:1005
|
||||
#: participation/models.py:1064
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>You will observe the solution of the team {observer} on the <a "
|
||||
|
@ -1427,11 +1492,11 @@ msgstr ""
|
|||
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
|
||||
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
|
||||
|
||||
#: participation/models.py:913 registration/models.py:629
|
||||
#: participation/models.py:967 registration/models.py:629
|
||||
msgid "First round"
|
||||
msgstr "Premier tour"
|
||||
|
||||
#: participation/models.py:925
|
||||
#: participation/models.py:979
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>For the second round, you will defend <a href='{solution_url}'>your "
|
||||
|
@ -1440,12 +1505,12 @@ msgstr ""
|
|||
"<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
|
||||
"solution du problème {problem}</a>.</p>"
|
||||
|
||||
#: participation/models.py:971 participation/models.py:1030
|
||||
#: participation/models.py:1025 participation/models.py:1084
|
||||
#: registration/models.py:640
|
||||
msgid "Second round"
|
||||
msgstr "Second tour"
|
||||
|
||||
#: participation/models.py:984
|
||||
#: participation/models.py:1038
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>For the third round, you will defend <a href='{solution_url}'>your "
|
||||
|
@ -1454,7 +1519,7 @@ msgstr ""
|
|||
"<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre "
|
||||
"solution du problème {problem}</a>.</p>"
|
||||
|
||||
#: participation/models.py:1036
|
||||
#: participation/models.py:1090
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>The tournament {tournament} is ended. You can check the results on the <a "
|
||||
|
@ -1463,57 +1528,57 @@ msgstr ""
|
|||
"<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats "
|
||||
"sur la <a href='{url}'>page du tournoi</a>.</p>"
|
||||
|
||||
#: participation/models.py:1041
|
||||
#: participation/models.py:1095
|
||||
msgid "Tournament ended"
|
||||
msgstr "Tournoi terminé"
|
||||
|
||||
#: participation/models.py:1051 participation/models.py:1094
|
||||
#: participation/models.py:1105 participation/models.py:1148
|
||||
msgid "participations"
|
||||
msgstr "participations"
|
||||
|
||||
#: participation/models.py:1066 participation/models.py:1067
|
||||
#: participation/models.py:1068
|
||||
#: participation/models.py:1120 participation/models.py:1121
|
||||
#: participation/models.py:1122
|
||||
#, python-brace-format
|
||||
msgid "Round {round}"
|
||||
msgstr "Tour {round}"
|
||||
|
||||
#: participation/models.py:1082
|
||||
#: participation/models.py:1136
|
||||
msgid "room"
|
||||
msgstr "salle"
|
||||
|
||||
#: participation/models.py:1084
|
||||
#: participation/models.py:1138
|
||||
msgid "Room 1"
|
||||
msgstr "Salle 1"
|
||||
|
||||
#: participation/models.py:1085
|
||||
#: participation/models.py:1139
|
||||
msgid "Room 2"
|
||||
msgstr "Salle 2"
|
||||
|
||||
#: participation/models.py:1088
|
||||
#: participation/models.py:1142
|
||||
msgid "For 5-teams pools only"
|
||||
msgstr "Pour les poules de 5 équipe uniquement"
|
||||
|
||||
#: participation/models.py:1100
|
||||
#: participation/models.py:1154
|
||||
msgid "juries"
|
||||
msgstr "jurys"
|
||||
|
||||
#: participation/models.py:1109
|
||||
#: participation/models.py:1163
|
||||
msgid "president of the jury"
|
||||
msgstr "président⋅e du jury"
|
||||
|
||||
#: participation/models.py:1116
|
||||
#: participation/models.py:1170
|
||||
msgid "BigBlueButton URL"
|
||||
msgstr "Lien BigBlueButton"
|
||||
|
||||
#: participation/models.py:1117
|
||||
#: participation/models.py:1171
|
||||
msgid "The link of the BBB visio for this pool."
|
||||
msgstr "Le lien du salon BBB pour cette poule."
|
||||
|
||||
#: participation/models.py:1122
|
||||
#: participation/models.py:1176
|
||||
msgid "results available"
|
||||
msgstr "résultats disponibles"
|
||||
|
||||
#: participation/models.py:1123
|
||||
#: participation/models.py:1177
|
||||
msgid ""
|
||||
"Check this case when results become accessible to teams. They stay "
|
||||
"accessible to you. Only averages are given."
|
||||
|
@ -1522,33 +1587,61 @@ msgstr ""
|
|||
"Ils restent toujours accessibles pour vous. Seules les moyennes sont "
|
||||
"communiquées."
|
||||
|
||||
#: participation/models.py:1155
|
||||
#: participation/models.py:1209
|
||||
msgid "The president of the jury must be part of the jury."
|
||||
msgstr "Læ président⋅e du jury doit faire partie du jury."
|
||||
|
||||
#: participation/models.py:1544
|
||||
#: participation/models.py:1235 participation/models.py:1309
|
||||
#: participation/views.py:1481 participation/views.py:1730
|
||||
msgid "Problem"
|
||||
msgstr "Problème"
|
||||
|
||||
#: participation/models.py:1245 participation/views.py:1530
|
||||
#: participation/views.py:1531
|
||||
msgid "Juree"
|
||||
msgstr "Juré⋅e"
|
||||
|
||||
#: participation/models.py:1268 participation/models.py:1588
|
||||
#: participation/models.py:1610 participation/views.py:1600
|
||||
msgid "Average"
|
||||
msgstr "Moyenne"
|
||||
|
||||
#: participation/models.py:1274 participation/views.py:1619
|
||||
msgid "Coefficient"
|
||||
msgstr "Coefficien"
|
||||
|
||||
#: participation/models.py:1275 participation/views.py:1662
|
||||
msgid "Subtotal"
|
||||
msgstr "Sous-total"
|
||||
|
||||
#: participation/models.py:1535
|
||||
#, python-brace-format
|
||||
msgid "Input must be a valid integer between {min_note} and {max_note}."
|
||||
msgstr "L'entrée doit être un entier valide entre {min_note} et {max_note}."
|
||||
|
||||
#: participation/models.py:1623
|
||||
#, python-brace-format
|
||||
msgid "The jury {jury} is not part of the jury for this pool."
|
||||
msgstr "{jury} ne fait pas partie du jury pour cette poule."
|
||||
|
||||
#: participation/models.py:1557
|
||||
#: participation/models.py:1636
|
||||
#, python-brace-format
|
||||
msgid "Pool {code} for tournament {tournament} with teams {teams}"
|
||||
msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}"
|
||||
|
||||
#: participation/models.py:1577
|
||||
#: participation/models.py:1656
|
||||
msgid "position"
|
||||
msgstr "position"
|
||||
|
||||
#: participation/models.py:1584
|
||||
#: participation/models.py:1663
|
||||
msgid "defended solution"
|
||||
msgstr "solution défendue"
|
||||
|
||||
#: participation/models.py:1622
|
||||
#: participation/models.py:1701
|
||||
msgid "penalties"
|
||||
msgstr "pénalités"
|
||||
|
||||
#: participation/models.py:1624
|
||||
#: participation/models.py:1703
|
||||
msgid ""
|
||||
"Number of penalties for the defender. The defender will loose a 0.5 "
|
||||
"coefficient per penalty."
|
||||
|
@ -1556,128 +1649,128 @@ msgstr ""
|
|||
"Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 "
|
||||
"sur sa présentation orale par pénalité."
|
||||
|
||||
#: participation/models.py:1721 participation/models.py:1724
|
||||
#: participation/models.py:1727 participation/models.py:1730
|
||||
#: participation/models.py:1829 participation/models.py:1832
|
||||
#: participation/models.py:1835 participation/models.py:1838
|
||||
#, python-brace-format
|
||||
msgid "Team {trigram} is not registered in the pool."
|
||||
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
|
||||
|
||||
#: participation/models.py:1735
|
||||
#: participation/models.py:1843
|
||||
#, python-brace-format
|
||||
msgid "Passage of {defender} for problem {problem}"
|
||||
msgstr "Passage de {defender} pour le problème {problem}"
|
||||
|
||||
#: participation/models.py:1739 participation/models.py:1748
|
||||
#: participation/models.py:1833 participation/models.py:1876
|
||||
#: participation/models.py:1847 participation/models.py:1856
|
||||
#: participation/models.py:1941 participation/models.py:1984
|
||||
msgid "passage"
|
||||
msgstr "passage"
|
||||
|
||||
#: participation/models.py:1740
|
||||
#: participation/models.py:1848
|
||||
msgid "passages"
|
||||
msgstr "passages"
|
||||
|
||||
#: participation/models.py:1759
|
||||
#: participation/models.py:1867
|
||||
msgid "difference"
|
||||
msgstr "différence"
|
||||
|
||||
#: participation/models.py:1760
|
||||
#: participation/models.py:1868
|
||||
msgid "Score to add/remove on the final score"
|
||||
msgstr "Score à ajouter/retrancher au score final"
|
||||
|
||||
#: participation/models.py:1767
|
||||
#: participation/models.py:1875
|
||||
msgid "tweak"
|
||||
msgstr "harmonisation"
|
||||
|
||||
#: participation/models.py:1768
|
||||
#: participation/models.py:1876
|
||||
msgid "tweaks"
|
||||
msgstr "harmonisations"
|
||||
|
||||
#: participation/models.py:1796
|
||||
#: participation/models.py:1904
|
||||
msgid "solution for the final tournament"
|
||||
msgstr "solution pour la finale"
|
||||
|
||||
#: participation/models.py:1801 participation/models.py:1845
|
||||
#: participation/models.py:1909 participation/models.py:1953
|
||||
msgid "file"
|
||||
msgstr "fichier"
|
||||
|
||||
#: participation/models.py:1811
|
||||
#: participation/models.py:1919
|
||||
#, python-brace-format
|
||||
msgid "Solution of team {team} for problem {problem}"
|
||||
msgstr "Solution de l'équipe {team} pour le problème {problem}"
|
||||
|
||||
#: participation/models.py:1813
|
||||
#: participation/models.py:1921
|
||||
msgid "for final"
|
||||
msgstr "pour la finale"
|
||||
|
||||
#: participation/models.py:1816
|
||||
#: participation/models.py:1924
|
||||
msgid "solution"
|
||||
msgstr "solution"
|
||||
|
||||
#: participation/models.py:1817
|
||||
#: participation/models.py:1925
|
||||
msgid "solutions"
|
||||
msgstr "solutions"
|
||||
|
||||
#: participation/models.py:1851
|
||||
#: participation/models.py:1959
|
||||
#, python-brace-format
|
||||
msgid "Synthesis of {team} as {type} for problem {problem} of {defender}"
|
||||
msgstr ""
|
||||
"Note de synthèse de l'équipe {team} en tant que {type} pour le problème "
|
||||
"{problem} de {defender}"
|
||||
|
||||
#: participation/models.py:1859
|
||||
#: participation/models.py:1967
|
||||
msgid "synthesis"
|
||||
msgstr "note de synthèse"
|
||||
|
||||
#: participation/models.py:1860
|
||||
#: participation/models.py:1968
|
||||
msgid "syntheses"
|
||||
msgstr "notes de synthèse"
|
||||
|
||||
#: participation/models.py:1869
|
||||
#: participation/models.py:1977
|
||||
msgid "jury"
|
||||
msgstr "jury"
|
||||
|
||||
#: participation/models.py:1881
|
||||
#: participation/models.py:1989
|
||||
msgid "defender writing note"
|
||||
msgstr "note d'écrit défenseur⋅se"
|
||||
|
||||
#: participation/models.py:1887
|
||||
#: participation/models.py:1995
|
||||
msgid "defender oral note"
|
||||
msgstr "note d'oral défenseur⋅se"
|
||||
|
||||
#: participation/models.py:1893
|
||||
#: participation/models.py:2001
|
||||
msgid "opponent writing note"
|
||||
msgstr "note d'écrit opposant⋅e"
|
||||
|
||||
#: participation/models.py:1899
|
||||
#: participation/models.py:2007
|
||||
msgid "opponent oral note"
|
||||
msgstr "note d'oral opposant⋅e"
|
||||
|
||||
#: participation/models.py:1905
|
||||
#: participation/models.py:2013
|
||||
msgid "reviewer writing note"
|
||||
msgstr "note d'écrit rapporteur⋅rice"
|
||||
|
||||
#: participation/models.py:1911
|
||||
#: participation/models.py:2019
|
||||
msgid "reviewer oral note"
|
||||
msgstr "note d'oral du rapporteur⋅rice"
|
||||
|
||||
#: participation/models.py:1917
|
||||
#: participation/models.py:2025
|
||||
msgid "observer writing note"
|
||||
msgstr "note d'écrit de l'observateur⋅rice"
|
||||
|
||||
#: participation/models.py:1923
|
||||
#: participation/models.py:2031
|
||||
msgid "observer oral note"
|
||||
msgstr "note d'oral de l'observateur⋅rice"
|
||||
|
||||
#: participation/models.py:1988
|
||||
#: participation/models.py:2096
|
||||
#, python-brace-format
|
||||
msgid "Notes of {jury} for {passage}"
|
||||
msgstr "Notes de {jury} pour le {passage}"
|
||||
|
||||
#: participation/models.py:1991
|
||||
#: participation/models.py:2099
|
||||
msgid "note"
|
||||
msgstr "note"
|
||||
|
||||
#: participation/models.py:1992
|
||||
#: participation/models.py:2100
|
||||
msgid "notes"
|
||||
msgstr "notes"
|
||||
|
||||
|
@ -1718,8 +1811,8 @@ msgstr "Pas d'équipe définie"
|
|||
#: participation/tables.py:147
|
||||
#: participation/templates/participation/note_form.html:14
|
||||
#: participation/templates/participation/passage_detail.html:15
|
||||
#: participation/templates/participation/passage_detail.html:168
|
||||
#: participation/templates/participation/passage_detail.html:174
|
||||
#: participation/templates/participation/passage_detail.html:176
|
||||
#: participation/templates/participation/passage_detail.html:182
|
||||
#: participation/templates/participation/pool_detail.html:13
|
||||
#: participation/templates/participation/pool_detail.html:152
|
||||
#: participation/templates/participation/team_detail.html:185
|
||||
|
@ -1807,7 +1900,7 @@ msgid "Upload solution"
|
|||
msgstr "Envoyer une solution"
|
||||
|
||||
#: participation/templates/participation/participation_detail.html:65
|
||||
#: participation/templates/participation/passage_detail.html:180
|
||||
#: participation/templates/participation/passage_detail.html:188
|
||||
#: participation/templates/participation/pool_detail.html:157
|
||||
#: participation/templates/participation/team_detail.html:245
|
||||
#: participation/templates/participation/upload_motivation_letter.html:13
|
||||
|
@ -1870,12 +1963,12 @@ msgid "No synthesis was uploaded yet."
|
|||
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:173
|
||||
#: participation/templates/participation/passage_detail.html:181
|
||||
msgid "Update notes"
|
||||
msgstr "Modifier les notes"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:66
|
||||
#: participation/templates/participation/passage_detail.html:179
|
||||
#: participation/templates/participation/passage_detail.html:187
|
||||
msgid "Upload synthesis"
|
||||
msgstr "Envoyer une note de synthèse"
|
||||
|
||||
|
@ -1887,51 +1980,51 @@ msgstr "Détails des notes"
|
|||
msgid "Average points for the defender writing"
|
||||
msgstr "Moyenne de l'écrit de l'équipe défenseuse"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:88
|
||||
#: participation/templates/participation/passage_detail.html:90
|
||||
msgid "Average points for the defender oral"
|
||||
msgstr "Moyenne de l'oral de l'équipe défenseuse"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:94
|
||||
#: participation/templates/participation/passage_detail.html:98
|
||||
msgid "Average points for the opponent writing"
|
||||
msgstr "Moyenne de l'écrit de l'équipe opposante"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:100
|
||||
#: participation/templates/participation/passage_detail.html:104
|
||||
msgid "Average points for the opponent oral"
|
||||
msgstr "Moyenne de l'oral de l'équipe opposante"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:106
|
||||
#: participation/templates/participation/passage_detail.html:110
|
||||
msgid "Average points for the reviewer writing"
|
||||
msgstr "Moyenne de l'écrit de l'équipe rapportrice"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:112
|
||||
#: participation/templates/participation/passage_detail.html:116
|
||||
msgid "Average points for the reviewer oral"
|
||||
msgstr "Moyenne de l'oral de l'équipe rapportrice"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:119
|
||||
#: participation/templates/participation/passage_detail.html:123
|
||||
msgid "Average points for the observer writing"
|
||||
msgstr "Moyenne de l'écrit de l'équipe observatrice"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:125
|
||||
#: participation/templates/participation/passage_detail.html:129
|
||||
msgid "Average points for the observer oral"
|
||||
msgstr "Moyenne de l'oral de l'équipe observatrice"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:136
|
||||
#: participation/templates/participation/passage_detail.html:140
|
||||
msgid "Defender points"
|
||||
msgstr "Points de l'équipe défenseuse"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:142
|
||||
#: participation/templates/participation/passage_detail.html:148
|
||||
msgid "Opponent points"
|
||||
msgstr "Points de l'équipe opposante"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:148
|
||||
#: participation/templates/participation/passage_detail.html:156
|
||||
msgid "reviewer points"
|
||||
msgstr "Points de l'équipe rapportrice"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:155
|
||||
#: participation/templates/participation/passage_detail.html:163
|
||||
msgid "observer points"
|
||||
msgstr "Points de l'équipe observatrice"
|
||||
|
||||
#: participation/templates/participation/passage_detail.html:167
|
||||
#: participation/templates/participation/passage_detail.html:175
|
||||
#: participation/templates/participation/passage_form.html:11
|
||||
msgid "Update passage"
|
||||
msgstr "Modifier le passage"
|
||||
|
@ -2355,10 +2448,6 @@ msgstr "Dépublier les notes pour le second tour"
|
|||
msgid "Files available for download"
|
||||
msgstr "Fichiers disponibles au téléchargement"
|
||||
|
||||
#: participation/templates/participation/tournament_harmonize.html:14
|
||||
msgid "Rank"
|
||||
msgstr "Rang"
|
||||
|
||||
#: participation/templates/participation/tournament_harmonize.html:16
|
||||
#: registration/models.py:655
|
||||
msgid "Note"
|
||||
|
@ -2615,17 +2704,37 @@ msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e
|
|||
msgid "Notes were successfully uploaded."
|
||||
msgstr "Les notes ont bien été envoyées."
|
||||
|
||||
#: participation/views.py:1845
|
||||
#: participation/views.py:1496
|
||||
msgid "Role"
|
||||
msgstr "Rôle"
|
||||
|
||||
#: participation/views.py:1502
|
||||
msgid "Defender"
|
||||
msgstr "Défenseur⋅se"
|
||||
|
||||
#: participation/views.py:1508
|
||||
msgid "Opponent"
|
||||
msgstr "Opposant⋅e"
|
||||
|
||||
#: participation/views.py:1515
|
||||
msgid "Reviewer"
|
||||
msgstr "Rapporteur⋅rice"
|
||||
|
||||
#: participation/views.py:1522
|
||||
msgid "Observer"
|
||||
msgstr "Observateur⋅rice"
|
||||
|
||||
#: participation/views.py:1893
|
||||
#, python-brace-format
|
||||
msgid "Notation sheets of pool {pool} of {tournament}.zip"
|
||||
msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip"
|
||||
|
||||
#: participation/views.py:1850
|
||||
#: participation/views.py:1898
|
||||
#, python-brace-format
|
||||
msgid "Notation sheets of {tournament}.zip"
|
||||
msgstr "Feuilles de notation de {tournament}.zip"
|
||||
|
||||
#: participation/views.py:2017
|
||||
#: participation/views.py:2065
|
||||
msgid "You can't upload a synthesis after the deadline."
|
||||
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
|
||||
|
||||
|
|
|
@ -302,25 +302,26 @@ class UploadNotesForm(forms.Form):
|
|||
line = [s for s in line if s == s]
|
||||
# Strip cases
|
||||
line = [str(s).strip() for s in line if str(s)]
|
||||
if line and line[0] == 'Problème':
|
||||
if line and line[0] in ["Problème", "Problem"]:
|
||||
pool_size = len(line) - 1
|
||||
line_length = 2 + 6 * pool_size
|
||||
line_length = 2 + (8 if df.iat[1, 8] == "Observer" else 6) * pool_size
|
||||
continue
|
||||
|
||||
if pool_size == 0 or len(line) < line_length:
|
||||
continue
|
||||
|
||||
name = line[0]
|
||||
if name.lower() in ["rôle", "juré⋅e", "juré?e", "moyenne", "coefficient", "sous-total", "équipe", "equipe"]:
|
||||
if name.lower() in ["rôle", "juré⋅e", "juré?e", "moyenne", "coefficient", "sous-total", "équipe", "equipe",
|
||||
"role", "juree", "average", "coefficient", "subtotal", "team"]:
|
||||
continue
|
||||
notes = line[2:line_length]
|
||||
print(name, notes)
|
||||
if not all(s.isnumeric() or s[0] == '-' and s[1:].isnumeric() for s in notes):
|
||||
continue
|
||||
notes = list(map(lambda x: int(float(x)), notes))
|
||||
print(notes)
|
||||
|
||||
max_notes = pool_size * [20, 20, 10, 10, 10, 10]
|
||||
max_notes = pool_size * [20 if settings.TFJM_APP == "TFJM" else 10,
|
||||
20 if settings.TFJM_APP == "TFJM" else 10,
|
||||
10, 10, 10, 10, 10, 10]
|
||||
for n, max_n in zip(notes, max_notes):
|
||||
if n > max_n:
|
||||
self.add_error('file',
|
||||
|
|
|
@ -458,7 +458,7 @@ class Tournament(models.Model):
|
|||
return self.notes_sheet_id
|
||||
|
||||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||
spreadsheet = gc.create(f"Feuille de notes - {self.name}", folder_id=settings.NOTES_DRIVE_FOLDER_ID)
|
||||
spreadsheet = gc.create(f"{_('Notation sheet')} - {self.name}", folder_id=settings.NOTES_DRIVE_FOLDER_ID)
|
||||
spreadsheet.update_locale("fr_FR")
|
||||
spreadsheet.share(None, "anyone", "writer", with_link=True)
|
||||
self.notes_sheet_id = spreadsheet.id
|
||||
|
@ -470,17 +470,21 @@ class Tournament(models.Model):
|
|||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||
spreadsheet = gc.open_by_key(self.notes_sheet_id)
|
||||
worksheets = spreadsheet.worksheets()
|
||||
if "Classement final" not in [ws.title for ws in worksheets]:
|
||||
worksheet = spreadsheet.add_worksheet("Classement final", 100, 26)
|
||||
if _("Final ranking") not in [ws.title for ws in worksheets]:
|
||||
worksheet = spreadsheet.add_worksheet(_("Final ranking"), 30, 10)
|
||||
else:
|
||||
worksheet = spreadsheet.worksheet("Classement final")
|
||||
worksheet = spreadsheet.worksheet(_("Final ranking"))
|
||||
|
||||
if worksheet.index != self.pools.count():
|
||||
worksheet.update_index(self.pools.count())
|
||||
|
||||
header = [["Équipe", "Score jour 1", "Harmonisation 1", "Score jour 2", "Harmonisation 2", "Total", "Rang"]]
|
||||
header = [[_("Team"), _("Scores day 1"), _("Tweaks day 1"), _("Scores day 2"), _("Tweaks day 2")]
|
||||
+ ([_("Total D1 + D2"), _("Scores day 3"), _("Tweaks day 3")]
|
||||
if settings.NB_ROUNDS >= 3 else [])
|
||||
+ [_("Total"), _("Rank")]]
|
||||
lines = []
|
||||
participations = self.participations.filter(pools__round=1, pools__tournament=self).distinct().all()
|
||||
total_col, rank_col = ("F", "G") if settings.NB_ROUNDS == 2 else ("I", "J")
|
||||
for i, participation in enumerate(participations):
|
||||
line = [f"{participation.team.name} ({participation.team.trigram})"]
|
||||
lines.append(line)
|
||||
|
@ -494,7 +498,7 @@ class Tournament(models.Model):
|
|||
tweak1_qs = Tweak.objects.filter(pool=pool1, participation=participation)
|
||||
tweak1 = tweak1_qs.get() if tweak1_qs.exists() else None
|
||||
|
||||
line.append(f"=SIERREUR('Poule {pool1.short_name}'!$D{pool1.juries.count() + 10 + position1}; 0)")
|
||||
line.append(f"=SIERREUR('{_('Pool')} {pool1.short_name}'!$D{pool1.juries.count() + 10 + position1}; 0)")
|
||||
line.append(tweak1.diff if tweak1 else 0)
|
||||
|
||||
if Passage.objects.filter(pool__tournament=self, pool__round=2, defender=participation).exists():
|
||||
|
@ -508,23 +512,49 @@ class Tournament(models.Model):
|
|||
tweak2 = tweak2_qs.get() if tweak2_qs.exists() else None
|
||||
|
||||
line.append(
|
||||
f"=SIERREUR('Poule {pool2.short_name}'!$D{pool2.juries.count() + 10 + position2}; 0)")
|
||||
f"=SIERREUR('{_('Pool')} {pool2.short_name}'!$D{pool2.juries.count() + 10 + position2}; 0)")
|
||||
line.append(tweak2.diff if tweak2 else 0)
|
||||
|
||||
if settings.NB_ROUNDS >= 3:
|
||||
line.append(f"=$B{i + 2} + $C{i + 2} + $D{i + 2} + E{i + 2}")
|
||||
|
||||
if Passage.objects.filter(pool__tournament=self, pool__round=3, defender=participation).exists():
|
||||
passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, defender=participation)
|
||||
pool3 = passage3.pool
|
||||
if pool3.participations.count() != 5:
|
||||
position3 = passage3.position
|
||||
else:
|
||||
position3 = (passage3.position - 1) * 2 + pool3.room
|
||||
tweak3_qs = Tweak.objects.filter(pool=pool3, participation=participation)
|
||||
tweak3 = tweak3_qs.get() if tweak3_qs.exists() else None
|
||||
|
||||
line.append(
|
||||
f"=SIERREUR('{_('Pool')} {pool3.short_name}'!$D{pool3.juries.count() + 10 + position3}; 0)")
|
||||
line.append(tweak3.diff if tweak3 else 0)
|
||||
else:
|
||||
line.append(0)
|
||||
line.append(0)
|
||||
else:
|
||||
# User has no second pool yet
|
||||
# There is no second pool yet
|
||||
line.append(0)
|
||||
line.append(0)
|
||||
|
||||
line.append(f"=$B{i + 2} + $C{i + 2} + $D{i + 2} + E{i + 2}")
|
||||
line.append(f"=RANG($F{i + 2}; $F$2:$F${participations.count() + 1})")
|
||||
if settings.NB_ROUNDS >= 3:
|
||||
line.append(f"=$B{i + 2} + $C{i + 2} + $D{i + 2} + E{i + 2}")
|
||||
line.append(0)
|
||||
line.append(0)
|
||||
|
||||
final_ranking = [["", "", "", ""], ["", "", "", ""], ["Équipe", "Score", "Rang", "Mention"],
|
||||
line.append(f"=$B{i + 2} + $C{i + 2} + $D{i + 2} + E{i + 2}"
|
||||
+ (f" + (PI() - 2) * $G{i + 2} + $H{i + 2}" if settings.NB_ROUNDS >= 3 else ""))
|
||||
line.append(f"=RANG(${total_col}{i + 2}; ${total_col}$2:${total_col}${participations.count() + 1})")
|
||||
|
||||
final_ranking = [["", "", "", ""], ["", "", "", ""], [_("Team"), _("Score"), _("Rank"), _("Mention")],
|
||||
[f"=SORT($A$2:$A${participations.count() + 1}; "
|
||||
f"$F$2:$F${participations.count() + 1}; FALSE)",
|
||||
f"=SORT($F$2:$F${participations.count() + 1}; "
|
||||
f"$F$2:$F${participations.count() + 1}; FALSE)",
|
||||
f"=SORT($G$2:$G${participations.count() + 1}; "
|
||||
f"$F$2:$F${participations.count() + 1}; FALSE)", ]]
|
||||
f"${total_col}$2:${total_col}${participations.count() + 1}; FALSE)",
|
||||
f"=SORT(${total_col}$2:${total_col}${participations.count() + 1}; "
|
||||
f"${total_col}$2:${total_col}${participations.count() + 1}; FALSE)",
|
||||
f"=SORT(${rank_col}$2:${rank_col}${participations.count() + 1}; "
|
||||
f"${total_col}$2:${total_col}${participations.count() + 1}; FALSE)", ]]
|
||||
final_ranking += [["", "", ""] for _i in range(participations.count() - 1)]
|
||||
|
||||
notes = dict()
|
||||
|
@ -538,12 +568,13 @@ class Tournament(models.Model):
|
|||
final_ranking[i + 3].append(participation.mention if not self.final else participation.mention_final)
|
||||
|
||||
data = header + lines + final_ranking
|
||||
worksheet.update(data, f"A1:G{2 * participations.count() + 4}", raw=False)
|
||||
worksheet.update(data, f"A1:{rank_col}{2 * participations.count() + 4}", raw=False)
|
||||
|
||||
format_requests = []
|
||||
|
||||
# Set the width of the columns
|
||||
column_widths = [("A", 300), ("B", 150), ("C", 150), ("D", 150), ("E", 150), ("F", 150), ("G", 150)]
|
||||
column_widths = [("A", 300), ("B", 150), ("C", 150), ("D", 150), ("E", 150), ("F", 150), ("G", 150),
|
||||
("H", 150), ("I", 150), ("J", 150)]
|
||||
for column, width in column_widths:
|
||||
grid_range = a1_range_to_grid_range(column, worksheet.id)
|
||||
format_requests.append({
|
||||
|
@ -563,7 +594,7 @@ class Tournament(models.Model):
|
|||
|
||||
# Set borders
|
||||
border_ranges = [("A1:Z", "0000"),
|
||||
(f"A1:G{participations.count() + 1}", "1111"),
|
||||
(f"A1:{rank_col}{participations.count() + 1}", "1111"),
|
||||
(f"A{participations.count() + 4}:D{2 * participations.count() + 4}", "1111")]
|
||||
sides_names = ['top', 'bottom', 'left', 'right']
|
||||
styles = ["NONE", "SOLID", "SOLID_MEDIUM", "SOLID_THICK", "DOUBLE"]
|
||||
|
@ -585,7 +616,7 @@ class Tournament(models.Model):
|
|||
})
|
||||
|
||||
# Make titles bold
|
||||
bold_ranges = [("A1:Z", False), ("A1:G1", True),
|
||||
bold_ranges = [("A1:Z", False), (f"A1:{rank_col}1", True),
|
||||
(f"A{participations.count() + 4}:D{participations.count() + 4}", True)]
|
||||
for bold_range, bold in bold_ranges:
|
||||
format_requests.append({
|
||||
|
@ -598,14 +629,18 @@ class Tournament(models.Model):
|
|||
|
||||
# Set background color for headers and footers
|
||||
bg_colors = [("A1:Z", (1, 1, 1)),
|
||||
("A1:G1", (0.8, 0.8, 0.8)),
|
||||
(f"A1:{rank_col}1", (0.8, 0.8, 0.8)),
|
||||
(f"A2:B{participations.count() + 1}", (0.9, 0.9, 0.9)),
|
||||
(f"C2:C{participations.count() + 1}", (1, 1, 1)),
|
||||
(f"D2:D{participations.count() + 1}", (0.9, 0.9, 0.9)),
|
||||
(f"E2:E{participations.count() + 1}", (1, 1, 1)),
|
||||
(f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9)),
|
||||
(f"A{participations.count() + 4}:D{participations.count() + 4}", (0.8, 0.8, 0.8)),
|
||||
(f"A{participations.count() + 5}:C{2 * participations.count() + 4}", (0.9, 0.9, 0.9)),]
|
||||
if settings.NB_ROUNDS >= 3:
|
||||
bg_colors.append((f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9)))
|
||||
bg_colors.append((f"H2:I{participations.count() + 1}", (0.9, 0.9, 0.9)))
|
||||
else:
|
||||
bg_colors.append((f"F2:G{participations.count() + 1}", (0.9, 0.9, 0.9)))
|
||||
for bg_range, bg_color in bg_colors:
|
||||
r, g, b = bg_color
|
||||
format_requests.append({
|
||||
|
@ -622,9 +657,15 @@ class Tournament(models.Model):
|
|||
(f"D2:D{participations.count() + 1}", "0.0"),
|
||||
(f"E2:E{participations.count() + 1}", "0"),
|
||||
(f"F2:F{participations.count() + 1}", "0.0"),
|
||||
(f"G2:G{participations.count() + 1}", "0"),
|
||||
(f"B{participations.count() + 5}:B{2 * participations.count() + 5}", "0.0"),
|
||||
(f"C{participations.count() + 5}:C{2 * participations.count() + 5}", "0"), ]
|
||||
if settings.NB_ROUNDS >= 3:
|
||||
number_format_ranges += [(f"G2:G{participations.count() + 1}", "0.0"),
|
||||
(f"H2:H{participations.count() + 1}", "0"),
|
||||
(f"I2:I{participations.count() + 1}", "0.0"),
|
||||
(f"J2:J{participations.count() + 1}", "0"), ]
|
||||
else:
|
||||
number_format_ranges.append((f"G2:G{participations.count() + 1}", "0"))
|
||||
for number_format_range, pattern in number_format_ranges:
|
||||
format_requests.append({
|
||||
"repeatCell": {
|
||||
|
@ -643,16 +684,16 @@ class Tournament(models.Model):
|
|||
})
|
||||
|
||||
# Protect the header, the juries list, the footer and the ranking
|
||||
protected_ranges = ["A1:G1", f"A2:B{participations.count() + 1}",
|
||||
protected_ranges = ["A1:J1", f"A2:B{participations.count() + 1}",
|
||||
f"D2:D{participations.count() + 1}", f"F2:G{participations.count() + 1}",
|
||||
f"I2:J{participations.count() + 1}",
|
||||
f"A{participations.count() + 4}:C{2 * participations.count() + 4}", ]
|
||||
for protected_range in protected_ranges:
|
||||
format_requests.append({
|
||||
"addProtectedRange": {
|
||||
"protectedRange": {
|
||||
"range": a1_range_to_grid_range(protected_range, worksheet.id),
|
||||
"description": "Structure du tableur à ne pas modifier "
|
||||
"pour une meilleure prise en charge automatisée",
|
||||
"description": _("Don't update the table structure for a better automated integration."),
|
||||
"warningOnly": True,
|
||||
},
|
||||
}
|
||||
|
@ -666,19 +707,21 @@ class Tournament(models.Model):
|
|||
# Draw has not been done yet
|
||||
return
|
||||
|
||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
||||
|
||||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||
spreadsheet = gc.open_by_key(self.notes_sheet_id)
|
||||
worksheet = spreadsheet.worksheet("Classement final")
|
||||
worksheet = spreadsheet.worksheet(_("Final ranking"))
|
||||
|
||||
score_cell = worksheet.find("Score")
|
||||
score_cell = worksheet.find(_("Score"))
|
||||
max_row = score_cell.row - 3
|
||||
if max_row == 1:
|
||||
# There is no team
|
||||
return
|
||||
|
||||
data = worksheet.get_values(f"A2:E{max_row}")
|
||||
data = worksheet.get_values(f"A2:H{max_row}")
|
||||
for line in data:
|
||||
trigram = line[0][-4:-1]
|
||||
trigram = line[0][-settings.TEAM_CODE_LENGTH - 1:-1]
|
||||
participation = self.participations.get(team__trigram=trigram)
|
||||
pool1 = self.pools.get(round=1, participations=participation, room=1)
|
||||
tweak1_qs = Tweak.objects.filter(pool=pool1, participation=participation)
|
||||
|
@ -701,6 +744,17 @@ class Tournament(models.Model):
|
|||
create_defaults={'diff': tweak2_nb, 'pool': pool2,
|
||||
'participation': participation})
|
||||
|
||||
if self.pools.filter(round=3, participations=participation).exists():
|
||||
pool3 = self.pools.get(round=3, participations=participation, room=1)
|
||||
tweak3_qs = Tweak.objects.filter(pool=pool3, participation=participation)
|
||||
tweak3_nb = int(line[7])
|
||||
if not tweak3_nb:
|
||||
tweak3_qs.delete()
|
||||
else:
|
||||
tweak3_qs.update_or_create(defaults={'diff': tweak3_nb},
|
||||
create_defaults={'diff': tweak3_nb, 'pool': pool3,
|
||||
'participation': participation})
|
||||
|
||||
nb_participations = self.participations.filter(valid=True).count()
|
||||
mentions = worksheet.get_values(f"A{score_cell.row + 1}:D{score_cell.row + nb_participations}")
|
||||
notes = dict()
|
||||
|
@ -1164,26 +1218,31 @@ class Pool(models.Model):
|
|||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
|
||||
worksheets = spreadsheet.worksheets()
|
||||
if f"Poule {self.short_name}" not in [ws.title for ws in worksheets]:
|
||||
worksheet = spreadsheet.add_worksheet(f"Poule {self.short_name}", 100, 26)
|
||||
if f"{_('Pool')} {self.short_name}" not in [ws.title for ws in worksheets]:
|
||||
worksheet = spreadsheet.add_worksheet(f"{_('Pool')} {self.short_name}", 100, 34)
|
||||
else:
|
||||
worksheet = spreadsheet.worksheet(f"Poule {self.short_name}")
|
||||
worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}")
|
||||
if any(ws.title == "Sheet1" for ws in worksheets):
|
||||
spreadsheet.del_worksheet(spreadsheet.worksheet("Sheet1"))
|
||||
|
||||
pool_size = self.participations.count()
|
||||
passage_width = 6
|
||||
has_observer = settings.TFJM_APP == "ETEAM" and pool_size >= 4
|
||||
passage_width = 6 + (2 if has_observer else 0)
|
||||
passages = self.passages.all()
|
||||
|
||||
header = [
|
||||
sum(([f"Problème {passage.solution_number}"] + (passage_width - 1) * [""]
|
||||
for passage in passages), start=["Problème", ""]),
|
||||
sum(([f"Défenseur⋅se ({passage.defender.team.trigram})", "",
|
||||
f"Opposant⋅e ({passage.opponent.team.trigram})", "",
|
||||
f"Rapporteur⋅rice ({passage.reviewer.team.trigram})", ""]
|
||||
sum(([_("Problem #{problem}").format(problem=passage.solution_number)] + (passage_width - 1) * [""]
|
||||
for passage in passages), start=[_("Problem"), ""]),
|
||||
sum(([f"{_('Defender')} ({passage.defender.team.trigram})", "",
|
||||
f"{_('Opponent')} ({passage.opponent.team.trigram})", "",
|
||||
f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""]
|
||||
+ ([f"{('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else [])
|
||||
for passage in passages), start=["Rôle", ""]),
|
||||
sum((["Écrit (/20)", "Oral (/20)", "Écrit (/10)", "Oral (/10)", "Écrit (/10)", "Oral (/10)"]
|
||||
for _passage in passages), start=["Juré⋅e", ""]),
|
||||
sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == "TFJM" else 10})",
|
||||
f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"
|
||||
f"{_('Writing')} (/10)", f"{_('Oral')} (/10)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"]
|
||||
+ ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else [])
|
||||
for _passage in passages), start=[_("Juree"), ""]),
|
||||
]
|
||||
|
||||
notes = [[]] # Begin with empty hidden line to ensure pretty design
|
||||
|
@ -1193,6 +1252,8 @@ class Pool(models.Model):
|
|||
note = passage.notes.filter(jury=jury).first()
|
||||
line.extend([note.defender_writing, note.defender_oral, note.opponent_writing, note.opponent_oral,
|
||||
note.reviewer_writing, note.reviewer_oral])
|
||||
if has_observer:
|
||||
line.extend([note.observer_writing, note.observer_oral])
|
||||
notes.append(line)
|
||||
notes.append([]) # Add empty line to ensure pretty design
|
||||
|
||||
|
@ -1204,11 +1265,15 @@ class Pool(models.Model):
|
|||
return ''
|
||||
return getcol((number - 1) // 26) + chr(65 + (number - 1) % 26)
|
||||
|
||||
average = ["Moyenne", ""]
|
||||
coeffs = sum(([1, 1.6 - 0.4 * passage.defender_penalties, 0.9, 2, 0.9, 1] for passage in passages),
|
||||
start=["Coefficient", ""])
|
||||
subtotal = ["Sous-total", ""]
|
||||
footer = [average, coeffs, subtotal, 26 * [""]]
|
||||
average = [_("Average"), ""]
|
||||
coeffs = sum(([passage.coeff_defender_writing, passage.coeff_defender_oral,
|
||||
passage.coeff_opponent_writing, passage.coeff_opponent_oral,
|
||||
passage.coeff_reviewer_writing, passage.coeff_reviewer_oral]
|
||||
+ ([passage.coeff_observer_writing, passage.coeff_observer_oral] if has_observer else [])
|
||||
for passage in passages),
|
||||
start=[_("Coefficient"), ""])
|
||||
subtotal = [_("Subtotal"), ""]
|
||||
footer = [average, coeffs, subtotal, 34 * [""]]
|
||||
|
||||
min_row = 5
|
||||
max_row = min_row + self.juries.count()
|
||||
|
@ -1234,8 +1299,14 @@ class Pool(models.Model):
|
|||
subtotal.extend([f"={rep_w_col}{max_row + 1} * {rep_w_col}{max_row + 2}"
|
||||
f" + {rep_o_col}{max_row + 1} * {rep_o_col}{max_row + 2}", ""])
|
||||
|
||||
if has_observer:
|
||||
obs_w_col = getcol(min_column + passage_width * i + 6)
|
||||
obs_o_col = getcol(min_column + passage_width * i + 7)
|
||||
subtotal.extend([f"={obs_w_col}{max_row + 1} * {obs_w_col}{max_row + 2}"
|
||||
f" + {obs_o_col}{max_row + 1} * {obs_o_col}{max_row + 2}", ""])
|
||||
|
||||
ranking = [
|
||||
["Équipe", "", "Problème", "Total", "Rang"],
|
||||
[_("Team"), "", _("Problem"), _("Total"), _("Rank")],
|
||||
]
|
||||
all_passages = Passage.objects.filter(pool__tournament=self.tournament,
|
||||
pool__round=self.round,
|
||||
|
@ -1258,14 +1329,22 @@ class Pool(models.Model):
|
|||
reviewer_col = reviewer_passage.position - 1
|
||||
|
||||
formula = "="
|
||||
formula += (f"'Poule {defender_passage.pool.short_name}'"
|
||||
formula += (f"'{_('Pool')} {defender_passage.pool.short_name}'"
|
||||
f"!{getcol(min_column + defender_col * passage_width)}{defender_row + 3}") # Defender
|
||||
formula += (f" + 'Poule {opponent_passage.pool.short_name}'"
|
||||
formula += (f" + '{_('Pool')} {opponent_passage.pool.short_name}'"
|
||||
f"!{getcol(min_column + opponent_col * passage_width + 2)}{opponent_row + 3}") # Opponent
|
||||
formula += (f" + 'Poule {reviewer_passage.pool.short_name}'"
|
||||
formula += (f" + '{_('Pool')} {reviewer_passage.pool.short_name}'"
|
||||
f"!{getcol(min_column + reviewer_col * passage_width + 4)}{reviewer_row + 3}") # reviewer
|
||||
if has_observer:
|
||||
observer_passage = Passage.objects.get(observer=participation,
|
||||
pool__tournament=self.tournament, pool__round=self.round)
|
||||
observer_row = 5 + observer_passage.pool.juries.count()
|
||||
observer_col = observer_passage.position - 1
|
||||
formula += (f" + '{_('Pool')} {observer_passage.pool.short_name}'"
|
||||
f"!{getcol(min_column + observer_col * passage_width + 6)}{observer_row + 3}")
|
||||
|
||||
ranking.append([f"{participation.team.name} ({participation.team.trigram})", "",
|
||||
f"='Poule {defender_passage.pool.short_name}'"
|
||||
f"='{_('Pool')} {defender_passage.pool.short_name}'"
|
||||
f"!${getcol(3 + defender_col * passage_width)}$1",
|
||||
formula,
|
||||
f"=RANG(D{max_row + 6 + i}; "
|
||||
|
@ -1273,8 +1352,8 @@ class Pool(models.Model):
|
|||
|
||||
all_values = header + notes + footer + ranking
|
||||
|
||||
worksheet.batch_clear([f"A1:Z{max_row + 5 + pool_size}"])
|
||||
worksheet.update("A1:Z", all_values, raw=False)
|
||||
worksheet.batch_clear([f"A1:AH{max_row + 5 + pool_size}"])
|
||||
worksheet.update("A1:AH", all_values, raw=False)
|
||||
|
||||
format_requests = []
|
||||
|
||||
|
@ -1300,13 +1379,13 @@ class Pool(models.Model):
|
|||
for i in range(pool_size + 1):
|
||||
merge_cells.append(f"A{max_row + 5 + i}:B{max_row + 5 + i}")
|
||||
|
||||
format_requests.append({"unmergeCells": {"range": a1_range_to_grid_range("A1:Z", worksheet.id)}})
|
||||
format_requests.append({"unmergeCells": {"range": a1_range_to_grid_range("A1:AH", worksheet.id)}})
|
||||
for name in merge_cells:
|
||||
grid_range = a1_range_to_grid_range(name, worksheet.id)
|
||||
format_requests.append({"mergeCells": {"mergeType": MergeType.merge_all, "range": grid_range}})
|
||||
|
||||
# Make titles bold
|
||||
bold_ranges = [("A1:Z", False), ("A1:Z3", True),
|
||||
bold_ranges = [("A1:AH", False), ("A1:AH3", True),
|
||||
(f"A{max_row + 1}:B{max_row + 3}", True), (f"A{max_row + 5}:E{max_row + 5}", True)]
|
||||
for bold_range, bold in bold_ranges:
|
||||
format_requests.append({
|
||||
|
@ -1318,7 +1397,7 @@ class Pool(models.Model):
|
|||
})
|
||||
|
||||
# Set background color for headers and footers
|
||||
bg_colors = [("A1:Z", (1, 1, 1)),
|
||||
bg_colors = [("A1:AH", (1, 1, 1)),
|
||||
(f"A1:{getcol(2 + passages.count() * passage_width)}3", (0.8, 0.8, 0.8)),
|
||||
(f"A{min_row - 1}:B{max_row}", (0.95, 0.95, 0.95)),
|
||||
(f"A{max_row + 1}:B{max_row + 3}", (0.8, 0.8, 0.8)),
|
||||
|
@ -1407,7 +1486,7 @@ class Pool(models.Model):
|
|||
})
|
||||
|
||||
# Define borders
|
||||
border_ranges = [("A1:Z", "0000"),
|
||||
border_ranges = [("A1:AH", "0000"),
|
||||
(f"A1:{getcol(2 + passages.count() * passage_width)}{max_row + 3}", "1111"),
|
||||
(f"A{max_row + 5}:E{max_row + pool_size + 5}", "1111"),
|
||||
(f"A1:B{max_row + 3}", "1113"),
|
||||
|
@ -1443,7 +1522,7 @@ class Pool(models.Model):
|
|||
for j in range(passage_width):
|
||||
column = getcol(min_column + i * passage_width + j)
|
||||
min_note = 0
|
||||
max_note = 20 if j < 2 else 10
|
||||
max_note = 20 if j < 2 and settings.TFJM_APP == "TFJM" else 10
|
||||
format_requests.append({
|
||||
"setDataValidation": {
|
||||
"range": a1_range_to_grid_range(f"{column}{min_row - 1}:{column}{max_row}", worksheet.id),
|
||||
|
@ -1453,8 +1532,8 @@ class Pool(models.Model):
|
|||
"values": [{"userEnteredValue": f'=ET(REGEXMATCH(TO_TEXT({column}4); "^-?[0-9]+$"); '
|
||||
f'{column}4>={min_note}; {column}4<={max_note})'},],
|
||||
},
|
||||
"inputMessage": f"La saisie doit être un entier valide "
|
||||
f"compris entre {min_note} et {max_note}.",
|
||||
"inputMessage": (_("Input must be a valid integer between {min_note} and {max_note}.")
|
||||
.format(min_note=min_note, max_note=max_note)),
|
||||
"strict": True,
|
||||
},
|
||||
}
|
||||
|
@ -1482,16 +1561,15 @@ class Pool(models.Model):
|
|||
})
|
||||
|
||||
# Protect the header, the juries list, the footer and the ranking
|
||||
protected_ranges = ["A1:Z4",
|
||||
protected_ranges = ["A1:AH4",
|
||||
f"A{min_row}:B{max_row}",
|
||||
f"A{max_row}:Z{max_row + 5 + pool_size}"]
|
||||
f"A{max_row}:AH{max_row + 5 + pool_size}"]
|
||||
for protected_range in protected_ranges:
|
||||
format_requests.append({
|
||||
"addProtectedRange": {
|
||||
"protectedRange": {
|
||||
"range": a1_range_to_grid_range(protected_range, worksheet.id),
|
||||
"description": "Structure du tableur à ne pas modifier "
|
||||
"pour une meilleure prise en charge automatisée",
|
||||
"description": _("Don't update the table structure for a better automated integration."),
|
||||
"warningOnly": True,
|
||||
},
|
||||
}
|
||||
|
@ -1505,9 +1583,9 @@ class Pool(models.Model):
|
|||
|
||||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
|
||||
worksheet = spreadsheet.worksheet(f"Poule {self.short_name}")
|
||||
worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}")
|
||||
|
||||
average_cell = worksheet.find("Moyenne")
|
||||
average_cell = worksheet.find(_("Average"))
|
||||
min_row = 5
|
||||
max_row = average_cell.row - 1
|
||||
juries_visible = worksheet.get(f"A{min_row}:B{max_row}")
|
||||
|
@ -1527,16 +1605,17 @@ class Pool(models.Model):
|
|||
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
|
||||
self.tournament.create_spreadsheet()
|
||||
spreadsheet = gc.open_by_key(self.tournament.notes_sheet_id)
|
||||
worksheet = spreadsheet.worksheet(f"Poule {self.short_name}")
|
||||
worksheet = spreadsheet.worksheet(f"{_('Pool')} {self.short_name}")
|
||||
|
||||
average_cell = worksheet.find("Moyenne")
|
||||
average_cell = worksheet.find(_("Average"))
|
||||
min_row = 5
|
||||
max_row = average_cell.row - 2
|
||||
data = worksheet.get_values(f"A{min_row}:Z{max_row}")
|
||||
data = worksheet.get_values(f"A{min_row}:AH{max_row}")
|
||||
if not data or not data[0]:
|
||||
return
|
||||
|
||||
passage_width = 6
|
||||
has_observer = settings.TFJM_APP == "ETEAM" and self.participations.count() >= 4
|
||||
passage_width = 6 + (2 if has_observer else 0)
|
||||
for line in data:
|
||||
jury_name = line[0]
|
||||
jury_id = line[1]
|
||||
|
@ -1640,56 +1719,87 @@ class Passage(models.Model):
|
|||
def average_defender_writing(self) -> float:
|
||||
return self.avg(note.defender_writing for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_defender_writing(self) -> float:
|
||||
return 1 if settings.TFJM_APP == "TFJM" else 2
|
||||
|
||||
@property
|
||||
def average_defender_oral(self) -> float:
|
||||
return self.avg(note.defender_oral for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_defender_oral(self) -> float:
|
||||
coeff = 1.6 if settings.TFJM_APP == "TFJM" else 3
|
||||
coeff *= 1 - 0.25 * self.defender_penalties
|
||||
return coeff
|
||||
|
||||
@property
|
||||
def average_defender(self) -> float:
|
||||
writing_coeff = 1 if settings.TFJM_APP == "TFJM" else 2
|
||||
oral_coeff = 1.6 if settings.TFJM_APP == "TFJM" else 3
|
||||
oral_coeff *= 1 - 0.25 * self.defender_penalties
|
||||
return writing_coeff * self.average_defender_writing + oral_coeff * self.average_defender_oral
|
||||
return (self.coeff_defender_writing * self.average_defender_writing
|
||||
+ self.coeff_defender_oral * self.average_defender_oral)
|
||||
|
||||
@property
|
||||
def average_opponent_writing(self) -> float:
|
||||
return self.avg(note.opponent_writing for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_opponent_writing(self) -> float:
|
||||
return 0.9 if not self.observer else 0.6
|
||||
|
||||
@property
|
||||
def average_opponent_oral(self) -> float:
|
||||
return self.avg(note.opponent_oral for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_opponent_oral(self) -> float:
|
||||
return 2
|
||||
|
||||
@property
|
||||
def average_opponent(self) -> float:
|
||||
writing_coeff = 0.9 if not self.observer else 0.6
|
||||
oral_coeff = 2
|
||||
return writing_coeff * self.average_opponent_writing + oral_coeff * self.average_opponent_oral
|
||||
return (self.coeff_opponent_writing * self.average_opponent_writing
|
||||
+ self.coeff_opponent_oral * self.average_opponent_oral)
|
||||
|
||||
@property
|
||||
def average_reviewer_writing(self) -> float:
|
||||
return self.avg(note.reviewer_writing for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_reviewer_writing(self):
|
||||
return 0.9 if not self.observer else 0.6
|
||||
|
||||
@property
|
||||
def average_reviewer_oral(self) -> float:
|
||||
return self.avg(note.reviewer_oral for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_reviewer_oral(self):
|
||||
return 1 if settings.TFJM_APP == "TFJM" else 1.2
|
||||
|
||||
@property
|
||||
def average_reviewer(self) -> float:
|
||||
writing_coeff = 0.9 if not self.observer else 0.6
|
||||
oral_coeff = 1 if settings.TFJM_APP == "TFJM" else 1.2
|
||||
return writing_coeff * self.average_reviewer_writing + oral_coeff * self.average_reviewer_oral
|
||||
return (self.coeff_reviewer_writing * self.average_reviewer_writing
|
||||
+ self.coeff_reviewer_oral * self.average_reviewer_oral)
|
||||
|
||||
@property
|
||||
def average_observer_writing(self) -> float:
|
||||
return self.avg(note.observer_writing for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_observer_writing(self):
|
||||
return 0.6
|
||||
|
||||
@property
|
||||
def average_observer_oral(self) -> float:
|
||||
return self.avg(note.observer_oral for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def coeff_observer_oral(self):
|
||||
return 0.5
|
||||
|
||||
@property
|
||||
def average_observer(self) -> float:
|
||||
return 0.6 * self.average_observer_writing + 0.5 * self.average_observer_oral
|
||||
return (self.coeff_observer_writing * self.average_observer_writing
|
||||
+ self.coeff_observer_oral * self.average_observer_oral)
|
||||
|
||||
@property
|
||||
def averages(self):
|
||||
|
@ -1707,9 +1817,7 @@ class Passage(models.Model):
|
|||
avg = self.average_defender if participation == self.defender else self.average_opponent \
|
||||
if participation == self.opponent else self.average_reviewer if participation == self.reviewer \
|
||||
else self.average_observer if participation == self.observer else 0
|
||||
|
||||
if self.pool.round == 3 and settings.TFJM_APP == "ETEAM":
|
||||
avg *= math.pi - 2
|
||||
avg *= self.pool.coeff
|
||||
|
||||
return avg
|
||||
|
||||
|
@ -1957,7 +2065,7 @@ class Note(models.Model):
|
|||
passage = Passage.objects.prefetch_related('pool__tournament', 'pool__participations').get(pk=self.passage.pk)
|
||||
spreadsheet_id = passage.pool.tournament.notes_sheet_id
|
||||
spreadsheet = gc.open_by_key(spreadsheet_id)
|
||||
worksheet = spreadsheet.worksheet(f"Poule {passage.pool.short_name}")
|
||||
worksheet = spreadsheet.worksheet(f"{_('Pool')} {passage.pool.short_name}")
|
||||
jury_id_cell = worksheet.find(str(self.jury_id), in_column=2)
|
||||
if not jury_id_cell:
|
||||
raise ValueError("The jury ID cell was not found in the spreadsheet.")
|
||||
|
|
|
@ -82,13 +82,17 @@
|
|||
{% trans "Average points for the defender writing" %}
|
||||
({{ passage.defender.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_defender_writing|floatformat }}/20</dd>
|
||||
<dd class="col-sm-4">
|
||||
{{ passage.average_defender_writing|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-8">
|
||||
{% trans "Average points for the defender oral" %}
|
||||
({{ passage.defender.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_defender_oral|floatformat }}/20</dd>
|
||||
<dd class="col-sm-4">
|
||||
{{ passage.average_defender_oral|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-8">
|
||||
{% trans "Average points for the opponent writing" %}
|
||||
|
@ -136,19 +140,23 @@
|
|||
{% trans "Defender points" %}
|
||||
({{ passage.defender.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_defender|floatformat }}/52</dd>
|
||||
<dd class="col-sm-4">
|
||||
{{ passage.average_defender|floatformat }}/{% if TFJM_APP == "TFJM" %}52{% else %}50{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-8">
|
||||
{% trans "Opponent points" %}
|
||||
({{ passage.opponent.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_opponent|floatformat }}/29</dd>
|
||||
<dd class="col-sm-4">
|
||||
{{ passage.average_opponent|floatformat }}/{% if TFJM_APP == "TFJM" %}29{% else %}{% if passage.observer %}26{% else %}29{% endif %}{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-8">
|
||||
{% trans "reviewer points" %}
|
||||
({{ passage.reviewer.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/19</dd>
|
||||
<dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/{% if TFJM_APP == "TFJM" %}19{% else %}{% if passage.observer %}18{% else %}21{% endif %}{% endif %}</dd>
|
||||
|
||||
{% if passage.observer %}
|
||||
<dt class="col-sm-8">
|
||||
|
@ -156,7 +164,7 @@
|
|||
({{ passage.observer.team.trigram }}) :
|
||||
</dt>
|
||||
|
||||
<dd class="col-sm-4">{{ passage.average_observer|floatformat }}/10</dd>
|
||||
<dd class="col-sm-4">{{ passage.average_observer|floatformat }}/6</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
|
|
@ -24,7 +24,7 @@ from django.http import FileResponse, Http404, HttpResponse
|
|||
from django.shortcuts import redirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.timezone import localtime
|
||||
|
@ -1254,7 +1254,7 @@ class PoolUploadNotesView(VolunteerMixin, FormView, DetailView):
|
|||
return self.form_invalid(form)
|
||||
|
||||
for vr, notes in parsed_notes.items():
|
||||
notes_count = 6
|
||||
notes_count = 6 + (2 if pool.participations.count() >= 4 and settings.TFJM_APP == "ETEAM" else 0)
|
||||
for i, passage in enumerate(pool.passages.all()):
|
||||
note = Note.objects.get_or_create(jury=vr, passage=passage)[0]
|
||||
passage_notes = notes[notes_count * i:notes_count * (i + 1)]
|
||||
|
@ -1289,8 +1289,11 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
return self.handle_no_permission()
|
||||
|
||||
def render_to_response(self, context, **response_kwargs): # noqa: C901
|
||||
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
|
||||
|
||||
pool_size = self.object.passages.count()
|
||||
passage_width = 6
|
||||
has_observer = self.object.participations.count() >= 4 and settings.TFJM_APP == "ETEAM"
|
||||
passage_width = 6 + (2 if has_observer else 0)
|
||||
line_length = pool_size * passage_width
|
||||
|
||||
def getcol(number: int) -> str:
|
||||
|
@ -1475,79 +1478,96 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
header_pb = TableRow()
|
||||
table.addElement(header_pb)
|
||||
problems_tc = TableCell(valuetype="string", stylename=title_style_topleft)
|
||||
problems_tc.addElement(P(text="Problème"))
|
||||
problems_tc.addElement(P(text=_("Problem")))
|
||||
problems_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_pb.addElement(problems_tc)
|
||||
header_pb.addElement(CoveredTableCell())
|
||||
for passage in self.object.passages.all():
|
||||
tc = TableCell(valuetype="string", stylename=title_style_topleftright)
|
||||
tc.addElement(P(text=f"Problème {passage.solution_number}"))
|
||||
tc.setAttribute('numbercolumnsspanned', "6")
|
||||
tc.addElement(P(text=_("Problem #{problem}").format(problem=passage.solution_number)))
|
||||
tc.setAttribute('numbercolumnsspanned', str(passage_width))
|
||||
header_pb.addElement(tc)
|
||||
header_pb.addElement(CoveredTableCell(numbercolumnsrepeated=5))
|
||||
header_pb.addElement(CoveredTableCell(numbercolumnsrepeated=passage_width - 1))
|
||||
|
||||
# Add roles on the second line of the table
|
||||
header_role = TableRow()
|
||||
table.addElement(header_role)
|
||||
role_tc = TableCell(valuetype="string", stylename=title_style_left)
|
||||
role_tc.addElement(P(text="Rôle"))
|
||||
role_tc.addElement(P(text=_("Role")))
|
||||
role_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_role.addElement(role_tc)
|
||||
header_role.addElement(CoveredTableCell())
|
||||
for i in range(pool_size):
|
||||
defender_tc = TableCell(valuetype="string", stylename=title_style_left)
|
||||
defender_tc.addElement(P(text="Défenseur⋅se"))
|
||||
defender_tc.addElement(P(text=_("Defender")))
|
||||
defender_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_role.addElement(defender_tc)
|
||||
header_role.addElement(CoveredTableCell())
|
||||
|
||||
opponent_tc = TableCell(valuetype="string", stylename=title_style)
|
||||
opponent_tc.addElement(P(text="Opposant⋅e"))
|
||||
opponent_tc.addElement(P(text=_("Opponent")))
|
||||
opponent_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_role.addElement(opponent_tc)
|
||||
header_role.addElement(CoveredTableCell())
|
||||
|
||||
reviewer_tc = TableCell(valuetype="string",
|
||||
stylename=title_style_right)
|
||||
reviewer_tc.addElement(P(text="Rapporteur⋅rice"))
|
||||
stylename=title_style if has_observer else title_style_right)
|
||||
reviewer_tc.addElement(P(text=_("Reviewer")))
|
||||
reviewer_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_role.addElement(reviewer_tc)
|
||||
header_role.addElement(CoveredTableCell())
|
||||
|
||||
if has_observer:
|
||||
observer_tc = TableCell(valuetype="string", stylename=title_style_right)
|
||||
observer_tc.addElement(P(text=_("Observer")))
|
||||
observer_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_role.addElement(observer_tc)
|
||||
header_role.addElement(CoveredTableCell())
|
||||
|
||||
# Add maximum notes on the third line
|
||||
header_notes = TableRow()
|
||||
table.addElement(header_notes)
|
||||
jury_tc = TableCell(valuetype="string", value="Juré⋅e", stylename=title_style_botleft)
|
||||
jury_tc.addElement(P(text="Juré⋅e"))
|
||||
jury_tc = TableCell(valuetype="string", value=_("Juree"), stylename=title_style_botleft)
|
||||
jury_tc.addElement(P(text=_("Juree")))
|
||||
jury_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
header_notes.addElement(jury_tc)
|
||||
header_notes.addElement(CoveredTableCell())
|
||||
|
||||
for i in range(pool_size):
|
||||
defender_w_tc = TableCell(valuetype="string", stylename=title_style_botleft)
|
||||
defender_w_tc.addElement(P(text="Écrit (/20)"))
|
||||
defender_w_tc.addElement(P(text=f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
|
||||
header_notes.addElement(defender_w_tc)
|
||||
|
||||
defender_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
|
||||
defender_o_tc.addElement(P(text="Oral (/20)"))
|
||||
defender_o_tc.addElement(P(text=f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
|
||||
header_notes.addElement(defender_o_tc)
|
||||
|
||||
opponent_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
|
||||
opponent_w_tc.addElement(P(text="Écrit (/10)"))
|
||||
opponent_w_tc.addElement(P(text=f"{_('Writing')} (/10)"))
|
||||
header_notes.addElement(opponent_w_tc)
|
||||
|
||||
opponent_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
|
||||
opponent_o_tc.addElement(P(text="Oral (/10)"))
|
||||
opponent_o_tc.addElement(P(text=f"{_('Oral')} (/10)"))
|
||||
header_notes.addElement(opponent_o_tc)
|
||||
|
||||
reviewer_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
|
||||
reviewer_w_tc.addElement(P(text="Écrit (/10)"))
|
||||
reviewer_w_tc.addElement(P(text=f"{_('Writing')} (/10)"))
|
||||
header_notes.addElement(reviewer_w_tc)
|
||||
|
||||
reviewer_o_tc = TableCell(valuetype="string", stylename=title_style_botright)
|
||||
reviewer_o_tc.addElement(P(text="Oral (/10)"))
|
||||
reviewer_o_tc = TableCell(valuetype="string",
|
||||
stylename=title_style_bot if has_observer else title_style_botright)
|
||||
reviewer_o_tc.addElement(P(text=f"{_('Oral')} (/10)"))
|
||||
header_notes.addElement(reviewer_o_tc)
|
||||
|
||||
if has_observer:
|
||||
observer_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
|
||||
observer_w_tc.addElement(P(text=f"{_('Writing')} (/10)"))
|
||||
header_notes.addElement(observer_w_tc)
|
||||
|
||||
observer_o_tc = TableCell(valuetype="string", stylename=title_style_botright)
|
||||
observer_o_tc.addElement(P(text=f"{_('Oral')} (/10)"))
|
||||
header_notes.addElement(observer_o_tc)
|
||||
|
||||
# Add a notation line for each jury
|
||||
for jury in self.object.juries.all():
|
||||
jury_row = TableRow()
|
||||
|
@ -1577,7 +1597,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
average_row = TableRow()
|
||||
table.addElement(average_row)
|
||||
average_tc = TableCell(valuetype="string", stylename=title_style_topleftright)
|
||||
average_tc.addElement(P(text="Moyenne"))
|
||||
average_tc.addElement(P(text=_("Average")))
|
||||
average_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
average_row.addElement(average_tc)
|
||||
average_row.addElement(CoveredTableCell())
|
||||
|
@ -1596,40 +1616,50 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
coeff_row = TableRow()
|
||||
table.addElement(coeff_row)
|
||||
coeff_tc = TableCell(valuetype="string", stylename=title_style_leftright)
|
||||
coeff_tc.addElement(P(text="Coefficient"))
|
||||
coeff_tc.addElement(P(text=_("Coefficient")))
|
||||
coeff_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
coeff_row.addElement(coeff_tc)
|
||||
coeff_row.addElement(CoveredTableCell())
|
||||
for passage in self.object.passages.all():
|
||||
defender_w_tc = TableCell(valuetype="float", value=1, stylename=style_left)
|
||||
defender_w_tc.addElement(P(text="1"))
|
||||
defender_w_tc = TableCell(valuetype="float", value=passage.coeff_defender_writing, stylename=style_left)
|
||||
defender_w_tc.addElement(P(text=str(passage.coeff_defender_writing)))
|
||||
coeff_row.addElement(defender_w_tc)
|
||||
|
||||
defender_o_tc = TableCell(valuetype="float", value=1.6 - 0.4 * passage.defender_penalties, stylename=style)
|
||||
defender_o_tc.addElement(P(text=str(2 - 0.4 * passage.defender_penalties)))
|
||||
defender_o_tc = TableCell(valuetype="float", value=passage.coeff_defender_oral, stylename=style)
|
||||
defender_o_tc.addElement(P(text=str(passage.coeff_defender_oral)))
|
||||
coeff_row.addElement(defender_o_tc)
|
||||
|
||||
opponent_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
|
||||
opponent_w_tc.addElement(P(text="1"))
|
||||
opponent_w_tc = TableCell(valuetype="float", value=passage.coeff_opponent_writing, stylename=style)
|
||||
opponent_w_tc.addElement(P(text=str(passage.coeff_opponent_writing)))
|
||||
coeff_row.addElement(opponent_w_tc)
|
||||
|
||||
opponent_o_tc = TableCell(valuetype="float", value=2, stylename=style)
|
||||
opponent_o_tc.addElement(P(text="2"))
|
||||
opponent_o_tc = TableCell(valuetype="float", value=passage.coeff_opponent_oral, stylename=style)
|
||||
opponent_o_tc.addElement(P(text=str(passage.coeff_opponent_oral)))
|
||||
coeff_row.addElement(opponent_o_tc)
|
||||
|
||||
reviewer_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
|
||||
reviewer_w_tc.addElement(P(text="1"))
|
||||
reviewer_w_tc = TableCell(valuetype="float", value=passage.coeff_reviewer_writing, stylename=style)
|
||||
reviewer_w_tc.addElement(P(text=str(passage.coeff_reviewer_writing)))
|
||||
coeff_row.addElement(reviewer_w_tc)
|
||||
|
||||
reviewer_o_tc = TableCell(valuetype="float", value=1, stylename=style_right)
|
||||
reviewer_o_tc.addElement(P(text="1"))
|
||||
reviewer_o_tc = TableCell(valuetype="float", value=passage.coeff_reviewer_oral,
|
||||
stylename=style if has_observer else style_right)
|
||||
reviewer_o_tc.addElement(P(text=str(passage.coeff_reviewer_oral)))
|
||||
coeff_row.addElement(reviewer_o_tc)
|
||||
|
||||
if has_observer:
|
||||
observer_w_tc = TableCell(valuetype="float", value=passage.coeff_observer_writing, stylename=style)
|
||||
observer_w_tc.addElement(P(text=str(passage.coeff_observer_writing)))
|
||||
coeff_row.addElement(observer_w_tc)
|
||||
|
||||
observer_o_tc = TableCell(valuetype="float", value=passage.coeff_observer_oral, stylename=style_right)
|
||||
observer_o_tc.addElement(P(text=str(passage.coeff_observer_oral)))
|
||||
coeff_row.addElement(observer_o_tc)
|
||||
|
||||
# Add the subtotal on the next line
|
||||
subtotal_row = TableRow()
|
||||
table.addElement(subtotal_row)
|
||||
subtotal_tc = TableCell(valuetype="string", stylename=title_style_botleft)
|
||||
subtotal_tc.addElement(P(text="Sous-total"))
|
||||
subtotal_tc.addElement(P(text=_("Subtotal")))
|
||||
subtotal_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
subtotal_row.addElement(subtotal_tc)
|
||||
subtotal_row.addElement(CoveredTableCell())
|
||||
|
@ -1656,7 +1686,8 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
|
||||
rep_w_col = getcol(min_column + passage_width * i + 4)
|
||||
rep_o_col = getcol(min_column + passage_width * i + 5)
|
||||
reviewer_tc = TableCell(valuetype="float", value=passage.average_reviewer, stylename=style_botright)
|
||||
reviewer_tc = TableCell(valuetype="float", value=passage.average_reviewer,
|
||||
stylename=style_bot if has_observer else style_botright)
|
||||
reviewer_tc.addElement(P(text=str(passage.average_reviewer)))
|
||||
reviewer_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
reviewer_tc.setAttribute("formula", f"of:=[.{rep_w_col}{max_row + 1}] * [.{rep_w_col}{max_row + 2}]"
|
||||
|
@ -1664,6 +1695,17 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
subtotal_row.addElement(reviewer_tc)
|
||||
subtotal_row.addElement(CoveredTableCell())
|
||||
|
||||
if has_observer:
|
||||
obs_w_col = getcol(min_column + passage_width * i + 6)
|
||||
obs_o_col = getcol(min_column + passage_width * i + 7)
|
||||
observer_tc = TableCell(valuetype="float", value=passage.average_observer, stylename=style_botright)
|
||||
observer_tc.addElement(P(text=str(passage.average_observer)))
|
||||
observer_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
observer_tc.setAttribute("formula", f"of:=[.{obs_w_col}{max_row + 1}] * [.{obs_w_col}{max_row + 2}]"
|
||||
f" + [.{obs_o_col}{max_row + 1}] * [.{obs_o_col}{max_row + 2}]")
|
||||
subtotal_row.addElement(observer_tc)
|
||||
subtotal_row.addElement(CoveredTableCell())
|
||||
|
||||
table.addElement(TableRow())
|
||||
|
||||
if self.object.participations.count() == 5:
|
||||
|
@ -1681,17 +1723,17 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
scores_header = TableRow()
|
||||
table.addElement(scores_header)
|
||||
team_tc = TableCell(valuetype="string", stylename=title_style_topbotleft)
|
||||
team_tc.addElement(P(text="Équipe"))
|
||||
team_tc.addElement(P(text=_("Team")))
|
||||
team_tc.setAttribute('numbercolumnsspanned', "2")
|
||||
scores_header.addElement(team_tc)
|
||||
problem_tc = TableCell(valuetype="string", stylename=title_style_topbot)
|
||||
problem_tc.addElement(P(text="Problème"))
|
||||
problem_tc.addElement(P(text=_("Problem")))
|
||||
scores_header.addElement(problem_tc)
|
||||
total_tc = TableCell(valuetype="string", stylename=title_style_topbot)
|
||||
total_tc.addElement(P(text="Total"))
|
||||
total_tc.addElement(P(text=_("Total")))
|
||||
scores_header.addElement(total_tc)
|
||||
rank_tc = TableCell(valuetype="string", stylename=title_style_topbotright)
|
||||
rank_tc.addElement(P(text="Rang"))
|
||||
rank_tc.addElement(P(text=_("Rank")))
|
||||
scores_header.addElement(rank_tc)
|
||||
|
||||
sorted_participations = sorted(self.object.participations.all(), key=lambda p: -self.object.average(p))
|
||||
|
@ -1707,13 +1749,15 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
|
||||
problem_tc = TableCell(valuetype="string",
|
||||
stylename=style_bot if passage.position == pool_size else style)
|
||||
problem_tc.addElement(P(text=f"Problème {passage.solution_number}"))
|
||||
problem_tc.addElement(P(text=_("Problem #{problem}").format(problem=passage.solution_number)))
|
||||
problem_tc.setAttribute("formula", f"of:=[.B{3 + passage_width * (passage.position - 1)}]")
|
||||
team_row.addElement(problem_tc)
|
||||
|
||||
defender_pos = passage.position - 1
|
||||
opponent_pos = self.object.passages.get(opponent=passage.defender).position - 1
|
||||
reviewer_pos = self.object.passages.get(reviewer=passage.defender).position - 1
|
||||
observer_pos = self.object.passages.get(observer=passage.defender).position - 1 \
|
||||
if has_observer else None
|
||||
|
||||
score_tc = TableCell(valuetype="float", value=self.object.average(passage.defender),
|
||||
stylename=style_bot if passage.position == pool_size else style)
|
||||
|
@ -1721,7 +1765,10 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
formula = "of:="
|
||||
formula += getcol(min_column + defender_pos * passage_width) + str(max_row + 3) # Defender
|
||||
formula += " + " + getcol(min_column + opponent_pos * passage_width + 2) + str(max_row + 3) # Opponent
|
||||
formula += " + " + getcol(min_column + reviewer_pos * passage_width + 4) + str(max_row + 3) # reviewer
|
||||
formula += " + " + getcol(min_column + reviewer_pos * passage_width + 4) + str(max_row + 3) # Reviewer
|
||||
if has_observer:
|
||||
# Observer
|
||||
formula += " + " + getcol(min_column + observer_pos * passage_width + 6) + str(max_row + 3)
|
||||
score_tc.setAttribute("formula", formula)
|
||||
team_row.addElement(score_tc)
|
||||
|
||||
|
@ -1730,7 +1777,8 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
stylename=style_botright if passage.position == pool_size else style_right)
|
||||
rank_tc.addElement(P(text=str(sorted_participations.index(passage.defender) + 1)))
|
||||
rank_tc.setAttribute("formula", f"of:=RANK([.{score_col}{max_row + 5 + passage.position}]; "
|
||||
f"[.{score_col}${max_row + 6}]:[.{score_col}${max_row + 5 + pool_size}])")
|
||||
f"[.{score_col}${max_row + 6}]:"
|
||||
f"[.{score_col}${max_row + 5 + pool_size}])")
|
||||
team_row.addElement(rank_tc)
|
||||
|
||||
table.addElement(TableRow())
|
||||
|
@ -1755,8 +1803,8 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
|
|||
|
||||
return FileResponse(streaming_content=open("/tmp/notes.ods", "rb"),
|
||||
content_type="application/vnd.oasis.opendocument.spreadsheet",
|
||||
filename=f"Feuille de notes - {self.object.tournament.name} "
|
||||
f"- Poule {self.object.short_name}.ods")
|
||||
filename=f"{_('Notation sheet')} - {self.object.tournament.name} "
|
||||
f"- {_('Pool')} {self.object.short_name}.ods")
|
||||
|
||||
|
||||
class NotationSheetTemplateView(VolunteerMixin, DetailView):
|
||||
|
|
Loading…
Reference in New Issue