From 05a6570bed76e148f3530eb58efcfa08c7d47393 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Fri, 5 Jul 2024 11:47:19 +0200 Subject: [PATCH] Add observer team Signed-off-by: Emmy D'Anello --- draw/models.py | 32 +- locale/fr/LC_MESSAGES/django.po | 364 ++++++++++-------- participation/admin.py | 15 +- participation/api/views.py | 4 +- participation/forms.py | 4 +- ...ver_oral_note_observer_writing_and_more.py | 86 +++++ participation/models.py | 125 +++++- participation/tables.py | 9 +- .../participation/passage_detail.html | 30 +- participation/views.py | 8 +- 10 files changed, 486 insertions(+), 191 deletions(-) create mode 100644 participation/migrations/0019_note_observer_oral_note_observer_writing_and_more.py diff --git a/draw/models.py b/draw/models.py index 179c7a2..5c021d2 100644 --- a/draw/models.py +++ b/draw/models.py @@ -5,6 +5,7 @@ import os from asgiref.sync import sync_to_async from django.conf import settings +from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.db.models import QuerySet @@ -199,7 +200,7 @@ class Round(models.Model): (3, _('Round 3'))], verbose_name=_('number'), help_text=_("The number of the round, 1 or 2 (or 3 for ETEAM)"), - validators=[MinValueValidator(1), MaxValueValidator(settings.NB_ROUNDS)], + validators=[MinValueValidator(1), MaxValueValidator(3)], ) current_pool = models.ForeignKey( @@ -233,6 +234,13 @@ class Round(models.Model): def __str__(self): return self.get_number_display() + def clean(self): + if self.number is not None and self.number > settings.NB_ROUNDS: + raise ValidationError({'number': _("The number of the round must be between 1 and {nb}.") + .format(nb=settings.NB_ROUNDS)}) + + return super().clean() + class Meta: verbose_name = _('round') verbose_name_plural = _('rounds') @@ -392,11 +400,11 @@ class Pool(models.Model): ] elif self.size == 5: table = [ - [0, 2, 3], - [1, 3, 4], - [2, 4, 0], - [3, 0, 1], - [4, 1, 2], + [0, 2, 3, 4], + [1, 3, 4, 0], + [2, 4, 0, 1], + [3, 0, 1, 2], + [4, 1, 2, 3], ] for i, line in enumerate(table): @@ -408,14 +416,20 @@ class Pool(models.Model): passage_pool = pool2 passage_position = 1 + i // 2 + defender = tds[line[0]].participation + opponent = tds[line[1]].participation + reviewer = tds[line[2]].participation + observer = tds[line[3]].participation if self.size >= 4 and settings.TFJM_APP == "ETEAM" else None + # Create the passage await Passage.objects.acreate( pool=passage_pool, position=passage_position, solution_number=tds[line[0]].accepted, - defender=tds[line[0]].participation, - opponent=tds[line[1]].participation, - reviewer=tds[line[2]].participation, + defender=defender, + opponent=opponent, + reviewer=reviewer, + observer=observer, defender_penalties=tds[line[0]].penalty_int, ) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 9e70a0d..da3f4e5 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-07-05 10:48+0200\n" +"POT-Creation-Date: 2024-07-05 11:45+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -77,9 +77,9 @@ msgid "Permission type that is required to write a message to a channel." 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:26 participation/admin.py:79 participation/admin.py:140 -#: participation/admin.py:171 participation/models.py:727 -#: participation/models.py:751 participation/models.py:1012 +#: 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 #: registration/models.py:763 #: registration/templates/registration/payment_form.html:53 msgid "tournament" @@ -93,9 +93,9 @@ msgstr "" "Pour une permission qui concerne un tournoi, indique quel est le tournoi " "concerné." -#: chat/models.py:73 draw/models.py:432 draw/models.py:459 -#: participation/admin.py:136 participation/admin.py:155 -#: participation/models.py:1515 participation/models.py:1524 +#: 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/tables.py:84 msgid "pool" msgstr "poule" @@ -108,7 +108,7 @@ msgstr "" "concernée." #: chat/models.py:84 draw/templates/draw/tournament_content.html:277 -#: participation/admin.py:167 participation/models.py:261 +#: participation/admin.py:172 participation/models.py:261 #: participation/models.py:742 #: participation/templates/participation/tournament_harmonize.html:15 #: registration/models.py:158 registration/models.py:754 @@ -264,8 +264,8 @@ msgstr "Connexion" msgid "teams" msgstr "équipes" -#: draw/admin.py:92 draw/models.py:237 draw/models.py:451 -#: participation/models.py:1016 +#: draw/admin.py:92 draw/models.py:245 draw/models.py:465 +#: participation/models.py:1064 msgid "round" msgstr "tour" @@ -453,31 +453,31 @@ msgstr "" msgid "The draw of the second round is starting!" msgstr "Le tirage au sort du deuxième tour commence !" -#: draw/models.py:27 +#: draw/models.py:28 msgid "The associated tournament." msgstr "Le tournoi associé." -#: draw/models.py:36 +#: draw/models.py:37 msgid "current round" msgstr "tour actuel" -#: draw/models.py:37 +#: draw/models.py:38 msgid "The current round where teams select their problems." msgstr "Le tour en cours où les équipes choisissent leurs problèmes." -#: draw/models.py:43 +#: draw/models.py:44 msgid "last message" msgstr "dernier message" -#: draw/models.py:44 +#: draw/models.py:45 msgid "The last message that is displayed on the drawing interface." msgstr "Le dernier message qui est affiché sur l'interface de tirage." -#: draw/models.py:94 +#: draw/models.py:95 msgid "State" msgstr "État" -#: draw/models.py:113 +#: draw/models.py:114 msgid "" "We are going to start the problem draw.
You can ask any question if " "something is not clear or wrong.

We are going to first draw the pools " @@ -490,7 +490,7 @@ msgstr "" "toutes les équipes, puis pour chaque poule, nous allons tirer l'ordre de " "tirage et les problèmes." -#: draw/models.py:118 +#: draw/models.py:119 msgid "" "The captains, you can now all throw a 100-sided dice, by clicking on the big " "dice button. The pools and the passage order during the first round will be " @@ -502,7 +502,7 @@ msgstr "" "le premier tour seront l'ordre croissant des dés, c'est-à-dire que le plus " "petit dé passera en premier dans la poule A." -#: draw/models.py:123 +#: draw/models.py:124 #, python-brace-format msgid "" "We are going to start the problem draw for the pool {pool}, " @@ -516,7 +516,7 @@ msgstr "" "déterminer l'ordre de tirage. L'équipe avec le score le plus élevé tirera en " "premier." -#: draw/models.py:133 +#: draw/models.py:134 #, python-brace-format msgid "" "The team {trigram} is going to draw a problem. Click on the " @@ -525,7 +525,7 @@ msgstr "" "L'équipe {trigram} va tirer un problème. Cliquez sur l'urne " "au milieu pour tirer un problème." -#: draw/models.py:139 +#: draw/models.py:140 #, python-brace-format msgid "" "The team {trigram} drew the problem {problem}: " @@ -534,7 +534,7 @@ msgstr "" "L'équipe {trigram} a tiré le problème {problem} : " "{problem_name}." -#: draw/models.py:145 +#: draw/models.py:146 msgid "" "It already refused this problem before, so it can refuse it without penalty " "and draw a new problem immediately, or change its mind." @@ -542,24 +542,24 @@ msgstr "" "Elle a déjà refusé ce problème auparavant, donc elle peut le refuser sans " "pénalité et tirer un nouveau problème immédiatement, ou changer d'avis." -#: draw/models.py:149 +#: draw/models.py:150 msgid "It can decide to accept or refuse this problem." msgstr "Elle peut décider d'accepter ou de refuser ce problème." -#: draw/models.py:151 +#: draw/models.py:152 msgid "" -"Refusing this problem will add a new 25% penalty on the coefficient of " -"the oral defense." +"Refusing this problem will add a new 25% penalty on the coefficient of the " +"oral defense." msgstr "" "Refuser ce problème ajoutera une nouvelle pénalité de 25nbsp;% sur le " "coefficient de l'oral de la défense." -#: draw/models.py:154 +#: draw/models.py:155 #, python-brace-format msgid "There are still {remaining} refusals without penalty." msgstr "Il reste {remaining} refus sans pénalité." -#: draw/models.py:158 +#: draw/models.py:159 msgid "" "The draw for the second round will take place at the end of the first round. " "Good luck!" @@ -567,7 +567,7 @@ msgstr "" "Le tirage au sort du deuxième tour aura lieu à la fin du premier tour. Bonne " "chance !" -#: draw/models.py:161 +#: draw/models.py:162 msgid "" "The draw is ended. The solutions of the other teams can be found in the tab " "\"My participation\"." @@ -575,7 +575,7 @@ msgstr "" "Le tirage est terminé. Les solutions des autres équipes peuvent être " "trouvées dans l'onglet « Ma participation »." -#: draw/models.py:166 +#: draw/models.py:167 #, python-brace-format msgid "" "For more details on the draw, the rules are available on {link}." -#: draw/models.py:177 +#: draw/models.py:178 #, python-brace-format msgid "Draw of tournament {tournament}" msgstr "Tirage au sort du tournoi {tournament}" -#: draw/models.py:180 draw/models.py:192 +#: draw/models.py:181 draw/models.py:193 msgid "draw" msgstr "tirage au sort" -#: draw/models.py:181 +#: draw/models.py:182 msgid "draws" msgstr "tirages au sort" -#: draw/models.py:197 +#: draw/models.py:198 msgid "Round 1" msgstr "Tour 1" -#: draw/models.py:198 +#: draw/models.py:199 msgid "Round 2" msgstr "Tour 2" -#: draw/models.py:199 +#: draw/models.py:200 msgid "Round 3" msgstr "Tour 3" -#: draw/models.py:200 +#: draw/models.py:201 msgid "number" msgstr "numéro" -#: draw/models.py:201 +#: draw/models.py:202 msgid "The number of the round, 1 or 2 (or 3 for ETEAM)" msgstr "Le numéro du tour, 1 ou 2 (ou 3 pour ETEAM)" -#: draw/models.py:211 +#: draw/models.py:212 msgid "current pool" msgstr "poule actuelle" -#: draw/models.py:212 +#: draw/models.py:213 msgid "The current pool where teams select their problems." msgstr "La poule en cours, où les équipes choisissent leurs problèmes" -#: draw/models.py:238 +#: draw/models.py:239 +#, 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}." + +#: draw/models.py:246 msgid "rounds" msgstr "tours" -#: draw/models.py:260 participation/models.py:1024 +#: draw/models.py:268 participation/models.py:1072 msgid "letter" msgstr "lettre" -#: draw/models.py:261 +#: draw/models.py:269 msgid "The letter of the pool: A, B, C or D." msgstr "La lettre de la poule : A, B, C ou D." -#: draw/models.py:265 +#: draw/models.py:273 #: participation/templates/participation/tournament_detail.html:15 msgid "size" msgstr "taille" -#: draw/models.py:267 +#: draw/models.py:275 msgid "The number of teams in this pool, between 3 and 5." msgstr "Le nombre d'équipes dans la poule, entre 3 et 5." -#: draw/models.py:276 +#: draw/models.py:284 msgid "current team" msgstr "équipe actuelle" -#: draw/models.py:277 +#: draw/models.py:285 msgid "The current team that is selecting its problem." msgstr "L'équipe qui est en train de choisir son problème." -#: draw/models.py:286 +#: draw/models.py:294 msgid "associated pool" msgstr "poule associée" -#: draw/models.py:287 +#: draw/models.py:295 msgid "The full pool instance." msgstr "L'instance complète de la poule." -#: draw/models.py:429 +#: draw/models.py:443 #, python-brace-format msgid "Pool {letter}{number}" msgstr "Poule {letter}{number}" -#: draw/models.py:433 participation/models.py:1516 +#: draw/models.py:447 participation/models.py:1564 msgid "pools" msgstr "poules" -#: draw/models.py:445 participation/models.py:1002 participation/models.py:1665 -#: participation/models.py:1695 participation/models.py:1737 +#: draw/models.py:459 participation/models.py:1050 participation/models.py:1754 +#: participation/models.py:1784 participation/models.py:1826 msgid "participation" msgstr "participation" -#: draw/models.py:466 +#: draw/models.py:480 msgid "passage index" msgstr "numéro de passage" -#: draw/models.py:467 +#: draw/models.py:481 msgid "" "The passage order in the pool, between 0 and the size of the pool minus 1." msgstr "" "L'ordre de passage dans la poule, de 0 à la taille de la poule moins 1." -#: draw/models.py:475 +#: draw/models.py:489 msgid "choose index" msgstr "numéro de choix" -#: draw/models.py:476 +#: draw/models.py:490 msgid "" "The choice order in the pool, between 0 and the size of the pool minus 1." msgstr "" "L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1." -#: draw/models.py:482 draw/models.py:505 participation/models.py:1538 -#: participation/models.py:1702 +#: draw/models.py:496 draw/models.py:519 participation/models.py:1586 +#: participation/models.py:1791 #, python-brace-format msgid "Problem #{problem}" msgstr "Problème n°{problem}" -#: draw/models.py:486 +#: draw/models.py:500 msgid "accepted problem" msgstr "problème accepté" -#: draw/models.py:493 +#: draw/models.py:507 msgid "passage dice" msgstr "dé d'ordre de passage" -#: draw/models.py:500 +#: draw/models.py:514 msgid "choice dice" msgstr "dé d'ordre de choix" -#: draw/models.py:509 +#: draw/models.py:523 msgid "purposed problem" msgstr "problème proposé" -#: draw/models.py:514 +#: draw/models.py:528 msgid "rejected problems" msgstr "problèmes rejetés" -#: draw/models.py:543 +#: draw/models.py:557 #, python-brace-format msgid "Draw of the team {trigram} for the pool {letter}{number}" msgstr "Tirage de l'équipe {trigram} pour la poule {letter}{number}" -#: draw/models.py:549 +#: draw/models.py:563 msgid "team draw" msgstr "tirage d'équipe" -#: draw/models.py:550 +#: draw/models.py:564 msgid "team draws" msgstr "tirages d'équipe" @@ -912,22 +917,27 @@ msgstr "valide" msgid "selected for final" msgstr "sélectionnée pour la finale" -#: participation/admin.py:124 participation/admin.py:183 -#: participation/models.py:1545 participation/tables.py:112 +#: participation/admin.py:124 participation/admin.py:188 +#: participation/models.py:1593 participation/tables.py:114 msgid "defender" msgstr "défenseur⋅se" -#: participation/admin.py:128 participation/models.py:1552 -#: participation/models.py:1749 +#: participation/admin.py:128 participation/models.py:1600 +#: participation/models.py:1838 msgid "opponent" msgstr "opposant⋅e" -#: participation/admin.py:132 participation/models.py:1559 -#: participation/models.py:1750 +#: participation/admin.py:132 participation/models.py:1607 +#: participation/models.py:1839 msgid "reviewer" msgstr "rapporteur⋅rice" -#: participation/admin.py:187 participation/models.py:1700 +#: participation/admin.py:136 participation/models.py:1614 +#: participation/models.py:1840 +msgid "observer" +msgstr "observateur⋅rice" + +#: participation/admin.py:192 participation/models.py:1789 msgid "problem" msgstr "numéro de problème" @@ -1369,7 +1379,7 @@ msgstr "" msgid "Draw of solutions" msgstr "Tirage au sort des solutions" -#: participation/models.py:863 +#: participation/models.py:865 #, python-brace-format msgid "" "

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

" -#: participation/models.py:872 participation/models.py:914 -#: participation/models.py:957 +#: participation/models.py:874 participation/models.py:932 +#: participation/models.py:991 #, 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:881 participation/models.py:923 -#: participation/models.py:966 +#: participation/models.py:883 participation/models.py:941 +#: participation/models.py:1000 #, python-brace-format msgid "" "

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

" -#: participation/models.py:897 registration/models.py:629 +#: participation/models.py:893 participation/models.py:951 +#: participation/models.py:1010 +#, python-brace-format +msgid "" +"

You will observe the solution of the team {observer} on the problem {problem}. You can upload your synthesis sheet " +"on this page.

" +msgstr "" +"

Vous observerez la solution de l'équipe {observer} sur le problème {problem}. Vous pouvez envoyer votre note " +"de synthèse sur cette page.

" + +#: participation/models.py:913 registration/models.py:629 msgid "First round" msgstr "Premier tour" -#: participation/models.py:907 +#: participation/models.py:925 #, python-brace-format msgid "" "

For the second round, you will defend your " @@ -1418,12 +1440,12 @@ msgstr "" "

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

" -#: participation/models.py:939 participation/models.py:982 +#: participation/models.py:971 participation/models.py:1030 #: registration/models.py:640 msgid "Second round" msgstr "Second tour" -#: participation/models.py:950 +#: participation/models.py:984 #, python-brace-format msgid "" "

For the third round, you will defend your " @@ -1432,7 +1454,7 @@ msgstr "" "

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

" -#: participation/models.py:988 +#: participation/models.py:1036 #, 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:993 +#: participation/models.py:1041 msgid "Tournament ended" msgstr "Tournoi terminé" -#: participation/models.py:1003 participation/models.py:1046 +#: participation/models.py:1051 participation/models.py:1094 msgid "participations" msgstr "participations" -#: participation/models.py:1018 participation/models.py:1019 -#: participation/models.py:1020 +#: participation/models.py:1066 participation/models.py:1067 +#: participation/models.py:1068 #, python-brace-format msgid "Round {round}" msgstr "Tour {round}" -#: participation/models.py:1034 +#: participation/models.py:1082 msgid "room" msgstr "salle" -#: participation/models.py:1036 +#: participation/models.py:1084 msgid "Room 1" msgstr "Salle 1" -#: participation/models.py:1037 +#: participation/models.py:1085 msgid "Room 2" msgstr "Salle 2" -#: participation/models.py:1040 +#: participation/models.py:1088 msgid "For 5-teams pools only" msgstr "Pour les poules de 5 équipe uniquement" -#: participation/models.py:1052 +#: participation/models.py:1100 msgid "juries" msgstr "jurys" -#: participation/models.py:1061 +#: participation/models.py:1109 msgid "president of the jury" msgstr "président⋅e du jury" -#: participation/models.py:1068 +#: participation/models.py:1116 msgid "BigBlueButton URL" msgstr "Lien BigBlueButton" -#: participation/models.py:1069 +#: participation/models.py:1117 msgid "The link of the BBB visio for this pool." msgstr "Le lien du salon BBB pour cette poule." -#: participation/models.py:1074 +#: participation/models.py:1122 msgid "results available" msgstr "résultats disponibles" -#: participation/models.py:1075 +#: participation/models.py:1123 msgid "" "Check this case when results become accessible to teams. They stay " "accessible to you. Only averages are given." @@ -1500,33 +1522,33 @@ msgstr "" "Ils restent toujours accessibles pour vous. Seules les moyennes sont " "communiquées." -#: participation/models.py:1107 +#: participation/models.py:1155 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:1496 +#: participation/models.py:1544 #, 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:1509 +#: participation/models.py:1557 #, 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:1529 +#: participation/models.py:1577 msgid "position" msgstr "position" -#: participation/models.py:1536 +#: participation/models.py:1584 msgid "defended solution" msgstr "solution défendue" -#: participation/models.py:1564 +#: participation/models.py:1622 msgid "penalties" msgstr "pénalités" -#: participation/models.py:1566 +#: participation/models.py:1624 msgid "" "Number of penalties for the defender. The defender will loose a 0.5 " "coefficient per penalty." @@ -1534,120 +1556,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:1635 participation/models.py:1638 -#: participation/models.py:1641 +#: participation/models.py:1721 participation/models.py:1724 +#: participation/models.py:1727 participation/models.py:1730 #, 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:1646 +#: participation/models.py:1735 #, python-brace-format msgid "Passage of {defender} for problem {problem}" msgstr "Passage de {defender} pour le problème {problem}" -#: participation/models.py:1650 participation/models.py:1659 -#: participation/models.py:1744 participation/models.py:1786 +#: participation/models.py:1739 participation/models.py:1748 +#: participation/models.py:1833 participation/models.py:1876 msgid "passage" msgstr "passage" -#: participation/models.py:1651 +#: participation/models.py:1740 msgid "passages" msgstr "passages" -#: participation/models.py:1670 +#: participation/models.py:1759 msgid "difference" msgstr "différence" -#: participation/models.py:1671 +#: participation/models.py:1760 msgid "Score to add/remove on the final score" msgstr "Score à ajouter/retrancher au score final" -#: participation/models.py:1678 +#: participation/models.py:1767 msgid "tweak" msgstr "harmonisation" -#: participation/models.py:1679 +#: participation/models.py:1768 msgid "tweaks" msgstr "harmonisations" -#: participation/models.py:1707 +#: participation/models.py:1796 msgid "solution for the final tournament" msgstr "solution pour la finale" -#: participation/models.py:1712 participation/models.py:1755 +#: participation/models.py:1801 participation/models.py:1845 msgid "file" msgstr "fichier" -#: participation/models.py:1722 +#: participation/models.py:1811 #, 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:1724 +#: participation/models.py:1813 msgid "for final" msgstr "pour la finale" -#: participation/models.py:1727 +#: participation/models.py:1816 msgid "solution" msgstr "solution" -#: participation/models.py:1728 +#: participation/models.py:1817 msgid "solutions" msgstr "solutions" -#: participation/models.py:1761 +#: participation/models.py:1851 #, 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:1769 +#: participation/models.py:1859 msgid "synthesis" msgstr "note de synthèse" -#: participation/models.py:1770 +#: participation/models.py:1860 msgid "syntheses" msgstr "notes de synthèse" -#: participation/models.py:1779 +#: participation/models.py:1869 msgid "jury" msgstr "jury" -#: participation/models.py:1791 +#: participation/models.py:1881 msgid "defender writing note" msgstr "note d'écrit défenseur⋅se" -#: participation/models.py:1797 +#: participation/models.py:1887 msgid "defender oral note" msgstr "note d'oral défenseur⋅se" -#: participation/models.py:1803 +#: participation/models.py:1893 msgid "opponent writing note" msgstr "note d'écrit opposant⋅e" -#: participation/models.py:1809 +#: participation/models.py:1899 msgid "opponent oral note" msgstr "note d'oral opposant⋅e" -#: participation/models.py:1815 +#: participation/models.py:1905 msgid "reviewer writing note" msgstr "note d'écrit rapporteur⋅rice" -#: participation/models.py:1821 +#: participation/models.py:1911 msgid "reviewer oral note" msgstr "note d'oral du rapporteur⋅rice" -#: participation/models.py:1881 +#: participation/models.py:1917 +msgid "observer writing note" +msgstr "note d'écrit de l'observateur⋅rice" + +#: participation/models.py:1923 +msgid "observer oral note" +msgstr "note d'oral de l'observateur⋅rice" + +#: participation/models.py:1988 #, python-brace-format msgid "Notes of {jury} for {passage}" msgstr "Notes de {jury} pour le {passage}" -#: participation/models.py:1884 +#: participation/models.py:1991 msgid "note" msgstr "note" -#: participation/models.py:1885 +#: participation/models.py:1992 msgid "notes" msgstr "notes" @@ -1685,11 +1715,11 @@ msgstr "Poule {code}" msgid "No defined team" msgstr "Pas d'équipe définie" -#: participation/tables.py:142 +#: participation/tables.py:147 #: participation/templates/participation/note_form.html:14 #: participation/templates/participation/passage_detail.html:15 -#: participation/templates/participation/passage_detail.html:140 -#: participation/templates/participation/passage_detail.html:146 +#: participation/templates/participation/passage_detail.html:168 +#: participation/templates/participation/passage_detail.html:174 #: participation/templates/participation/pool_detail.html:13 #: participation/templates/participation/pool_detail.html:152 #: participation/templates/participation/team_detail.html:185 @@ -1777,7 +1807,7 @@ msgid "Upload solution" msgstr "Envoyer une solution" #: participation/templates/participation/participation_detail.html:65 -#: participation/templates/participation/passage_detail.html:152 +#: participation/templates/participation/passage_detail.html:180 #: participation/templates/participation/pool_detail.html:157 #: participation/templates/participation/team_detail.html:245 #: participation/templates/participation/upload_motivation_letter.html:13 @@ -1814,78 +1844,94 @@ msgid "Opponent:" msgstr "Opposant⋅e :" #: participation/templates/participation/passage_detail.html:34 -msgid "reviewer:" +msgid "Reviewer:" msgstr "Rapporteur⋅rice :" -#: participation/templates/participation/passage_detail.html:37 +#: participation/templates/participation/passage_detail.html:38 +msgid "Observer:" +msgstr "Observateur⋅rice :" + +#: participation/templates/participation/passage_detail.html:42 msgid "Defended solution:" msgstr "Solution défendue" -#: participation/templates/participation/passage_detail.html:40 +#: participation/templates/participation/passage_detail.html:45 msgid "Defender penalties count:" msgstr "Nombre de pénalités :" -#: participation/templates/participation/passage_detail.html:43 +#: participation/templates/participation/passage_detail.html:48 #: participation/templates/participation/pool_detail.html:59 msgid "Syntheses:" msgstr "Notes de synthèse :" -#: participation/templates/participation/passage_detail.html:48 +#: participation/templates/participation/passage_detail.html:53 #: participation/templates/participation/pool_detail.html:68 msgid "No synthesis was uploaded yet." msgstr "Aucune note de synthèse n'a encore été envoyée." -#: participation/templates/participation/passage_detail.html:56 -#: participation/templates/participation/passage_detail.html:145 +#: participation/templates/participation/passage_detail.html:61 +#: participation/templates/participation/passage_detail.html:173 msgid "Update notes" msgstr "Modifier les notes" -#: participation/templates/participation/passage_detail.html:61 -#: participation/templates/participation/passage_detail.html:151 +#: participation/templates/participation/passage_detail.html:66 +#: participation/templates/participation/passage_detail.html:179 msgid "Upload synthesis" msgstr "Envoyer une note de synthèse" -#: participation/templates/participation/passage_detail.html:69 +#: participation/templates/participation/passage_detail.html:74 msgid "Notes detail" msgstr "Détails des notes" -#: participation/templates/participation/passage_detail.html:77 +#: participation/templates/participation/passage_detail.html:82 msgid "Average points for the defender writing" msgstr "Moyenne de l'écrit de l'équipe défenseuse" -#: participation/templates/participation/passage_detail.html:83 +#: participation/templates/participation/passage_detail.html:88 msgid "Average points for the defender oral" msgstr "Moyenne de l'oral de l'équipe défenseuse" -#: participation/templates/participation/passage_detail.html:89 +#: participation/templates/participation/passage_detail.html:94 msgid "Average points for the opponent writing" msgstr "Moyenne de l'écrit de l'équipe opposante" -#: participation/templates/participation/passage_detail.html:95 +#: participation/templates/participation/passage_detail.html:100 msgid "Average points for the opponent oral" msgstr "Moyenne de l'oral de l'équipe opposante" -#: participation/templates/participation/passage_detail.html:101 +#: participation/templates/participation/passage_detail.html:106 msgid "Average points for the reviewer writing" msgstr "Moyenne de l'écrit de l'équipe rapportrice" -#: participation/templates/participation/passage_detail.html:107 +#: participation/templates/participation/passage_detail.html:112 msgid "Average points for the reviewer oral" msgstr "Moyenne de l'oral de l'équipe rapportrice" -#: participation/templates/participation/passage_detail.html:117 +#: participation/templates/participation/passage_detail.html:119 +msgid "Average points for the observer writing" +msgstr "Moyenne de l'écrit de l'équipe observatrice" + +#: participation/templates/participation/passage_detail.html:125 +msgid "Average points for the observer oral" +msgstr "Moyenne de l'oral de l'équipe observatrice" + +#: participation/templates/participation/passage_detail.html:136 msgid "Defender points" msgstr "Points de l'équipe défenseuse" -#: participation/templates/participation/passage_detail.html:123 +#: participation/templates/participation/passage_detail.html:142 msgid "Opponent points" msgstr "Points de l'équipe opposante" -#: participation/templates/participation/passage_detail.html:129 +#: participation/templates/participation/passage_detail.html:148 msgid "reviewer points" msgstr "Points de l'équipe rapportrice" -#: participation/templates/participation/passage_detail.html:139 +#: participation/templates/participation/passage_detail.html:155 +msgid "observer points" +msgstr "Points de l'équipe observatrice" + +#: participation/templates/participation/passage_detail.html:167 #: participation/templates/participation/passage_form.html:11 msgid "Update passage" msgstr "Modifier le passage" @@ -2579,7 +2625,7 @@ msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip" msgid "Notation sheets of {tournament}.zip" msgstr "Feuilles de notation de {tournament}.zip" -#: participation/views.py:2015 +#: participation/views.py:2017 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/admin.py b/participation/admin.py index 895ef6f..ca73ee3 100644 --- a/participation/admin.py +++ b/participation/admin.py @@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline): model = Passage extra = 0 ordering = ('position',) - autocomplete_fields = ('defender', 'opponent', 'reviewer',) + autocomplete_fields = ('defender', 'opponent', 'reviewer', 'observer',) show_change_link = True @@ -114,11 +114,11 @@ class PoolAdmin(admin.ModelAdmin): @admin.register(Passage) class PassageAdmin(admin.ModelAdmin): list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reviewer_trigram', - 'pool_abbr', 'position', 'tournament') + 'observer_trigram', 'pool_abbr', 'position', 'tournament') list_filter = ('pool__tournament', 'pool__round', 'pool__letter', 'solution_number',) search_fields = ('pool__participations__team__name', 'pool__participations__team__trigram',) ordering = ('pool__tournament', 'pool__round', 'pool__letter', 'position',) - autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer',) + autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer', 'observer',) inlines = (NoteInline,) @admin.display(description=_("defender"), ordering='defender__team__trigram') @@ -133,6 +133,10 @@ class PassageAdmin(admin.ModelAdmin): def reviewer_trigram(self, record: Passage): return record.reviewer.team.trigram + @admin.display(description=_("observer"), ordering='observer__team__trigram') + def observer_trigram(self, record: Passage): + return record.observer.team.trigram + @admin.display(description=_("pool"), ordering='pool__letter') def pool_abbr(self, record): return f"{record.pool.short_name}" @@ -145,10 +149,11 @@ class PassageAdmin(admin.ModelAdmin): @admin.register(Note) class NoteAdmin(admin.ModelAdmin): list_display = ('passage', 'pool', 'jury', 'defender_writing', 'defender_oral', - 'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',) + 'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral', + 'observer_writing', 'observer_oral',) list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral', - 'reviewer_writing', 'reviewer_oral') + 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral') search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__defender__team__trigram',) autocomplete_fields = ('jury', 'passage',) diff --git a/participation/api/views.py b/participation/api/views.py index 69b7a0b..923d29d 100644 --- a/participation/api/views.py +++ b/participation/api/views.py @@ -13,7 +13,7 @@ class NoteViewSet(ModelViewSet): serializer_class = NoteSerializer filter_backends = [DjangoFilterBackend] filterset_fields = ['jury', 'passage', 'defender_writing', 'defender_oral', 'opponent_writing', - 'opponent_oral', 'reviewer_writing', 'reviewer_oral', ] + 'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', ] class ParticipationViewSet(ModelViewSet): @@ -27,7 +27,7 @@ class PassageViewSet(ModelViewSet): queryset = Passage.objects.all() serializer_class = PassageSerializer filter_backends = [DjangoFilterBackend] - filterset_fields = ['pool', 'solution_number', 'defender', 'opponent', 'reviewer', 'pool_tournament', ] + filterset_fields = ['pool', 'solution_number', 'defender', 'opponent', 'reviewer', 'observer', 'pool_tournament', ] class PoolViewSet(ModelViewSet): diff --git a/participation/forms.py b/participation/forms.py index 4e1325c..e01093c 100644 --- a/participation/forms.py +++ b/participation/forms.py @@ -355,7 +355,7 @@ class PassageForm(forms.ModelForm): class Meta: model = Passage - fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'defender_penalties',) + fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',) class SynthesisForm(forms.ModelForm): @@ -386,4 +386,4 @@ class NoteForm(forms.ModelForm): class Meta: model = Note fields = ('defender_writing', 'defender_oral', 'opponent_writing', - 'opponent_oral', 'reviewer_writing', 'reviewer_oral', ) + 'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', ) diff --git a/participation/migrations/0019_note_observer_oral_note_observer_writing_and_more.py b/participation/migrations/0019_note_observer_oral_note_observer_writing_and_more.py new file mode 100644 index 0000000..20c2c98 --- /dev/null +++ b/participation/migrations/0019_note_observer_oral_note_observer_writing_and_more.py @@ -0,0 +1,86 @@ +# Generated by Django 5.0.6 on 2024-07-05 09:47 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("participation", "0018_rename_reporter_to_reviewer"), + ] + + operations = [ + migrations.AddField( + model_name="note", + name="observer_oral", + field=models.PositiveSmallIntegerField( + choices=[ + (-10, -10), + (-9, -9), + (-8, -8), + (-7, -7), + (-6, -6), + (-5, -5), + (-4, -4), + (-3, -3), + (-2, -2), + (-1, -1), + (0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + ], + default=0, + verbose_name="observer oral note", + ), + ), + migrations.AddField( + model_name="note", + name="observer_writing", + field=models.PositiveSmallIntegerField( + choices=[ + (0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + ], + default=0, + verbose_name="observer writing note", + ), + ), + migrations.AddField( + model_name="passage", + name="observer", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="participation.participation", + verbose_name="observer", + ), + ), + migrations.AlterField( + model_name="synthesis", + name="type", + field=models.PositiveSmallIntegerField( + choices=[(1, "opponent"), (2, "reviewer"), (3, "observer")] + ), + ), + ] diff --git a/participation/models.py b/participation/models.py index a545afd..0b61a7f 100644 --- a/participation/models.py +++ b/participation/models.py @@ -859,6 +859,8 @@ class Participation(models.Model): defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self) opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self) reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self) + observer_passage = Passage.objects.filter(pool__tournament=self.tournament, pool__round=1, observer=self) + observer_passage = observer_passage.get() if observer_passage.exists() else None defender_text = _("

The solutions draw is ended. You can check the result on " "this page.

" @@ -887,12 +889,26 @@ class Participation(models.Model): solution_url=solution_url, problem=reviewer_passage.solution_number, passage_url=passage_url) + if observer_passage: + observer_text = _("

You will observe the solution of the team {observer} on the " + "problem {problem}. " + "You can upload your synthesis sheet on this page.

") + solution_url = observer_passage.defended_solution.file.url + passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) + observer_content = format_lazy(observer_text, + observer=observer_passage.defender.team.trigram, + solution_url=solution_url, + problem=observer_passage.solution_number, passage_url=passage_url) + else: + observer_content = "" + syntheses_template_begin = f"{settings.STATIC_URL}Fiche_synthèse." syntheses_templates = " — ".join(f"{ext.upper()}" for ext in ["pdf", "tex", "odt", "docx"]) syntheses_templates_content = f"

{_('Templates:')} {syntheses_templates}

" - content = defender_content + opponent_content + reviewer_content + syntheses_templates_content + content = defender_content + opponent_content + reviewer_content + observer_content \ + + syntheses_templates_content informations.append({ 'title': _("First round"), 'type': "info", @@ -903,6 +919,8 @@ class Participation(models.Model): defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self) opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self) reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self) + observer_passage = Passage.objects.filter(pool__tournament=self.tournament, pool__round=2, observer=self) + observer_passage = observer_passage.get() if observer_passage.exists() else None defender_text = _("

For the second round, you will defend " "your solution of the problem {problem}.

") @@ -929,12 +947,26 @@ class Participation(models.Model): solution_url=solution_url, problem=reviewer_passage.solution_number, passage_url=passage_url) + if observer_passage: + observer_text = _("

You will observe the solution of the team {observer} on the " + "problem {problem}. " + "You can upload your synthesis sheet on this page.

") + solution_url = observer_passage.defended_solution.file.url + passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) + observer_content = format_lazy(observer_text, + observer=observer_passage.defender.team.trigram, + solution_url=solution_url, + problem=observer_passage.solution_number, passage_url=passage_url) + else: + observer_content = "" + syntheses_template_begin = f"{settings.STATIC_URL}Fiche_synthèse." syntheses_templates = " — ".join(f"{ext.upper()}" for ext in ["pdf", "tex", "odt", "docx"]) syntheses_templates_content = f"

{_('Templates:')} {syntheses_templates}

" - content = defender_content + opponent_content + reviewer_content + syntheses_templates_content + content = defender_content + opponent_content + reviewer_content + observer_content \ + + syntheses_templates_content informations.append({ 'title': _("Second round"), 'type': "info", @@ -946,6 +978,8 @@ class Participation(models.Model): defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self) opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self) reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self) + observer_passage = Passage.objects.filter(pool__tournament=self.tournament, pool__round=3, observer=self) + observer_passage = observer_passage.get() if observer_passage.exists() else None defender_text = _("

For the third round, you will defend " "your solution of the problem {problem}.

") @@ -972,12 +1006,26 @@ class Participation(models.Model): solution_url=solution_url, problem=reviewer_passage.solution_number, passage_url=passage_url) + if observer_passage: + observer_text = _("

You will observe the solution of the team {observer} on the " + "problem {problem}. " + "You can upload your synthesis sheet on this page.

") + solution_url = observer_passage.defended_solution.file.url + passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) + observer_content = format_lazy(observer_text, + observer=observer_passage.defender.team.trigram, + solution_url=solution_url, + problem=observer_passage.solution_number, passage_url=passage_url) + else: + observer_content = "" + syntheses_template_begin = f"{settings.STATIC_URL}Fiche_synthèse." syntheses_templates = " — ".join(f"{ext.upper()}" for ext in ["pdf", "tex", "odt", "docx"]) syntheses_templates_content = f"

{_('Templates:')} {syntheses_templates}

" - content = defender_content + opponent_content + reviewer_content + syntheses_templates_content + content = defender_content + opponent_content + reviewer_content + observer_content \ + + syntheses_templates_content informations.append({ 'title': _("Second round"), 'type': "info", @@ -1560,6 +1608,16 @@ class Passage(models.Model): related_name="+", ) + observer = models.ForeignKey( + Participation, + on_delete=models.SET_NULL, + verbose_name=_("observer"), + related_name="+", + null=True, + blank=True, + default=None, + ) + defender_penalties = models.PositiveSmallIntegerField( verbose_name=_("penalties"), default=0, @@ -1588,7 +1646,10 @@ class Passage(models.Model): @property def average_defender(self) -> float: - return self.average_defender_writing + (1.6 - 0.4 * self.defender_penalties) * self.average_defender_oral + 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 @property def average_opponent_writing(self) -> float: @@ -1600,7 +1661,9 @@ class Passage(models.Model): @property def average_opponent(self) -> float: - return 0.9 * self.average_opponent_writing + 2 * self.average_opponent_oral + 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 @property def average_reviewer_writing(self) -> float: @@ -1612,7 +1675,21 @@ class Passage(models.Model): @property def average_reviewer(self) -> float: - return 0.9 * self.average_reviewer_writing + self.average_reviewer_oral + 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 + + @property + def average_observer_writing(self) -> float: + return self.avg(note.observer_writing for note in self.notes.all()) + + @property + def average_observer_oral(self) -> float: + return self.avg(note.observer_oral for note in self.notes.all()) + + @property + def average_observer(self) -> float: + return 0.6 * self.average_observer_writing + 0.5 * self.average_observer_oral @property def averages(self): @@ -1622,10 +1699,19 @@ class Passage(models.Model): yield self.average_opponent_oral yield self.average_reviewer_writing yield self.average_reviewer_oral + if self.observer: + yield self.average_observer_writing + yield self.average_observer_oral def average(self, participation): - return self.average_defender if participation == self.defender else self.average_opponent \ - if participation == self.opponent else self.average_reviewer if participation == self.reviewer else 0 + 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 + + return avg def get_absolute_url(self): return reverse_lazy("participation:passage_detail", args=(self.pk,)) @@ -1640,6 +1726,9 @@ class Passage(models.Model): if self.reviewer not in self.pool.participations.all(): raise ValidationError(_("Team {trigram} is not registered in the pool.") .format(trigram=self.reviewer.team.trigram)) + if self.observer and self.observer not in self.pool.participations.all(): + raise ValidationError(_("Team {trigram} is not registered in the pool.") + .format(trigram=self.observer.team.trigram)) return super().clean() def __str__(self): @@ -1748,6 +1837,7 @@ class Synthesis(models.Model): choices=[ (1, _("opponent"), ), (2, _("reviewer"), ), + (3, _("observer"), ), ] ) @@ -1823,6 +1913,18 @@ class Note(models.Model): default=0, ) + observer_writing = models.PositiveSmallIntegerField( + verbose_name=_("observer writing note"), + choices=[(i, i) for i in range(0, 11)], + default=0, + ) + + observer_oral = models.PositiveSmallIntegerField( + verbose_name=_("observer oral note"), + choices=[(i, i) for i in range(-10, 11)], + default=0, + ) + def get_all(self): yield self.defender_writing yield self.defender_oral @@ -1830,15 +1932,20 @@ class Note(models.Model): yield self.opponent_oral yield self.reviewer_writing yield self.reviewer_oral + if self.passage.observer: + yield self.observer_writing + yield self.observer_oral def set_all(self, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int, - reviewer_writing: int, reviewer_oral: int): + reviewer_writing: int, reviewer_oral: int, observer_writing: int = 0, observer_oral: int = 0): self.defender_writing = defender_writing self.defender_oral = defender_oral self.opponent_writing = opponent_writing self.opponent_oral = opponent_oral self.reviewer_writing = reviewer_writing self.reviewer_oral = reviewer_oral + self.observer_writing = observer_writing + self.observer_oral = observer_oral def update_spreadsheet(self): if not self.has_any_note(): diff --git a/participation/tables.py b/participation/tables.py index 83d45e1..47e3237 100644 --- a/participation/tables.py +++ b/participation/tables.py @@ -106,6 +106,8 @@ class PoolTable(tables.Table): class PassageTable(tables.Table): + # FIXME Ne pas afficher l'équipe observatrice si non nécessaire + defender = tables.LinkColumn( "participation:passage_detail", args=[tables.A("id")], @@ -121,12 +123,15 @@ class PassageTable(tables.Table): def render_reviewer(self, value): return value.team.trigram + def render_observer(self, value): + return value.team.trigram + class Meta: attrs = { 'class': 'table table-condensed table-striped text-center', } model = Passage - fields = ('defender', 'opponent', 'reviewer', 'solution_number', ) + fields = ('defender', 'opponent', 'reviewer', 'observer', 'solution_number', ) class NoteTable(tables.Table): @@ -155,4 +160,4 @@ class NoteTable(tables.Table): } model = Note fields = ('jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral', - 'reviewer_writing', 'reviewer_oral', 'update',) + 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',) diff --git a/participation/templates/participation/passage_detail.html b/participation/templates/participation/passage_detail.html index e29894d..13a18fb 100644 --- a/participation/templates/participation/passage_detail.html +++ b/participation/templates/participation/passage_detail.html @@ -31,9 +31,14 @@
{% trans "Opponent:" %}
{{ passage.opponent.team }}
-
{% trans "reviewer:" %}
+
{% trans "Reviewer:" %}
{{ passage.reviewer.team }}
+ {% if passage.observer %} +
{% trans "Observer:" %}
+
{{ passage.observer.team }}
+ {% endif %} +
{% trans "Defended solution:" %}
{{ passage.defended_solution }}
@@ -108,6 +113,20 @@ ({{ passage.reviewer.team.trigram }}) :
{{ passage.average_reviewer_oral|floatformat }}/10
+ + {% if passage.observer %} +
+ {% trans "Average points for the observer writing" %} + ({{ passage.observer.team.trigram }}) : +
+
{{ passage.average_observer_writing|floatformat }}/10
+ +
+ {% trans "Average points for the observer oral" %} + ({{ passage.observer.team.trigram }}) : +
+
{{ passage.average_observer_oral|floatformat }}/10
+ {% endif %}
@@ -130,6 +149,15 @@ ({{ passage.reviewer.team.trigram }}) :
{{ passage.average_reviewer|floatformat }}/19
+ + {% if passage.observer %} +
+ {% trans "observer points" %} + ({{ passage.observer.team.trigram }}) : +
+ +
{{ passage.average_observer|floatformat }}/10
+ {% endif %} diff --git a/participation/views.py b/participation/views.py index 85ac531..41bf115 100644 --- a/participation/views.py +++ b/participation/views.py @@ -1992,7 +1992,8 @@ class SynthesisUploadView(LoginRequiredMixin, FormView): self.participation = self.request.user.registration.team.participation self.passage = qs.get() - if self.participation not in [self.passage.opponent, self.passage.reviewer]: + if self.participation \ + and self.participation not in [self.passage.opponent, self.passage.reviewer, self.passage.observer]: return self.handle_no_permission() return super().dispatch(request, *args, **kwargs) @@ -2004,7 +2005,8 @@ class SynthesisUploadView(LoginRequiredMixin, FormView): It is discriminating whenever the team is selected for the final tournament or not. """ form_syn = form.instance - form_syn.type = 1 if self.participation == self.passage.opponent else 2 + form_syn.type = 1 if self.participation == self.passage.opponent \ + else 2 if self.participation == self.passage.reviewer else 3 syn_qs = Synthesis.objects.filter(participation=self.participation, passage=self.passage, type=form_syn.type).all() @@ -2052,6 +2054,8 @@ class NoteUpdateView(VolunteerMixin, UpdateView): form.fields['opponent_oral'].label += f" ({self.object.passage.opponent.team.trigram})" form.fields['reviewer_writing'].label += f" ({self.object.passage.reviewer.team.trigram})" form.fields['reviewer_oral'].label += f" ({self.object.passage.reviewer.team.trigram})" + form.fields['observer_writing'].label += f" ({self.object.passage.observer.team.trigram})" + form.fields['observer_oral'].label += f" ({self.object.passage.observer.team.trigram})" return form def form_valid(self, form):