diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 25a21c9..0c2bda6 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 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 \n" "Language-Team: LANGUAGE \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 "" "

The team {trigram} has {nb_missing_payments} missing payments. Each " @@ -831,11 +831,11 @@ msgstr "" "notification de bourse) pour participer au tournoi.

Les participant⋅es " "qui n'ont pas encore payé sont : {participants}.

" -#: participation/models.py:707 +#: participation/models.py:715 msgid "Missing payments" msgstr "Paiements manquants" -#: participation/models.py:714 +#: participation/models.py:722 msgid "" "

The solutions for the tournament of {tournament} are due on the {date:%Y-" "%m-%d %H:%M}.

You have currently sent {nb_solutions} " @@ -850,11 +850,11 @@ msgstr "" "pouvez envoyer vos solutions sur votre page de " "participation.

" -#: 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 "" "

The draw of the solutions for the tournament {tournament} is planned on " "the {date:%Y-%m-%d %H:%M}. You can join it on this link." @@ -864,11 +864,11 @@ msgstr "" "{date:%d/%m/%Y %H:%M}. Vous pouvez y participer sur ce lien.

" -#: 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 "" "

The solutions draw is ended. You can check the result on votre solution du problème " "{problem}.

" -#: participation/models.py:755 participation/models.py:798 +#: participation/models.py:763 participation/models.py:806 #, python-brace-format msgid "" "

You will oppose the solution of the team {opponent} on the problème {problem}. Vous pouvez envoyer votre note " "de synthèse sur cette page.

" -#: participation/models.py:764 participation/models.py:807 +#: participation/models.py:772 participation/models.py:815 #, python-brace-format msgid "" "

You will report the solution of the team {reporter} on the problème {problem}. Vous pouvez envoyer votre note " "de synthèse sur cette page.

" -#: 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 "" "

For the second round, you will defend your " @@ -915,11 +915,11 @@ msgstr "" "

Pour le second tour, vous défendrez votre " "solution du problème {problem}.

" -#: 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 "" "

The tournament {tournament} is ended. You can check the results on the Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats " "sur la page du tournoi.

" -#: 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." diff --git a/participation/forms.py b/participation/forms.py index c518001..08b0aed 100644 --- a/participation/forms.py +++ b/participation/forms.py @@ -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() diff --git a/participation/templates/participation/pool_detail.html b/participation/templates/participation/pool_detail.html index 8b8f449..c94cb2a 100644 --- a/participation/templates/participation/pool_detail.html +++ b/participation/templates/participation/pool_detail.html @@ -133,7 +133,7 @@