Support ODS and CSV formats to read notes from a spreadsheet
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
188b83ce2d
commit
b942baea17
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: TFJM\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-04-03 23:29+0200\n"
|
||||
"POT-Creation-Date: 2024-04-07 09:33+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"
|
||||
|
@ -29,15 +29,15 @@ msgstr "équipes"
|
|||
|
||||
#: draw/admin.py:53 draw/admin.py:71 draw/admin.py:88 draw/models.py:26
|
||||
#: participation/admin.py:79 participation/admin.py:140
|
||||
#: participation/admin.py:171 participation/models.py:648
|
||||
#: participation/models.py:672 participation/models.py:853
|
||||
#: participation/admin.py:171 participation/models.py:656
|
||||
#: participation/models.py:680 participation/models.py:861
|
||||
#: registration/models.py:674
|
||||
#: registration/templates/registration/payment_form.html:53
|
||||
msgid "tournament"
|
||||
msgstr "tournoi"
|
||||
|
||||
#: draw/admin.py:92 draw/models.py:234 draw/models.py:433
|
||||
#: participation/models.py:857
|
||||
#: participation/models.py:865
|
||||
msgid "round"
|
||||
msgstr "tour"
|
||||
|
||||
|
@ -49,64 +49,64 @@ msgstr "Tirage au sort"
|
|||
msgid "You are not an organizer."
|
||||
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
|
||||
|
||||
#: draw/consumers.py:153
|
||||
#: draw/consumers.py:162
|
||||
msgid "The draw is already started."
|
||||
msgstr "Le tirage a déjà commencé."
|
||||
|
||||
#: draw/consumers.py:159
|
||||
#: draw/consumers.py:168
|
||||
msgid "Invalid format"
|
||||
msgstr "Format invalide"
|
||||
|
||||
#: draw/consumers.py:164
|
||||
#: draw/consumers.py:173
|
||||
#, python-brace-format
|
||||
msgid "The sum must be equal to the number of teams: expected {len}, got {sum}"
|
||||
msgstr ""
|
||||
"La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}"
|
||||
|
||||
#: draw/consumers.py:169
|
||||
#: draw/consumers.py:178
|
||||
msgid "There can be at most one pool with 5 teams."
|
||||
msgstr "Il ne peut y avoir au plus qu'une seule poule de 5 équipes."
|
||||
|
||||
#: draw/consumers.py:209
|
||||
#: draw/consumers.py:218
|
||||
msgid "Draw started!"
|
||||
msgstr "Le tirage a commencé !"
|
||||
|
||||
#: draw/consumers.py:231
|
||||
#: draw/consumers.py:240
|
||||
#, python-brace-format
|
||||
msgid "The draw for the tournament {tournament} will start."
|
||||
msgstr "Le tirage au sort du tournoi {tournament} va commencer."
|
||||
|
||||
#: draw/consumers.py:242 draw/consumers.py:268 draw/consumers.py:676
|
||||
#: draw/consumers.py:893 draw/consumers.py:982 draw/consumers.py:1004
|
||||
#: draw/consumers.py:1094 draw/templates/draw/tournament_content.html:5
|
||||
#: draw/consumers.py:251 draw/consumers.py:277 draw/consumers.py:685
|
||||
#: draw/consumers.py:902 draw/consumers.py:991 draw/consumers.py:1013
|
||||
#: draw/consumers.py:1103 draw/templates/draw/tournament_content.html:5
|
||||
msgid "The draw has not started yet."
|
||||
msgstr "Le tirage au sort n'a pas encore commencé."
|
||||
|
||||
#: draw/consumers.py:255
|
||||
#: draw/consumers.py:264
|
||||
#, python-brace-format
|
||||
msgid "The draw for the tournament {tournament} is aborted."
|
||||
msgstr "Le tirage au sort du tournoi {tournament} est annulé."
|
||||
|
||||
#: draw/consumers.py:295 draw/consumers.py:316 draw/consumers.py:611
|
||||
#: draw/consumers.py:681 draw/consumers.py:898
|
||||
#: draw/consumers.py:304 draw/consumers.py:325 draw/consumers.py:620
|
||||
#: draw/consumers.py:690 draw/consumers.py:907
|
||||
msgid "This is not the time for this."
|
||||
msgstr "Ce n'est pas le moment pour cela."
|
||||
|
||||
#: draw/consumers.py:308 draw/consumers.py:311
|
||||
#: draw/consumers.py:317 draw/consumers.py:320
|
||||
msgid "You've already launched the dice."
|
||||
msgstr "Vous avez déjà lancé le dé."
|
||||
|
||||
#: draw/consumers.py:314
|
||||
#: draw/consumers.py:323
|
||||
msgid "It is not your turn."
|
||||
msgstr "Ce n'est pas votre tour."
|
||||
|
||||
#: draw/consumers.py:401
|
||||
#: draw/consumers.py:410
|
||||
#, python-brace-format
|
||||
msgid "Dices from teams {teams} are identical. Please relaunch your dices."
|
||||
msgstr ""
|
||||
"Les dés des équipes {teams} sont identiques. Merci de relancer vos dés."
|
||||
|
||||
#: draw/consumers.py:1007
|
||||
#: draw/consumers.py:1016
|
||||
msgid "This is only available for the final tournament."
|
||||
msgstr "Cela n'est possible que pour la finale."
|
||||
|
||||
|
@ -175,7 +175,7 @@ msgstr "La poule en cours, où les équipes choisissent leurs problèmes"
|
|||
msgid "rounds"
|
||||
msgstr "tours"
|
||||
|
||||
#: draw/models.py:257 participation/models.py:871
|
||||
#: draw/models.py:257 participation/models.py:879
|
||||
msgid "letter"
|
||||
msgstr "lettre"
|
||||
|
||||
|
@ -214,17 +214,17 @@ msgid "Pool {letter}{number}"
|
|||
msgstr "Poule {letter}{number}"
|
||||
|
||||
#: draw/models.py:414 draw/models.py:441 participation/admin.py:136
|
||||
#: participation/admin.py:155 participation/models.py:1340
|
||||
#: participation/models.py:1349 participation/tables.py:84
|
||||
#: participation/admin.py:155 participation/models.py:1350
|
||||
#: participation/models.py:1359 participation/tables.py:84
|
||||
msgid "pool"
|
||||
msgstr "poule"
|
||||
|
||||
#: draw/models.py:415 participation/models.py:1341
|
||||
#: draw/models.py:415 participation/models.py:1351
|
||||
msgid "pools"
|
||||
msgstr "poules"
|
||||
|
||||
#: draw/models.py:427 participation/models.py:843 participation/models.py:1510
|
||||
#: participation/models.py:1540 participation/models.py:1582
|
||||
#: draw/models.py:427 participation/models.py:851 participation/models.py:1520
|
||||
#: participation/models.py:1550 participation/models.py:1592
|
||||
msgid "participation"
|
||||
msgstr "participation"
|
||||
|
||||
|
@ -248,8 +248,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
|
||||
|
||||
#: draw/models.py:464 draw/models.py:487 participation/models.py:1363
|
||||
#: participation/models.py:1547
|
||||
#: draw/models.py:464 draw/models.py:487 participation/models.py:1373
|
||||
#: participation/models.py:1557
|
||||
#, python-brace-format
|
||||
msgid "Problem #{problem}"
|
||||
msgstr "Problème n°{problem}"
|
||||
|
@ -332,7 +332,7 @@ msgid "Continue draw"
|
|||
msgstr "Continuer le tirage"
|
||||
|
||||
#: draw/templates/draw/tournament_content.html:216 participation/admin.py:167
|
||||
#: participation/models.py:252 participation/models.py:663
|
||||
#: participation/models.py:252 participation/models.py:671
|
||||
#: participation/templates/participation/tournament_harmonize.html:15
|
||||
#: registration/models.py:157 registration/models.py:665
|
||||
#: registration/tables.py:39
|
||||
|
@ -378,8 +378,8 @@ msgstr "Êtes-vous sûr·e de vouloir annuler le tirage au sort ?"
|
|||
msgid "Close"
|
||||
msgstr "Fermer"
|
||||
|
||||
#: draw/views.py:31 participation/views.py:156 participation/views.py:470
|
||||
#: participation/views.py:501
|
||||
#: draw/views.py:31 participation/views.py:161 participation/views.py:475
|
||||
#: participation/views.py:506
|
||||
msgid "You are not in a team."
|
||||
msgstr "Vous n'êtes pas dans une équipe."
|
||||
|
||||
|
@ -452,96 +452,96 @@ msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}"
|
|||
msgid "valid"
|
||||
msgstr "valide"
|
||||
|
||||
#: participation/admin.py:87 participation/models.py:684
|
||||
#: participation/admin.py:87 participation/models.py:692
|
||||
msgid "selected for final"
|
||||
msgstr "sélectionnée pour la finale"
|
||||
|
||||
#: participation/admin.py:124 participation/admin.py:183
|
||||
#: participation/models.py:1370 participation/tables.py:112
|
||||
#: participation/models.py:1380 participation/tables.py:112
|
||||
msgid "defender"
|
||||
msgstr "défenseur⋅se"
|
||||
|
||||
#: participation/admin.py:128 participation/models.py:1377
|
||||
#: participation/models.py:1594
|
||||
#: participation/admin.py:128 participation/models.py:1387
|
||||
#: participation/models.py:1604
|
||||
msgid "opponent"
|
||||
msgstr "opposant⋅e"
|
||||
|
||||
#: participation/admin.py:132 participation/models.py:1384
|
||||
#: participation/models.py:1595
|
||||
#: participation/admin.py:132 participation/models.py:1394
|
||||
#: participation/models.py:1605
|
||||
msgid "reporter"
|
||||
msgstr "rapporteur⋅rice"
|
||||
|
||||
#: participation/admin.py:187 participation/models.py:1545
|
||||
#: participation/admin.py:187 participation/models.py:1555
|
||||
msgid "problem"
|
||||
msgstr "numéro de problème"
|
||||
|
||||
#: participation/forms.py:30
|
||||
#: participation/forms.py:32
|
||||
msgid "This name is already used."
|
||||
msgstr "Ce nom est déjà utilisé."
|
||||
|
||||
#: participation/forms.py:37 participation/models.py:42
|
||||
#: participation/forms.py:39 participation/models.py:42
|
||||
msgid "The trigram must be composed of three uppercase letters."
|
||||
msgstr "Le trigramme doit être composé de trois lettres majuscules."
|
||||
|
||||
#: participation/forms.py:40
|
||||
#: participation/forms.py:42
|
||||
msgid "This trigram is already used."
|
||||
msgstr "Ce trigramme est déjà utilisé."
|
||||
|
||||
#: participation/forms.py:55
|
||||
#: participation/forms.py:57
|
||||
msgid "No team was found with this access code."
|
||||
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
|
||||
|
||||
#: participation/forms.py:59 participation/views.py:472
|
||||
#: participation/forms.py:61 participation/views.py:477
|
||||
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:88 participation/forms.py:340
|
||||
#: participation/forms.py:90 participation/forms.py:350
|
||||
#: registration/forms.py:122 registration/forms.py:144
|
||||
#: registration/forms.py:166 registration/forms.py:188
|
||||
#: registration/forms.py:237 registration/forms.py:270
|
||||
msgid "The uploaded file size must be under 2 Mo."
|
||||
msgstr "Le fichier envoyé doit peser moins de 2 Mo."
|
||||
|
||||
#: participation/forms.py:90 registration/forms.py:124
|
||||
#: participation/forms.py:92 registration/forms.py:124
|
||||
#: registration/forms.py:146 registration/forms.py:168
|
||||
#: registration/forms.py:190 registration/forms.py:239
|
||||
#: registration/forms.py:272
|
||||
msgid "The uploaded file must be a PDF, PNG of JPEG file."
|
||||
msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG."
|
||||
|
||||
#: participation/forms.py:108
|
||||
#: participation/forms.py:110
|
||||
msgid "I engage myself to participate to the whole TFJM²."
|
||||
msgstr "Je m'engage à participer à l'intégralité du TFJM²."
|
||||
|
||||
#: participation/forms.py:123
|
||||
#: participation/forms.py:125
|
||||
msgid "Message to address to the team:"
|
||||
msgstr "Message à adresser à l'équipe :"
|
||||
|
||||
#: participation/forms.py:158
|
||||
#: participation/forms.py:160
|
||||
msgid "The uploaded file size must be under 5 Mo."
|
||||
msgstr "Le fichier envoyé doit peser moins de 5 Mo."
|
||||
|
||||
#: participation/forms.py:160 participation/forms.py:342
|
||||
#: participation/forms.py:162 participation/forms.py:352
|
||||
msgid "The uploaded file must be a PDF file."
|
||||
msgstr "Le fichier envoyé doit être au format PDF."
|
||||
|
||||
#: participation/forms.py:164
|
||||
#: participation/forms.py:166
|
||||
msgid "The PDF file must not have more than 30 pages."
|
||||
msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
|
||||
|
||||
#: participation/forms.py:218
|
||||
#: participation/forms.py:220
|
||||
msgid "Add"
|
||||
msgstr "Ajouter"
|
||||
|
||||
#: participation/forms.py:233
|
||||
#: participation/forms.py:235
|
||||
msgid "This user already exists, but is a participant."
|
||||
msgstr "Cet⋅te utilisateur⋅rice existe déjà, mais en tant que participant⋅e."
|
||||
|
||||
#: participation/forms.py:244
|
||||
msgid "CSV file:"
|
||||
msgstr "Tableur au format CSV :"
|
||||
#: participation/forms.py:246
|
||||
msgid "Spreadsheet file:"
|
||||
msgstr "Fichier tableur :"
|
||||
|
||||
#: participation/forms.py:268
|
||||
#: participation/forms.py:272
|
||||
msgid ""
|
||||
"This file contains non-UTF-8 and non-ISO-8859-1 content. Please send your "
|
||||
"sheet as a CSV file."
|
||||
|
@ -549,30 +549,30 @@ 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:283
|
||||
#: participation/forms.py:293
|
||||
msgid "Can't determine the pool size. Are you sure your file is correct?"
|
||||
msgstr ""
|
||||
"Impossible de déterminer la taille de la poule. Êtes-vous sûr⋅e que le "
|
||||
"fichier est correct ?"
|
||||
|
||||
#: participation/forms.py:303
|
||||
#: participation/forms.py:313
|
||||
msgid "The following note is higher of the maximum expected value:"
|
||||
msgstr "La note suivante est supérieure au maximum attendu :"
|
||||
|
||||
#: participation/forms.py:309
|
||||
#: participation/forms.py:319
|
||||
msgid "The following user was not found:"
|
||||
msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :"
|
||||
|
||||
#: participation/forms.py:323
|
||||
#: participation/forms.py:333
|
||||
msgid "The defender, the opponent and the reporter must be different."
|
||||
msgstr ""
|
||||
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
|
||||
|
||||
#: participation/forms.py:327
|
||||
#: participation/forms.py:337
|
||||
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:346
|
||||
#: participation/forms.py:356
|
||||
msgid "The PDF file must not have more than 2 pages."
|
||||
msgstr "Le fichier PDF ne doit pas avoir plus de 2 pages."
|
||||
|
||||
|
@ -797,28 +797,28 @@ msgstr "finale"
|
|||
msgid "Google Sheet ID"
|
||||
msgstr "ID de la feuille Google Sheets"
|
||||
|
||||
#: participation/models.py:649 registration/admin.py:125
|
||||
#: participation/models.py:657 registration/admin.py:125
|
||||
msgid "tournaments"
|
||||
msgstr "tournois"
|
||||
|
||||
#: participation/models.py:678
|
||||
#: participation/models.py:686
|
||||
msgid "valid team"
|
||||
msgstr "équipe valide"
|
||||
|
||||
#: participation/models.py:679
|
||||
#: participation/models.py:687
|
||||
msgid "The participation got the validation of the organizers."
|
||||
msgstr "La participation a été validée par les organisateur⋅rices."
|
||||
|
||||
#: participation/models.py:685
|
||||
#: participation/models.py:693
|
||||
msgid "The team is selected for the final tournament."
|
||||
msgstr "L'équipe est sélectionnée pour la finale."
|
||||
|
||||
#: participation/models.py:692
|
||||
#: participation/models.py:700
|
||||
#, python-brace-format
|
||||
msgid "Participation of the team {name} ({trigram})"
|
||||
msgstr "Participation de l'équipe {name} ({trigram})"
|
||||
|
||||
#: participation/models.py:699
|
||||
#: participation/models.py:707
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>The team {trigram} has {nb_missing_payments} missing payments. Each "
|
||||
|
@ -831,11 +831,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:707
|
||||
#: participation/models.py:715
|
||||
msgid "Missing payments"
|
||||
msgstr "Paiements manquants"
|
||||
|
||||
#: participation/models.py:714
|
||||
#: participation/models.py:722
|
||||
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> "
|
||||
|
@ -850,11 +850,11 @@ msgstr ""
|
|||
"pouvez envoyer vos solutions sur <a href='{url}'>votre page de "
|
||||
"participation</a>.</p>"
|
||||
|
||||
#: participation/models.py:723
|
||||
#: participation/models.py:731
|
||||
msgid "Solutions due"
|
||||
msgstr "Rendu des solutions"
|
||||
|
||||
#: participation/models.py:729 registration/models.py:518
|
||||
#: participation/models.py:737 registration/models.py:518
|
||||
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>."
|
||||
|
@ -864,11 +864,11 @@ msgstr ""
|
|||
"{date:%d/%m/%Y %H:%M}. Vous pouvez y participer sur <a href='{url}'>ce lien</"
|
||||
"a>.</p>"
|
||||
|
||||
#: participation/models.py:735 registration/models.py:525
|
||||
#: participation/models.py:743 registration/models.py:525
|
||||
msgid "Draw of solutions"
|
||||
msgstr "Tirage au sort des solutions"
|
||||
|
||||
#: participation/models.py:746
|
||||
#: participation/models.py:754
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>The solutions draw is ended. You can check the result on <a "
|
||||
|
@ -880,7 +880,7 @@ msgstr ""
|
|||
"tour, vous défendrez <a href='{solution_url}'>votre solution du problème "
|
||||
"{problem}</a>.</p>"
|
||||
|
||||
#: participation/models.py:755 participation/models.py:798
|
||||
#: participation/models.py:763 participation/models.py:806
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>You will oppose the solution of the team {opponent} on the <a "
|
||||
|
@ -891,7 +891,7 @@ 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:764 participation/models.py:807
|
||||
#: participation/models.py:772 participation/models.py:815
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>You will report the solution of the team {reporter} on the <a "
|
||||
|
@ -902,11 +902,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:780 registration/models.py:540
|
||||
#: participation/models.py:788 registration/models.py:540
|
||||
msgid "First round"
|
||||
msgstr "Premier tour"
|
||||
|
||||
#: participation/models.py:791
|
||||
#: participation/models.py:799
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>For the second round, you will defend <a href='{solution_url}'>your "
|
||||
|
@ -915,11 +915,11 @@ msgstr ""
|
|||
"<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
|
||||
"solution du problème {problem}</a>.</p>"
|
||||
|
||||
#: participation/models.py:823 registration/models.py:551
|
||||
#: participation/models.py:831 registration/models.py:551
|
||||
msgid "Second round"
|
||||
msgstr "Second tour"
|
||||
|
||||
#: participation/models.py:829
|
||||
#: participation/models.py:837
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"<p>The tournament {tournament} is ended. You can check the results on the <a "
|
||||
|
@ -928,40 +928,40 @@ 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:834
|
||||
#: participation/models.py:842
|
||||
msgid "Tournament ended"
|
||||
msgstr "Tournoi terminé"
|
||||
|
||||
#: participation/models.py:844 participation/models.py:877
|
||||
#: participation/models.py:852 participation/models.py:885
|
||||
msgid "participations"
|
||||
msgstr "participations"
|
||||
|
||||
#: participation/models.py:859 participation/models.py:860
|
||||
#: participation/models.py:867 participation/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "Round {round}"
|
||||
msgstr "Tour {round}"
|
||||
|
||||
#: participation/models.py:883
|
||||
#: participation/models.py:891
|
||||
msgid "juries"
|
||||
msgstr "jurys"
|
||||
|
||||
#: participation/models.py:892
|
||||
#: participation/models.py:900
|
||||
msgid "president of the jury"
|
||||
msgstr "président⋅e du jury"
|
||||
|
||||
#: participation/models.py:899
|
||||
#: participation/models.py:907
|
||||
msgid "BigBlueButton URL"
|
||||
msgstr "Lien BigBlueButton"
|
||||
|
||||
#: participation/models.py:900
|
||||
#: participation/models.py:908
|
||||
msgid "The link of the BBB visio for this pool."
|
||||
msgstr "Le lien du salon BBB pour cette poule."
|
||||
|
||||
#: participation/models.py:905
|
||||
#: participation/models.py:913
|
||||
msgid "results available"
|
||||
msgstr "résultats disponibles"
|
||||
|
||||
#: participation/models.py:906
|
||||
#: participation/models.py:914
|
||||
msgid ""
|
||||
"Check this case when results become accessible to teams. They stay "
|
||||
"accessible to you. Only averages are given."
|
||||
|
@ -970,37 +970,37 @@ msgstr ""
|
|||
"Ils restent toujours accessibles pour vous. Seules les moyennes sont "
|
||||
"communiquées."
|
||||
|
||||
#: participation/models.py:931
|
||||
#: participation/models.py:939
|
||||
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:1323
|
||||
#: participation/models.py:1331
|
||||
#, 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:1334
|
||||
#: participation/models.py:1344
|
||||
#, python-brace-format
|
||||
msgid "Pool of day {round} for tournament {tournament} with teams {teams}"
|
||||
msgstr "Poule du jour {round} du tournoi {tournament} avec les équipes {teams}"
|
||||
|
||||
#: participation/models.py:1354
|
||||
#: participation/models.py:1364
|
||||
msgid "position"
|
||||
msgstr "position"
|
||||
|
||||
#: participation/models.py:1361
|
||||
#: participation/models.py:1371
|
||||
msgid "defended solution"
|
||||
msgstr "solution défendue"
|
||||
|
||||
#: participation/models.py:1394
|
||||
#: participation/models.py:1404
|
||||
msgid "observer"
|
||||
msgstr "observateur⋅rice"
|
||||
|
||||
#: participation/models.py:1399
|
||||
#: participation/models.py:1409
|
||||
msgid "penalties"
|
||||
msgstr "pénalités"
|
||||
|
||||
#: participation/models.py:1401
|
||||
#: participation/models.py:1411
|
||||
msgid ""
|
||||
"Number of penalties for the defender. The defender will loose a 0.5 "
|
||||
"coefficient per penalty."
|
||||
|
@ -1008,124 +1008,124 @@ msgstr ""
|
|||
"Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 "
|
||||
"sur sa présentation orale par pénalité."
|
||||
|
||||
#: participation/models.py:1477 participation/models.py:1480
|
||||
#: participation/models.py:1483 participation/models.py:1486
|
||||
#: participation/models.py:1487 participation/models.py:1490
|
||||
#: participation/models.py:1493 participation/models.py:1496
|
||||
#, 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:1491
|
||||
#: participation/models.py:1501
|
||||
#, python-brace-format
|
||||
msgid "Passage of {defender} for problem {problem}"
|
||||
msgstr "Passage de {defender} pour le problème {problem}"
|
||||
|
||||
#: participation/models.py:1495 participation/models.py:1504
|
||||
#: participation/models.py:1589 participation/models.py:1631
|
||||
#: participation/models.py:1505 participation/models.py:1514
|
||||
#: participation/models.py:1599 participation/models.py:1641
|
||||
msgid "passage"
|
||||
msgstr "passage"
|
||||
|
||||
#: participation/models.py:1496
|
||||
#: participation/models.py:1506
|
||||
msgid "passages"
|
||||
msgstr "passages"
|
||||
|
||||
#: participation/models.py:1515
|
||||
#: participation/models.py:1525
|
||||
msgid "difference"
|
||||
msgstr "différence"
|
||||
|
||||
#: participation/models.py:1516
|
||||
#: participation/models.py:1526
|
||||
msgid "Score to add/remove on the final score"
|
||||
msgstr "Score à ajouter/retrancher au score final"
|
||||
|
||||
#: participation/models.py:1523
|
||||
#: participation/models.py:1533
|
||||
msgid "tweak"
|
||||
msgstr "harmonisation"
|
||||
|
||||
#: participation/models.py:1524
|
||||
#: participation/models.py:1534
|
||||
msgid "tweaks"
|
||||
msgstr "harmonisations"
|
||||
|
||||
#: participation/models.py:1552
|
||||
#: participation/models.py:1562
|
||||
msgid "solution for the final tournament"
|
||||
msgstr "solution pour la finale"
|
||||
|
||||
#: participation/models.py:1557 participation/models.py:1600
|
||||
#: participation/models.py:1567 participation/models.py:1610
|
||||
msgid "file"
|
||||
msgstr "fichier"
|
||||
|
||||
#: participation/models.py:1567
|
||||
#: participation/models.py:1577
|
||||
#, 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:1569
|
||||
#: participation/models.py:1579
|
||||
msgid "for final"
|
||||
msgstr "pour la finale"
|
||||
|
||||
#: participation/models.py:1572
|
||||
#: participation/models.py:1582
|
||||
msgid "solution"
|
||||
msgstr "solution"
|
||||
|
||||
#: participation/models.py:1573
|
||||
#: participation/models.py:1583
|
||||
msgid "solutions"
|
||||
msgstr "solutions"
|
||||
|
||||
#: participation/models.py:1606
|
||||
#: participation/models.py:1616
|
||||
#, 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:1614
|
||||
#: participation/models.py:1624
|
||||
msgid "synthesis"
|
||||
msgstr "note de synthèse"
|
||||
|
||||
#: participation/models.py:1615
|
||||
#: participation/models.py:1625
|
||||
msgid "syntheses"
|
||||
msgstr "notes de synthèse"
|
||||
|
||||
#: participation/models.py:1624
|
||||
#: participation/models.py:1634
|
||||
msgid "jury"
|
||||
msgstr "jury"
|
||||
|
||||
#: participation/models.py:1636
|
||||
#: participation/models.py:1646
|
||||
msgid "defender writing note"
|
||||
msgstr "note d'écrit défenseur⋅se"
|
||||
|
||||
#: participation/models.py:1642
|
||||
#: participation/models.py:1652
|
||||
msgid "defender oral note"
|
||||
msgstr "note d'oral défenseur⋅se"
|
||||
|
||||
#: participation/models.py:1648
|
||||
#: participation/models.py:1658
|
||||
msgid "opponent writing note"
|
||||
msgstr "note d'écrit opposant⋅e"
|
||||
|
||||
#: participation/models.py:1654
|
||||
#: participation/models.py:1664
|
||||
msgid "opponent oral note"
|
||||
msgstr "note d'oral opposant⋅e"
|
||||
|
||||
#: participation/models.py:1660
|
||||
#: participation/models.py:1670
|
||||
msgid "reporter writing note"
|
||||
msgstr "note d'écrit rapporteur⋅rice"
|
||||
|
||||
#: participation/models.py:1666
|
||||
#: participation/models.py:1676
|
||||
msgid "reporter oral note"
|
||||
msgstr "note d'oral du rapporteur⋅rice"
|
||||
|
||||
#: participation/models.py:1672
|
||||
#: participation/models.py:1682
|
||||
msgid "observer note"
|
||||
msgstr "note de l'observation"
|
||||
|
||||
#: participation/models.py:1733
|
||||
#: participation/models.py:1743
|
||||
#, python-brace-format
|
||||
msgid "Notes of {jury} for {passage}"
|
||||
msgstr "Notes de {jury} pour le {passage}"
|
||||
|
||||
#: participation/models.py:1736
|
||||
#: participation/models.py:1746
|
||||
msgid "note"
|
||||
msgstr "note"
|
||||
|
||||
#: participation/models.py:1737
|
||||
#: participation/models.py:1747
|
||||
msgid "notes"
|
||||
msgstr "notes"
|
||||
|
||||
|
@ -1269,7 +1269,7 @@ msgstr "Envoyer une solution"
|
|||
#: participation/templates/participation/pool_detail.html:162
|
||||
#: participation/templates/participation/team_detail.html:210
|
||||
#: participation/templates/participation/upload_motivation_letter.html:13
|
||||
#: participation/templates/participation/upload_notes.html:25
|
||||
#: participation/templates/participation/upload_notes.html:24
|
||||
#: participation/templates/participation/upload_solution.html:11
|
||||
#: participation/templates/participation/upload_synthesis.html:18
|
||||
#: registration/templates/registration/upload_health_sheet.html:17
|
||||
|
@ -1453,8 +1453,8 @@ msgid "Ranking"
|
|||
msgstr "Classement"
|
||||
|
||||
#: participation/templates/participation/pool_detail.html:136
|
||||
msgid "Upload notes from a CSV file"
|
||||
msgstr "Soumettre les notes à partir d'un fichier CSV"
|
||||
msgid "Upload notes from a spreadsheet file"
|
||||
msgstr "Soumettre les notes à partir d'un tableur"
|
||||
|
||||
#: participation/templates/participation/pool_detail.html:140
|
||||
msgid "Download notation spreadsheet"
|
||||
|
@ -1658,7 +1658,7 @@ msgid "Invalidate"
|
|||
msgstr "Invalider"
|
||||
|
||||
#: participation/templates/participation/team_detail.html:209
|
||||
#: participation/views.py:327
|
||||
#: participation/views.py:332
|
||||
msgid "Upload motivation letter"
|
||||
msgstr "Envoyer la lettre de motivation"
|
||||
|
||||
|
@ -1667,7 +1667,7 @@ msgid "Update team"
|
|||
msgstr "Modifier l'équipe"
|
||||
|
||||
#: participation/templates/participation/team_detail.html:219
|
||||
#: participation/views.py:464
|
||||
#: participation/views.py:469
|
||||
msgid "Leave team"
|
||||
msgstr "Quitter l'équipe"
|
||||
|
||||
|
@ -1820,14 +1820,12 @@ msgstr "Retour aux détails de l'utilisateur⋅rice"
|
|||
|
||||
#: participation/templates/participation/upload_notes.html:11
|
||||
msgid ""
|
||||
"Remember to export your spreadsheet as a CSV file before uploading it here. "
|
||||
"Rows that are full of zeros are ignored. Unknown juries are not considered."
|
||||
msgstr ""
|
||||
"N'oubliez pas d'exporter votre tableur en tant que fichier CSV avant de le "
|
||||
"téléverser ici. Les lignes remplies de zéros sont ignorées. Les juré⋅es "
|
||||
"inconnu⋅es ne sont pas pris⋅es en compte."
|
||||
"Les lignes remplies de zéros sont ignorées. Les juré⋅es inconnu⋅es ne sont "
|
||||
"pas pris⋅es en compte."
|
||||
|
||||
#: participation/templates/participation/upload_notes.html:19
|
||||
#: participation/templates/participation/upload_notes.html:18
|
||||
msgid "Download empty notation sheet"
|
||||
msgstr "Télécharger la fiche de notation vierge"
|
||||
|
||||
|
@ -1839,44 +1837,44 @@ msgstr "Modèles :"
|
|||
msgid "Warning: non-free format"
|
||||
msgstr "Attention : format non libre"
|
||||
|
||||
#: participation/views.py:56 tfjm/templates/base.html:79
|
||||
#: participation/views.py:61 tfjm/templates/base.html:79
|
||||
#: tfjm/templates/navbar.html:35
|
||||
msgid "Create team"
|
||||
msgstr "Créer une équipe"
|
||||
|
||||
#: participation/views.py:65 participation/views.py:106
|
||||
#: participation/views.py:70 participation/views.py:111
|
||||
msgid "You don't participate, so you can't create a team."
|
||||
msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe."
|
||||
|
||||
#: participation/views.py:67 participation/views.py:108
|
||||
#: participation/views.py:72 participation/views.py:113
|
||||
msgid "You are already in a team."
|
||||
msgstr "Vous êtes déjà dans une équipe."
|
||||
|
||||
#: participation/views.py:97 tfjm/templates/base.html:74
|
||||
#: participation/views.py:102 tfjm/templates/base.html:74
|
||||
#: tfjm/templates/navbar.html:40
|
||||
msgid "Join team"
|
||||
msgstr "Rejoindre une équipe"
|
||||
|
||||
#: participation/views.py:157 participation/views.py:502
|
||||
#: participation/views.py:162 participation/views.py:507
|
||||
msgid "You don't participate, so you don't have any team."
|
||||
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
|
||||
|
||||
#: participation/views.py:183
|
||||
#: participation/views.py:188
|
||||
#, python-brace-format
|
||||
msgid "Detail of team {trigram}"
|
||||
msgstr "Détails de l'équipe {trigram}"
|
||||
|
||||
#: participation/views.py:212
|
||||
#: participation/views.py:217
|
||||
msgid "You don't participate, so you can't request the validation of the team."
|
||||
msgstr ""
|
||||
"Vous ne participez pas, vous ne pouvez pas demander la validation de "
|
||||
"l'équipe."
|
||||
|
||||
#: participation/views.py:215
|
||||
#: participation/views.py:220
|
||||
msgid "The validation of the team is already done or pending."
|
||||
msgstr "La validation de l'équipe est déjà faite ou en cours."
|
||||
|
||||
#: participation/views.py:218
|
||||
#: participation/views.py:223
|
||||
msgid ""
|
||||
"The team can't be validated: missing email address confirmations, "
|
||||
"authorizations, people, motivation letter or the tournament is not set."
|
||||
|
@ -1885,167 +1883,167 @@ msgstr ""
|
|||
"d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre "
|
||||
"de motivation, soit le tournoi n'a pas été choisi."
|
||||
|
||||
#: participation/views.py:240
|
||||
#: participation/views.py:245
|
||||
msgid "You are not an organizer of the tournament."
|
||||
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi."
|
||||
|
||||
#: participation/views.py:243
|
||||
#: participation/views.py:248
|
||||
msgid "This team has no pending validation."
|
||||
msgstr "L'équipe n'a pas de validation en attente."
|
||||
|
||||
#: participation/views.py:270
|
||||
#: participation/views.py:275
|
||||
msgid "You must specify if you validate the registration or not."
|
||||
msgstr "Vous devez spécifier si vous validez l'inscription ou non."
|
||||
|
||||
#: participation/views.py:305
|
||||
#: participation/views.py:310
|
||||
#, python-brace-format
|
||||
msgid "Update team {trigram}"
|
||||
msgstr "Mise à jour de l'équipe {trigram}"
|
||||
|
||||
#: participation/views.py:366 participation/views.py:449
|
||||
#: participation/views.py:371 participation/views.py:454
|
||||
#, python-brace-format
|
||||
msgid "Motivation letter of {team}.{ext}"
|
||||
msgstr "Lettre de motivation de {team}.{ext}"
|
||||
|
||||
#: participation/views.py:398
|
||||
#: participation/views.py:403
|
||||
#, python-brace-format
|
||||
msgid "Authorizations of team {trigram}.zip"
|
||||
msgstr "Autorisations de l'équipe {trigram}.zip"
|
||||
|
||||
#: participation/views.py:402
|
||||
#: participation/views.py:407
|
||||
#, python-brace-format
|
||||
msgid "Authorizations of {tournament}.zip"
|
||||
msgstr "Autorisations du tournoi {tournament}.zip"
|
||||
|
||||
#: participation/views.py:418
|
||||
#: participation/views.py:423
|
||||
#, python-brace-format
|
||||
msgid "Photo authorization of {participant}.{ext}"
|
||||
msgstr "Autorisation de droit à l'image de {participant}.{ext}"
|
||||
|
||||
#: participation/views.py:426
|
||||
#: participation/views.py:431
|
||||
#, python-brace-format
|
||||
msgid "Parental authorization of {participant}.{ext}"
|
||||
msgstr "Autorisation parentale de {participant}.{ext}"
|
||||
|
||||
#: participation/views.py:434
|
||||
#: participation/views.py:439
|
||||
#, python-brace-format
|
||||
msgid "Health sheet of {participant}.{ext}"
|
||||
msgstr "Fiche sanitaire de {participant}.{ext}"
|
||||
|
||||
#: participation/views.py:442
|
||||
#: participation/views.py:447
|
||||
#, python-brace-format
|
||||
msgid "Vaccine sheet of {participant}.{ext}"
|
||||
msgstr "Carnet de vaccination de {participant}.{ext}"
|
||||
|
||||
#: participation/views.py:516
|
||||
#: participation/views.py:521
|
||||
msgid "The team is not validated yet."
|
||||
msgstr "L'équipe n'est pas encore validée."
|
||||
|
||||
#: participation/views.py:530
|
||||
#: participation/views.py:535
|
||||
#, python-brace-format
|
||||
msgid "Participation of team {trigram}"
|
||||
msgstr "Participation de l'équipe {trigram}"
|
||||
|
||||
#: participation/views.py:612
|
||||
#: participation/views.py:617
|
||||
#, python-brace-format
|
||||
msgid "Payments of {tournament}"
|
||||
msgstr "Paiements de {tournament}"
|
||||
|
||||
#: participation/views.py:708
|
||||
#: participation/views.py:713
|
||||
msgid "Notes published!"
|
||||
msgstr "Notes publiées !"
|
||||
|
||||
#: participation/views.py:710
|
||||
#: participation/views.py:715
|
||||
msgid "Notes hidden!"
|
||||
msgstr "Notes dissimulées !"
|
||||
|
||||
#: participation/views.py:741
|
||||
#: participation/views.py:746
|
||||
#, python-brace-format
|
||||
msgid "Harmonize notes of {tournament} - Day {round}"
|
||||
msgstr "Harmoniser les notes de {tournament} - Jour {round}"
|
||||
|
||||
#: participation/views.py:822
|
||||
#: participation/views.py:827
|
||||
msgid "You can't upload a solution after the deadline."
|
||||
msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
|
||||
|
||||
#: participation/views.py:939
|
||||
#: participation/views.py:944
|
||||
#, python-brace-format
|
||||
msgid "Solutions of team {trigram}.zip"
|
||||
msgstr "Solutions de l'équipe {trigram}.zip"
|
||||
|
||||
#: participation/views.py:939
|
||||
#: participation/views.py:944
|
||||
#, python-brace-format
|
||||
msgid "Syntheses of team {trigram}.zip"
|
||||
msgstr "Notes de synthèse de l'équipe {trigram}.zip"
|
||||
|
||||
#: participation/views.py:956 participation/views.py:971
|
||||
#: participation/views.py:961 participation/views.py:976
|
||||
#, python-brace-format
|
||||
msgid "Solutions of {tournament}.zip"
|
||||
msgstr "Solutions de {tournament}.zip"
|
||||
|
||||
#: participation/views.py:956 participation/views.py:971
|
||||
#: participation/views.py:961 participation/views.py:976
|
||||
#, python-brace-format
|
||||
msgid "Syntheses of {tournament}.zip"
|
||||
msgstr "Notes de synthèse de {tournament}.zip"
|
||||
|
||||
#: participation/views.py:980
|
||||
#: participation/views.py:985
|
||||
#, python-brace-format
|
||||
msgid "Solutions for pool {pool} of tournament {tournament}.zip"
|
||||
msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip"
|
||||
|
||||
#: participation/views.py:981
|
||||
#: participation/views.py:986
|
||||
#, python-brace-format
|
||||
msgid "Syntheses for pool {pool} of tournament {tournament}.zip"
|
||||
msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip"
|
||||
|
||||
#: participation/views.py:1023
|
||||
#: participation/views.py:1028
|
||||
#, python-brace-format
|
||||
msgid "Jury of pool {pool} for {tournament} with teams {teams}"
|
||||
msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
|
||||
|
||||
#: participation/views.py:1039
|
||||
#: participation/views.py:1044
|
||||
#, python-brace-format
|
||||
msgid "The jury {name} is already in the pool!"
|
||||
msgstr "{name} est déjà dans la poule !"
|
||||
|
||||
#: participation/views.py:1059
|
||||
#: participation/views.py:1064
|
||||
msgid "New TFJM² jury account"
|
||||
msgstr "Nouveau compte de juré⋅e pour le TFJM²"
|
||||
|
||||
#: participation/views.py:1080
|
||||
#: participation/views.py:1085
|
||||
#, python-brace-format
|
||||
msgid "The jury {name} has been successfully added!"
|
||||
msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !"
|
||||
|
||||
#: participation/views.py:1116
|
||||
#: participation/views.py:1121
|
||||
#, python-brace-format
|
||||
msgid "The jury {name} has been successfully removed!"
|
||||
msgstr "{name} a été retiré⋅e avec succès du jury !"
|
||||
|
||||
#: participation/views.py:1142
|
||||
#: participation/views.py:1147
|
||||
#, python-brace-format
|
||||
msgid "The jury {name} has been successfully promoted president!"
|
||||
msgstr "{name} a été nommé⋅e président⋅e du jury !"
|
||||
|
||||
#: participation/views.py:1170
|
||||
#: participation/views.py:1175
|
||||
msgid "The following user is not registered as a jury:"
|
||||
msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :"
|
||||
|
||||
#: participation/views.py:1187
|
||||
#: participation/views.py:1192
|
||||
msgid "Notes were successfully uploaded."
|
||||
msgstr "Les notes ont bien été envoyées."
|
||||
|
||||
#: participation/views.py:1820
|
||||
#: participation/views.py:1825
|
||||
#, 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:1825
|
||||
#: participation/views.py:1830
|
||||
#, python-brace-format
|
||||
msgid "Notation sheets of {tournament}.zip"
|
||||
msgstr "Feuilles de notation de {tournament}.zip"
|
||||
|
||||
#: participation/views.py:1976
|
||||
#: participation/views.py:2007
|
||||
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."
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import csv
|
||||
import math
|
||||
from io import StringIO
|
||||
import re
|
||||
from typing import Iterable
|
||||
|
@ -13,6 +14,7 @@ from django.contrib.auth.models import User
|
|||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import FileExtensionValidator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import pandas
|
||||
from pypdf import PdfReader
|
||||
from registration.models import VolunteerRegistration
|
||||
|
||||
|
@ -241,50 +243,50 @@ class AddJuryForm(forms.ModelForm):
|
|||
|
||||
class UploadNotesForm(forms.Form):
|
||||
file = forms.FileField(
|
||||
label=_("CSV file:"),
|
||||
validators=[FileExtensionValidator(allowed_extensions=["csv"])],
|
||||
label=_("Spreadsheet file:"),
|
||||
validators=[FileExtensionValidator(allowed_extensions=["csv", "ods"])],
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['file'].widget.attrs['accept'] = 'text/csv'
|
||||
self.fields['file'].widget.attrs['accept'] = 'text/csv,application/vnd.oasis.opendocument.spreadsheet'
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if 'file' in cleaned_data:
|
||||
file = cleaned_data['file']
|
||||
with file:
|
||||
try:
|
||||
data: bytes = file.read()
|
||||
if file.name.endswith('.csv'):
|
||||
with file:
|
||||
try:
|
||||
content = data.decode()
|
||||
except UnicodeDecodeError:
|
||||
# This is not UTF-8, grrrr
|
||||
content = data.decode('latin1')
|
||||
for delimiter in [',', ';', '\t', '|']:
|
||||
if content.split('\n')[0].count(delimiter) > 1:
|
||||
break
|
||||
else:
|
||||
self.add_error('file',
|
||||
_("Unable to detect the CSV delimiter. Please use a comma-separated file."))
|
||||
return cleaned_data
|
||||
data: bytes = file.read()
|
||||
try:
|
||||
content = data.decode()
|
||||
except UnicodeDecodeError:
|
||||
# This is not UTF-8, grrrr
|
||||
content = data.decode('latin1')
|
||||
|
||||
csvfile = csv.reader(StringIO(content), delimiter=delimiter)
|
||||
self.process(csvfile, cleaned_data)
|
||||
except UnicodeDecodeError:
|
||||
self.add_error('file', _("This file contains non-UTF-8 and non-ISO-8859-1 content. "
|
||||
"Please send your sheet as a CSV file."))
|
||||
table = pandas.read_csv(StringIO(content), sep=None, header=None)
|
||||
self.process(table, cleaned_data)
|
||||
except UnicodeDecodeError:
|
||||
self.add_error('file', _("This file contains non-UTF-8 and non-ISO-8859-1 content. "
|
||||
"Please send your sheet as a CSV file."))
|
||||
elif file.name.endswith('.ods'):
|
||||
table = pandas.read_excel(file, header=None, engine='odf')
|
||||
self.process(table, cleaned_data)
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def process(self, csvfile: Iterable[str], cleaned_data: dict):
|
||||
def process(self, df: pandas.DataFrame, cleaned_data: dict):
|
||||
parsed_notes = {}
|
||||
valid_lengths = [2 + 6 * 3, 2 + 7 * 4, 2 + 6 * 5] # Per pool sizes
|
||||
pool_size = 0
|
||||
line_length = 0
|
||||
for line in csvfile:
|
||||
line = [s.strip() for s in line if s]
|
||||
for line in df.values.tolist():
|
||||
# Remove NaN
|
||||
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':
|
||||
pool_size = len(line) - 1
|
||||
if pool_size < 3 or pool_size > 5:
|
||||
|
@ -297,12 +299,12 @@ class UploadNotesForm(forms.Form):
|
|||
continue
|
||||
|
||||
name = line[0]
|
||||
if name.lower() in ["rôle", "juré", "moyenne", "coefficient", "sous-total", "équipe", "equipe"]:
|
||||
if name.lower() in ["rôle", "juré⋅e", "juré?e", "moyenne", "coefficient", "sous-total", "équipe", "equipe"]:
|
||||
continue
|
||||
notes = line[2:line_length]
|
||||
if not all(s.isnumeric() or s[0] == '-' and s[1:].isnumeric() for s in notes):
|
||||
continue
|
||||
notes = list(map(int, notes))
|
||||
notes = list(map(lambda x: int(float(x)), notes))
|
||||
|
||||
max_notes = pool_size * ([20, 20, 10, 10, 10, 10] + ([4] if pool_size == 4 else []))
|
||||
for n, max_n in zip(notes, max_notes):
|
||||
|
@ -312,7 +314,7 @@ class UploadNotesForm(forms.Form):
|
|||
+ str(n) + " > " + str(max_n))
|
||||
|
||||
# Search by volunteer id
|
||||
jury = VolunteerRegistration.objects.filter(pk=line[1])
|
||||
jury = VolunteerRegistration.objects.filter(pk=int(float(line[1])))
|
||||
if jury.count() != 1:
|
||||
raise ValidationError({'file': _("The following user was not found:") + " " + name})
|
||||
jury = jury.get()
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
<div class="btn btn-group">
|
||||
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#uploadNotesModal">
|
||||
<i class="fas fa-upload"></i>
|
||||
{% trans "Upload notes from a CSV file" %}
|
||||
{% trans "Upload notes from a spreadsheet file" %}
|
||||
</button>
|
||||
<a class="btn btn-sm btn-info" href="{% url 'participation:pool_notes_template' pk=pool.pk %}">
|
||||
<i class="fas fa-download"></i>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<div class="alert alert-warning">
|
||||
{% url 'participation:pool_jury' pk=pool.jury as jury_url %}
|
||||
{% blocktrans trimmed with jury_url=jury_url %}
|
||||
Remember to export your spreadsheet as a CSV file before uploading it here.
|
||||
Rows that are full of zeros are ignored.
|
||||
Unknown juries are not considered.
|
||||
{% endblocktrans %}
|
||||
|
|
|
@ -13,12 +13,10 @@ django-polymorphic~=3.1.0
|
|||
django-tables2~=2.7.0
|
||||
djangorestframework~=3.14.0
|
||||
django-rest-polymorphic~=0.1.10
|
||||
google-api-python-client~=2.124.0
|
||||
google-auth-httplib2~=0.2.0
|
||||
google-auth-oauthlib~=1.2.0
|
||||
gspread~=6.1.0
|
||||
gunicorn~=21.2.0
|
||||
odfpy~=1.4.1
|
||||
pandas~=2.2.1
|
||||
phonenumbers~=8.13.27
|
||||
psycopg2-binary~=2.9.9
|
||||
pypdf~=3.17.4
|
||||
|
|
Loading…
Reference in New Issue