Add observer team

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-07-05 11:47:19 +02:00
parent 2a298a3ee4
commit 05a6570bed
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
10 changed files with 486 additions and 191 deletions

View File

@ -5,6 +5,7 @@ import os
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import QuerySet from django.db.models import QuerySet
@ -199,7 +200,7 @@ class Round(models.Model):
(3, _('Round 3'))], (3, _('Round 3'))],
verbose_name=_('number'), verbose_name=_('number'),
help_text=_("The number of the round, 1 or 2 (or 3 for ETEAM)"), 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( current_pool = models.ForeignKey(
@ -233,6 +234,13 @@ class Round(models.Model):
def __str__(self): def __str__(self):
return self.get_number_display() 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: class Meta:
verbose_name = _('round') verbose_name = _('round')
verbose_name_plural = _('rounds') verbose_name_plural = _('rounds')
@ -392,11 +400,11 @@ class Pool(models.Model):
] ]
elif self.size == 5: elif self.size == 5:
table = [ table = [
[0, 2, 3], [0, 2, 3, 4],
[1, 3, 4], [1, 3, 4, 0],
[2, 4, 0], [2, 4, 0, 1],
[3, 0, 1], [3, 0, 1, 2],
[4, 1, 2], [4, 1, 2, 3],
] ]
for i, line in enumerate(table): for i, line in enumerate(table):
@ -408,14 +416,20 @@ class Pool(models.Model):
passage_pool = pool2 passage_pool = pool2
passage_position = 1 + i // 2 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 # Create the passage
await Passage.objects.acreate( await Passage.objects.acreate(
pool=passage_pool, pool=passage_pool,
position=passage_position, position=passage_position,
solution_number=tds[line[0]].accepted, solution_number=tds[line[0]].accepted,
defender=tds[line[0]].participation, defender=defender,
opponent=tds[line[1]].participation, opponent=opponent,
reviewer=tds[line[2]].participation, reviewer=reviewer,
observer=observer,
defender_penalties=tds[line[0]].penalty_int, defender_penalties=tds[line[0]].penalty_int,
) )

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -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." msgstr "Type de permission nécessaire pour écrire un message dans un canal."
#: chat/models.py:62 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88 #: chat/models.py:62 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88
#: draw/models.py:26 participation/admin.py:79 participation/admin.py:140 #: draw/models.py:27 participation/admin.py:79 participation/admin.py:144
#: participation/admin.py:171 participation/models.py:727 #: participation/admin.py:176 participation/models.py:727
#: participation/models.py:751 participation/models.py:1012 #: participation/models.py:751 participation/models.py:1060
#: registration/models.py:763 #: registration/models.py:763
#: registration/templates/registration/payment_form.html:53 #: registration/templates/registration/payment_form.html:53
msgid "tournament" msgid "tournament"
@ -93,9 +93,9 @@ msgstr ""
"Pour une permission qui concerne un tournoi, indique quel est le tournoi " "Pour une permission qui concerne un tournoi, indique quel est le tournoi "
"concerné." "concerné."
#: chat/models.py:73 draw/models.py:432 draw/models.py:459 #: chat/models.py:73 draw/models.py:446 draw/models.py:473
#: participation/admin.py:136 participation/admin.py:155 #: participation/admin.py:140 participation/admin.py:160
#: participation/models.py:1515 participation/models.py:1524 #: participation/models.py:1563 participation/models.py:1572
#: participation/tables.py:84 #: participation/tables.py:84
msgid "pool" msgid "pool"
msgstr "poule" msgstr "poule"
@ -108,7 +108,7 @@ msgstr ""
"concernée." "concernée."
#: chat/models.py:84 draw/templates/draw/tournament_content.html:277 #: 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/models.py:742
#: participation/templates/participation/tournament_harmonize.html:15 #: participation/templates/participation/tournament_harmonize.html:15
#: registration/models.py:158 registration/models.py:754 #: registration/models.py:158 registration/models.py:754
@ -264,8 +264,8 @@ msgstr "Connexion"
msgid "teams" msgid "teams"
msgstr "équipes" msgstr "équipes"
#: draw/admin.py:92 draw/models.py:237 draw/models.py:451 #: draw/admin.py:92 draw/models.py:245 draw/models.py:465
#: participation/models.py:1016 #: participation/models.py:1064
msgid "round" msgid "round"
msgstr "tour" msgstr "tour"
@ -453,31 +453,31 @@ msgstr ""
msgid "The draw of the second round is starting!" msgid "The draw of the second round is starting!"
msgstr "Le tirage au sort du deuxième tour commence !" msgstr "Le tirage au sort du deuxième tour commence !"
#: draw/models.py:27 #: draw/models.py:28
msgid "The associated tournament." msgid "The associated tournament."
msgstr "Le tournoi associé." msgstr "Le tournoi associé."
#: draw/models.py:36 #: draw/models.py:37
msgid "current round" msgid "current round"
msgstr "tour actuel" msgstr "tour actuel"
#: draw/models.py:37 #: draw/models.py:38
msgid "The current round where teams select their problems." msgid "The current round where teams select their problems."
msgstr "Le tour en cours où les équipes choisissent leurs problèmes." msgstr "Le tour en cours où les équipes choisissent leurs problèmes."
#: draw/models.py:43 #: draw/models.py:44
msgid "last message" msgid "last message"
msgstr "dernier message" msgstr "dernier message"
#: draw/models.py:44 #: draw/models.py:45
msgid "The last message that is displayed on the drawing interface." msgid "The last message that is displayed on the drawing interface."
msgstr "Le dernier message qui est affiché sur l'interface de tirage." msgstr "Le dernier message qui est affiché sur l'interface de tirage."
#: draw/models.py:94 #: draw/models.py:95
msgid "State" msgid "State"
msgstr "État" msgstr "État"
#: draw/models.py:113 #: draw/models.py:114
msgid "" msgid ""
"We are going to start the problem draw.<br>You can ask any question if " "We are going to start the problem draw.<br>You can ask any question if "
"something is not clear or wrong.<br><br>We are going to first draw the pools " "something is not clear or wrong.<br><br>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 " "toutes les équipes, puis pour chaque poule, nous allons tirer l'ordre de "
"tirage et les problèmes." "tirage et les problèmes."
#: draw/models.py:118 #: draw/models.py:119
msgid "" msgid ""
"The captains, you can now all throw a 100-sided dice, by clicking on the big " "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 " "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 " "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." "petit dé passera en premier dans la poule A."
#: draw/models.py:123 #: draw/models.py:124
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"We are going to start the problem draw for the pool <strong>{pool}</strong>, " "We are going to start the problem draw for the pool <strong>{pool}</strong>, "
@ -516,7 +516,7 @@ msgstr ""
"déterminer l'ordre de tirage. L'équipe avec le score le plus élevé tirera en " "déterminer l'ordre de tirage. L'équipe avec le score le plus élevé tirera en "
"premier." "premier."
#: draw/models.py:133 #: draw/models.py:134
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"The team <strong>{trigram}</strong> is going to draw a problem. Click on the " "The team <strong>{trigram}</strong> is going to draw a problem. Click on the "
@ -525,7 +525,7 @@ msgstr ""
"L'équipe <strong>{trigram}</strong> va tirer un problème. Cliquez sur l'urne " "L'équipe <strong>{trigram}</strong> va tirer un problème. Cliquez sur l'urne "
"au milieu pour tirer un problème." "au milieu pour tirer un problème."
#: draw/models.py:139 #: draw/models.py:140
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"The team <strong>{trigram}</strong> drew the problem <strong>{problem}: " "The team <strong>{trigram}</strong> drew the problem <strong>{problem}: "
@ -534,7 +534,7 @@ msgstr ""
"L'équipe <strong>{trigram}</strong> a tiré le problème <strong>{problem} : " "L'équipe <strong>{trigram}</strong> a tiré le problème <strong>{problem} : "
"{problem_name}</strong>." "{problem_name}</strong>."
#: draw/models.py:145 #: draw/models.py:146
msgid "" msgid ""
"It already refused this problem before, so it can refuse it without penalty " "It already refused this problem before, so it can refuse it without penalty "
"and draw a new problem immediately, or change its mind." "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 " "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." "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." msgid "It can decide to accept or refuse this problem."
msgstr "Elle peut décider d'accepter ou de refuser ce problème." msgstr "Elle peut décider d'accepter ou de refuser ce problème."
#: draw/models.py:151 #: draw/models.py:152
msgid "" msgid ""
"Refusing this problem will add a new 25% penalty on the coefficient of " "Refusing this problem will add a new 25% penalty on the coefficient of the "
"the oral defense." "oral defense."
msgstr "" msgstr ""
"Refuser ce problème ajoutera une nouvelle pénalité de 25nbsp;% sur le " "Refuser ce problème ajoutera une nouvelle pénalité de 25nbsp;% sur le "
"coefficient de l'oral de la défense." "coefficient de l'oral de la défense."
#: draw/models.py:154 #: draw/models.py:155
#, python-brace-format #, python-brace-format
msgid "There are still {remaining} refusals without penalty." msgid "There are still {remaining} refusals without penalty."
msgstr "Il reste {remaining} refus sans pénalité." msgstr "Il reste {remaining} refus sans pénalité."
#: draw/models.py:158 #: draw/models.py:159
msgid "" msgid ""
"The draw for the second round will take place at the end of the first round. " "The draw for the second round will take place at the end of the first round. "
"Good luck!" "Good luck!"
@ -567,7 +567,7 @@ msgstr ""
"Le tirage au sort du deuxième tour aura lieu à la fin du premier tour. Bonne " "Le tirage au sort du deuxième tour aura lieu à la fin du premier tour. Bonne "
"chance !" "chance !"
#: draw/models.py:161 #: draw/models.py:162
msgid "" msgid ""
"The draw is ended. The solutions of the other teams can be found in the tab " "The draw is ended. The solutions of the other teams can be found in the tab "
"\"My participation\"." "\"My participation\"."
@ -575,7 +575,7 @@ msgstr ""
"Le tirage est terminé. Les solutions des autres équipes peuvent être " "Le tirage est terminé. Les solutions des autres équipes peuvent être "
"trouvées dans l'onglet « Ma participation »." "trouvées dans l'onglet « Ma participation »."
#: draw/models.py:166 #: draw/models.py:167
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"For more details on the draw, the rules are available on <a class=\"alert-" "For more details on the draw, the rules are available on <a class=\"alert-"
@ -584,154 +584,159 @@ msgstr ""
"Pour plus de détails sur le tirage, les règles sont disponibles sur <a " "Pour plus de détails sur le tirage, les règles sont disponibles sur <a "
"class=\"alert-link\" href=\"{link}\">{link}</a>." "class=\"alert-link\" href=\"{link}\">{link}</a>."
#: draw/models.py:177 #: draw/models.py:178
#, python-brace-format #, python-brace-format
msgid "Draw of tournament {tournament}" msgid "Draw of tournament {tournament}"
msgstr "Tirage au sort du tournoi {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" msgid "draw"
msgstr "tirage au sort" msgstr "tirage au sort"
#: draw/models.py:181 #: draw/models.py:182
msgid "draws" msgid "draws"
msgstr "tirages au sort" msgstr "tirages au sort"
#: draw/models.py:197 #: draw/models.py:198
msgid "Round 1" msgid "Round 1"
msgstr "Tour 1" msgstr "Tour 1"
#: draw/models.py:198 #: draw/models.py:199
msgid "Round 2" msgid "Round 2"
msgstr "Tour 2" msgstr "Tour 2"
#: draw/models.py:199 #: draw/models.py:200
msgid "Round 3" msgid "Round 3"
msgstr "Tour 3" msgstr "Tour 3"
#: draw/models.py:200 #: draw/models.py:201
msgid "number" msgid "number"
msgstr "numéro" msgstr "numéro"
#: draw/models.py:201 #: draw/models.py:202
msgid "The number of the round, 1 or 2 (or 3 for ETEAM)" 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)" msgstr "Le numéro du tour, 1 ou 2 (ou 3 pour ETEAM)"
#: draw/models.py:211 #: draw/models.py:212
msgid "current pool" msgid "current pool"
msgstr "poule actuelle" msgstr "poule actuelle"
#: draw/models.py:212 #: draw/models.py:213
msgid "The current pool where teams select their problems." msgid "The current pool where teams select their problems."
msgstr "La poule en cours, où les équipes choisissent leurs problèmes" 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" msgid "rounds"
msgstr "tours" msgstr "tours"
#: draw/models.py:260 participation/models.py:1024 #: draw/models.py:268 participation/models.py:1072
msgid "letter" msgid "letter"
msgstr "lettre" msgstr "lettre"
#: draw/models.py:261 #: draw/models.py:269
msgid "The letter of the pool: A, B, C or D." msgid "The letter of the pool: A, B, C or D."
msgstr "La lettre de la poule : A, B, C ou 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 #: participation/templates/participation/tournament_detail.html:15
msgid "size" msgid "size"
msgstr "taille" msgstr "taille"
#: draw/models.py:267 #: draw/models.py:275
msgid "The number of teams in this pool, between 3 and 5." msgid "The number of teams in this pool, between 3 and 5."
msgstr "Le nombre d'équipes dans la poule, entre 3 et 5." msgstr "Le nombre d'équipes dans la poule, entre 3 et 5."
#: draw/models.py:276 #: draw/models.py:284
msgid "current team" msgid "current team"
msgstr "équipe actuelle" msgstr "équipe actuelle"
#: draw/models.py:277 #: draw/models.py:285
msgid "The current team that is selecting its problem." msgid "The current team that is selecting its problem."
msgstr "L'équipe qui est en train de choisir son problème." msgstr "L'équipe qui est en train de choisir son problème."
#: draw/models.py:286 #: draw/models.py:294
msgid "associated pool" msgid "associated pool"
msgstr "poule associée" msgstr "poule associée"
#: draw/models.py:287 #: draw/models.py:295
msgid "The full pool instance." msgid "The full pool instance."
msgstr "L'instance complète de la poule." msgstr "L'instance complète de la poule."
#: draw/models.py:429 #: draw/models.py:443
#, python-brace-format #, python-brace-format
msgid "Pool {letter}{number}" msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}" msgstr "Poule {letter}{number}"
#: draw/models.py:433 participation/models.py:1516 #: draw/models.py:447 participation/models.py:1564
msgid "pools" msgid "pools"
msgstr "poules" msgstr "poules"
#: draw/models.py:445 participation/models.py:1002 participation/models.py:1665 #: draw/models.py:459 participation/models.py:1050 participation/models.py:1754
#: participation/models.py:1695 participation/models.py:1737 #: participation/models.py:1784 participation/models.py:1826
msgid "participation" msgid "participation"
msgstr "participation" msgstr "participation"
#: draw/models.py:466 #: draw/models.py:480
msgid "passage index" msgid "passage index"
msgstr "numéro de passage" msgstr "numéro de passage"
#: draw/models.py:467 #: draw/models.py:481
msgid "" msgid ""
"The passage order in the pool, between 0 and the size of the pool minus 1." "The passage order in the pool, between 0 and the size of the pool minus 1."
msgstr "" msgstr ""
"L'ordre de passage dans la poule, de 0 à la taille de la poule moins 1." "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" msgid "choose index"
msgstr "numéro de choix" msgstr "numéro de choix"
#: draw/models.py:476 #: draw/models.py:490
msgid "" msgid ""
"The choice order in the pool, between 0 and the size of the pool minus 1." "The choice order in the pool, between 0 and the size of the pool minus 1."
msgstr "" msgstr ""
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1." "L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
#: draw/models.py:482 draw/models.py:505 participation/models.py:1538 #: draw/models.py:496 draw/models.py:519 participation/models.py:1586
#: participation/models.py:1702 #: participation/models.py:1791
#, python-brace-format #, python-brace-format
msgid "Problem #{problem}" msgid "Problem #{problem}"
msgstr "Problème n°{problem}" msgstr "Problème n°{problem}"
#: draw/models.py:486 #: draw/models.py:500
msgid "accepted problem" msgid "accepted problem"
msgstr "problème accepté" msgstr "problème accepté"
#: draw/models.py:493 #: draw/models.py:507
msgid "passage dice" msgid "passage dice"
msgstr "dé d'ordre de passage" msgstr "dé d'ordre de passage"
#: draw/models.py:500 #: draw/models.py:514
msgid "choice dice" msgid "choice dice"
msgstr "dé d'ordre de choix" msgstr "dé d'ordre de choix"
#: draw/models.py:509 #: draw/models.py:523
msgid "purposed problem" msgid "purposed problem"
msgstr "problème proposé" msgstr "problème proposé"
#: draw/models.py:514 #: draw/models.py:528
msgid "rejected problems" msgid "rejected problems"
msgstr "problèmes rejetés" msgstr "problèmes rejetés"
#: draw/models.py:543 #: draw/models.py:557
#, python-brace-format #, python-brace-format
msgid "Draw of the team {trigram} for the pool {letter}{number}" msgid "Draw of the team {trigram} for the pool {letter}{number}"
msgstr "Tirage de l'équipe {trigram} pour la poule {letter}{number}" msgstr "Tirage de l'équipe {trigram} pour la poule {letter}{number}"
#: draw/models.py:549 #: draw/models.py:563
msgid "team draw" msgid "team draw"
msgstr "tirage d'équipe" msgstr "tirage d'équipe"
#: draw/models.py:550 #: draw/models.py:564
msgid "team draws" msgid "team draws"
msgstr "tirages d'équipe" msgstr "tirages d'équipe"
@ -912,22 +917,27 @@ msgstr "valide"
msgid "selected for final" msgid "selected for final"
msgstr "sélectionnée pour la finale" msgstr "sélectionnée pour la finale"
#: participation/admin.py:124 participation/admin.py:183 #: participation/admin.py:124 participation/admin.py:188
#: participation/models.py:1545 participation/tables.py:112 #: participation/models.py:1593 participation/tables.py:114
msgid "defender" msgid "defender"
msgstr "défenseur⋅se" msgstr "défenseur⋅se"
#: participation/admin.py:128 participation/models.py:1552 #: participation/admin.py:128 participation/models.py:1600
#: participation/models.py:1749 #: participation/models.py:1838
msgid "opponent" msgid "opponent"
msgstr "opposant⋅e" msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:1559 #: participation/admin.py:132 participation/models.py:1607
#: participation/models.py:1750 #: participation/models.py:1839
msgid "reviewer" msgid "reviewer"
msgstr "rapporteur⋅rice" 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" msgid "problem"
msgstr "numéro de problème" msgstr "numéro de problème"
@ -1369,7 +1379,7 @@ msgstr ""
msgid "Draw of solutions" msgid "Draw of solutions"
msgstr "Tirage au sort des solutions" msgstr "Tirage au sort des solutions"
#: participation/models.py:863 #: participation/models.py:865
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>The solutions draw is ended. You can check the result on <a " "<p>The solutions draw is ended. You can check the result on <a "
@ -1381,8 +1391,8 @@ msgstr ""
"tour, vous défendrez <a href='{solution_url}'>votre solution du problème " "tour, vous défendrez <a href='{solution_url}'>votre solution du problème "
"{problem}</a>.</p>" "{problem}</a>.</p>"
#: participation/models.py:872 participation/models.py:914 #: participation/models.py:874 participation/models.py:932
#: participation/models.py:957 #: participation/models.py:991
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will oppose the solution of the team {opponent} on the <a " "<p>You will oppose the solution of the team {opponent} on the <a "
@ -1393,8 +1403,8 @@ msgstr ""
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:881 participation/models.py:923 #: participation/models.py:883 participation/models.py:941
#: participation/models.py:966 #: participation/models.py:1000
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will report the solution of the team {reviewer} on the <a " "<p>You will report the solution of the team {reviewer} on the <a "
@ -1405,11 +1415,23 @@ msgstr ""
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:897 registration/models.py:629 #: participation/models.py:893 participation/models.py:951
#: participation/models.py:1010
#, python-brace-format
msgid ""
"<p>You will observe the solution of the team {observer} on the <a "
"href='{solution_url}'>problem {problem}. You can upload your synthesis sheet "
"on <a href='{passage_url}'>this page</a>.</p>"
msgstr ""
"<p>Vous observerez la solution de l'équipe {observer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:913 registration/models.py:629
msgid "First round" msgid "First round"
msgstr "Premier tour" msgstr "Premier tour"
#: participation/models.py:907 #: participation/models.py:925
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the second round, you will defend <a href='{solution_url}'>your " "<p>For the second round, you will defend <a href='{solution_url}'>your "
@ -1418,12 +1440,12 @@ msgstr ""
"<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre " "<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>" "solution du problème {problem}</a>.</p>"
#: participation/models.py:939 participation/models.py:982 #: participation/models.py:971 participation/models.py:1030
#: registration/models.py:640 #: registration/models.py:640
msgid "Second round" msgid "Second round"
msgstr "Second tour" msgstr "Second tour"
#: participation/models.py:950 #: participation/models.py:984
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the third round, you will defend <a href='{solution_url}'>your " "<p>For the third round, you will defend <a href='{solution_url}'>your "
@ -1432,7 +1454,7 @@ msgstr ""
"<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre " "<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>" "solution du problème {problem}</a>.</p>"
#: participation/models.py:988 #: participation/models.py:1036
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>The tournament {tournament} is ended. You can check the results on the <a " "<p>The tournament {tournament} is ended. You can check the results on the <a "
@ -1441,57 +1463,57 @@ msgstr ""
"<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats " "<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats "
"sur la <a href='{url}'>page du tournoi</a>.</p>" "sur la <a href='{url}'>page du tournoi</a>.</p>"
#: participation/models.py:993 #: participation/models.py:1041
msgid "Tournament ended" msgid "Tournament ended"
msgstr "Tournoi terminé" msgstr "Tournoi terminé"
#: participation/models.py:1003 participation/models.py:1046 #: participation/models.py:1051 participation/models.py:1094
msgid "participations" msgid "participations"
msgstr "participations" msgstr "participations"
#: participation/models.py:1018 participation/models.py:1019 #: participation/models.py:1066 participation/models.py:1067
#: participation/models.py:1020 #: participation/models.py:1068
#, python-brace-format #, python-brace-format
msgid "Round {round}" msgid "Round {round}"
msgstr "Tour {round}" msgstr "Tour {round}"
#: participation/models.py:1034 #: participation/models.py:1082
msgid "room" msgid "room"
msgstr "salle" msgstr "salle"
#: participation/models.py:1036 #: participation/models.py:1084
msgid "Room 1" msgid "Room 1"
msgstr "Salle 1" msgstr "Salle 1"
#: participation/models.py:1037 #: participation/models.py:1085
msgid "Room 2" msgid "Room 2"
msgstr "Salle 2" msgstr "Salle 2"
#: participation/models.py:1040 #: participation/models.py:1088
msgid "For 5-teams pools only" msgid "For 5-teams pools only"
msgstr "Pour les poules de 5 équipe uniquement" msgstr "Pour les poules de 5 équipe uniquement"
#: participation/models.py:1052 #: participation/models.py:1100
msgid "juries" msgid "juries"
msgstr "jurys" msgstr "jurys"
#: participation/models.py:1061 #: participation/models.py:1109
msgid "president of the jury" msgid "president of the jury"
msgstr "président⋅e du jury" msgstr "président⋅e du jury"
#: participation/models.py:1068 #: participation/models.py:1116
msgid "BigBlueButton URL" msgid "BigBlueButton URL"
msgstr "Lien BigBlueButton" msgstr "Lien BigBlueButton"
#: participation/models.py:1069 #: participation/models.py:1117
msgid "The link of the BBB visio for this pool." msgid "The link of the BBB visio for this pool."
msgstr "Le lien du salon BBB pour cette poule." msgstr "Le lien du salon BBB pour cette poule."
#: participation/models.py:1074 #: participation/models.py:1122
msgid "results available" msgid "results available"
msgstr "résultats disponibles" msgstr "résultats disponibles"
#: participation/models.py:1075 #: participation/models.py:1123
msgid "" msgid ""
"Check this case when results become accessible to teams. They stay " "Check this case when results become accessible to teams. They stay "
"accessible to you. Only averages are given." "accessible to you. Only averages are given."
@ -1500,33 +1522,33 @@ msgstr ""
"Ils restent toujours accessibles pour vous. Seules les moyennes sont " "Ils restent toujours accessibles pour vous. Seules les moyennes sont "
"communiquées." "communiquées."
#: participation/models.py:1107 #: participation/models.py:1155
msgid "The president of the jury must be part of the jury." msgid "The president of the jury must be part of the jury."
msgstr "Læ président⋅e du jury doit faire partie du jury." msgstr "Læ président⋅e du jury doit faire partie du jury."
#: participation/models.py:1496 #: participation/models.py:1544
#, python-brace-format #, python-brace-format
msgid "The jury {jury} is not part of the jury for this pool." msgid "The jury {jury} is not part of the jury for this pool."
msgstr "{jury} ne fait pas partie du jury pour cette poule." msgstr "{jury} ne fait pas partie du jury pour cette poule."
#: participation/models.py:1509 #: participation/models.py:1557
#, python-brace-format #, python-brace-format
msgid "Pool {code} for tournament {tournament} with teams {teams}" msgid "Pool {code} for tournament {tournament} with teams {teams}"
msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}" msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}"
#: participation/models.py:1529 #: participation/models.py:1577
msgid "position" msgid "position"
msgstr "position" msgstr "position"
#: participation/models.py:1536 #: participation/models.py:1584
msgid "defended solution" msgid "defended solution"
msgstr "solution défendue" msgstr "solution défendue"
#: participation/models.py:1564 #: participation/models.py:1622
msgid "penalties" msgid "penalties"
msgstr "pénalités" msgstr "pénalités"
#: participation/models.py:1566 #: participation/models.py:1624
msgid "" msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 " "Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty." "coefficient per penalty."
@ -1534,120 +1556,128 @@ msgstr ""
"Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 " "Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 "
"sur sa présentation orale par pénalité." "sur sa présentation orale par pénalité."
#: participation/models.py:1635 participation/models.py:1638 #: participation/models.py:1721 participation/models.py:1724
#: participation/models.py:1641 #: participation/models.py:1727 participation/models.py:1730
#, python-brace-format #, python-brace-format
msgid "Team {trigram} is not registered in the pool." msgid "Team {trigram} is not registered in the pool."
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule." msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: participation/models.py:1646 #: participation/models.py:1735
#, python-brace-format #, python-brace-format
msgid "Passage of {defender} for problem {problem}" msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}" msgstr "Passage de {defender} pour le problème {problem}"
#: participation/models.py:1650 participation/models.py:1659 #: participation/models.py:1739 participation/models.py:1748
#: participation/models.py:1744 participation/models.py:1786 #: participation/models.py:1833 participation/models.py:1876
msgid "passage" msgid "passage"
msgstr "passage" msgstr "passage"
#: participation/models.py:1651 #: participation/models.py:1740
msgid "passages" msgid "passages"
msgstr "passages" msgstr "passages"
#: participation/models.py:1670 #: participation/models.py:1759
msgid "difference" msgid "difference"
msgstr "différence" msgstr "différence"
#: participation/models.py:1671 #: participation/models.py:1760
msgid "Score to add/remove on the final score" msgid "Score to add/remove on the final score"
msgstr "Score à ajouter/retrancher au score final" msgstr "Score à ajouter/retrancher au score final"
#: participation/models.py:1678 #: participation/models.py:1767
msgid "tweak" msgid "tweak"
msgstr "harmonisation" msgstr "harmonisation"
#: participation/models.py:1679 #: participation/models.py:1768
msgid "tweaks" msgid "tweaks"
msgstr "harmonisations" msgstr "harmonisations"
#: participation/models.py:1707 #: participation/models.py:1796
msgid "solution for the final tournament" msgid "solution for the final tournament"
msgstr "solution pour la finale" msgstr "solution pour la finale"
#: participation/models.py:1712 participation/models.py:1755 #: participation/models.py:1801 participation/models.py:1845
msgid "file" msgid "file"
msgstr "fichier" msgstr "fichier"
#: participation/models.py:1722 #: participation/models.py:1811
#, python-brace-format #, python-brace-format
msgid "Solution of team {team} for problem {problem}" msgid "Solution of team {team} for problem {problem}"
msgstr "Solution de l'équipe {team} pour le problème {problem}" msgstr "Solution de l'équipe {team} pour le problème {problem}"
#: participation/models.py:1724 #: participation/models.py:1813
msgid "for final" msgid "for final"
msgstr "pour la finale" msgstr "pour la finale"
#: participation/models.py:1727 #: participation/models.py:1816
msgid "solution" msgid "solution"
msgstr "solution" msgstr "solution"
#: participation/models.py:1728 #: participation/models.py:1817
msgid "solutions" msgid "solutions"
msgstr "solutions" msgstr "solutions"
#: participation/models.py:1761 #: participation/models.py:1851
#, python-brace-format #, python-brace-format
msgid "Synthesis of {team} as {type} for problem {problem} of {defender}" msgid "Synthesis of {team} as {type} for problem {problem} of {defender}"
msgstr "" msgstr ""
"Note de synthèse de l'équipe {team} en tant que {type} pour le problème " "Note de synthèse de l'équipe {team} en tant que {type} pour le problème "
"{problem} de {defender}" "{problem} de {defender}"
#: participation/models.py:1769 #: participation/models.py:1859
msgid "synthesis" msgid "synthesis"
msgstr "note de synthèse" msgstr "note de synthèse"
#: participation/models.py:1770 #: participation/models.py:1860
msgid "syntheses" msgid "syntheses"
msgstr "notes de synthèse" msgstr "notes de synthèse"
#: participation/models.py:1779 #: participation/models.py:1869
msgid "jury" msgid "jury"
msgstr "jury" msgstr "jury"
#: participation/models.py:1791 #: participation/models.py:1881
msgid "defender writing note" msgid "defender writing note"
msgstr "note d'écrit défenseur⋅se" msgstr "note d'écrit défenseur⋅se"
#: participation/models.py:1797 #: participation/models.py:1887
msgid "defender oral note" msgid "defender oral note"
msgstr "note d'oral défenseur⋅se" msgstr "note d'oral défenseur⋅se"
#: participation/models.py:1803 #: participation/models.py:1893
msgid "opponent writing note" msgid "opponent writing note"
msgstr "note d'écrit opposant⋅e" msgstr "note d'écrit opposant⋅e"
#: participation/models.py:1809 #: participation/models.py:1899
msgid "opponent oral note" msgid "opponent oral note"
msgstr "note d'oral opposant⋅e" msgstr "note d'oral opposant⋅e"
#: participation/models.py:1815 #: participation/models.py:1905
msgid "reviewer writing note" msgid "reviewer writing note"
msgstr "note d'écrit rapporteur⋅rice" msgstr "note d'écrit rapporteur⋅rice"
#: participation/models.py:1821 #: participation/models.py:1911
msgid "reviewer oral note" msgid "reviewer oral note"
msgstr "note d'oral du rapporteur⋅rice" msgstr "note d'oral du rapporteur⋅rice"
#: participation/models.py: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 #, python-brace-format
msgid "Notes of {jury} for {passage}" msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}" msgstr "Notes de {jury} pour le {passage}"
#: participation/models.py:1884 #: participation/models.py:1991
msgid "note" msgid "note"
msgstr "note" msgstr "note"
#: participation/models.py:1885 #: participation/models.py:1992
msgid "notes" msgid "notes"
msgstr "notes" msgstr "notes"
@ -1685,11 +1715,11 @@ msgstr "Poule {code}"
msgid "No defined team" msgid "No defined team"
msgstr "Pas d'équipe définie" msgstr "Pas d'équipe définie"
#: participation/tables.py:142 #: participation/tables.py:147
#: participation/templates/participation/note_form.html:14 #: participation/templates/participation/note_form.html:14
#: participation/templates/participation/passage_detail.html:15 #: participation/templates/participation/passage_detail.html:15
#: participation/templates/participation/passage_detail.html:140 #: participation/templates/participation/passage_detail.html:168
#: participation/templates/participation/passage_detail.html:146 #: participation/templates/participation/passage_detail.html:174
#: participation/templates/participation/pool_detail.html:13 #: participation/templates/participation/pool_detail.html:13
#: participation/templates/participation/pool_detail.html:152 #: participation/templates/participation/pool_detail.html:152
#: participation/templates/participation/team_detail.html:185 #: participation/templates/participation/team_detail.html:185
@ -1777,7 +1807,7 @@ msgid "Upload solution"
msgstr "Envoyer une solution" msgstr "Envoyer une solution"
#: participation/templates/participation/participation_detail.html:65 #: 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/pool_detail.html:157
#: participation/templates/participation/team_detail.html:245 #: participation/templates/participation/team_detail.html:245
#: participation/templates/participation/upload_motivation_letter.html:13 #: participation/templates/participation/upload_motivation_letter.html:13
@ -1814,78 +1844,94 @@ msgid "Opponent:"
msgstr "Opposant⋅e :" msgstr "Opposant⋅e :"
#: participation/templates/participation/passage_detail.html:34 #: participation/templates/participation/passage_detail.html:34
msgid "reviewer:" msgid "Reviewer:"
msgstr "Rapporteur⋅rice :" 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:" msgid "Defended solution:"
msgstr "Solution défendue" msgstr "Solution défendue"
#: participation/templates/participation/passage_detail.html:40 #: participation/templates/participation/passage_detail.html:45
msgid "Defender penalties count:" msgid "Defender penalties count:"
msgstr "Nombre de pénalités :" 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 #: participation/templates/participation/pool_detail.html:59
msgid "Syntheses:" msgid "Syntheses:"
msgstr "Notes de synthèse :" 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 #: participation/templates/participation/pool_detail.html:68
msgid "No synthesis was uploaded yet." msgid "No synthesis was uploaded yet."
msgstr "Aucune note de synthèse n'a encore été envoyée." msgstr "Aucune note de synthèse n'a encore été envoyée."
#: participation/templates/participation/passage_detail.html:56 #: participation/templates/participation/passage_detail.html:61
#: participation/templates/participation/passage_detail.html:145 #: participation/templates/participation/passage_detail.html:173
msgid "Update notes" msgid "Update notes"
msgstr "Modifier les notes" msgstr "Modifier les notes"
#: participation/templates/participation/passage_detail.html:61 #: participation/templates/participation/passage_detail.html:66
#: participation/templates/participation/passage_detail.html:151 #: participation/templates/participation/passage_detail.html:179
msgid "Upload synthesis" msgid "Upload synthesis"
msgstr "Envoyer une note de synthèse" msgstr "Envoyer une note de synthèse"
#: participation/templates/participation/passage_detail.html:69 #: participation/templates/participation/passage_detail.html:74
msgid "Notes detail" msgid "Notes detail"
msgstr "Détails des notes" 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" msgid "Average points for the defender writing"
msgstr "Moyenne de l'écrit de l'équipe défenseuse" 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" msgid "Average points for the defender oral"
msgstr "Moyenne de l'oral de l'équipe défenseuse" 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" msgid "Average points for the opponent writing"
msgstr "Moyenne de l'écrit de l'équipe opposante" 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" msgid "Average points for the opponent oral"
msgstr "Moyenne de l'oral de l'équipe opposante" 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" msgid "Average points for the reviewer writing"
msgstr "Moyenne de l'écrit de l'équipe rapportrice" 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" msgid "Average points for the reviewer oral"
msgstr "Moyenne de l'oral de l'équipe rapportrice" 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" msgid "Defender points"
msgstr "Points de l'équipe défenseuse" msgstr "Points de l'équipe défenseuse"
#: participation/templates/participation/passage_detail.html:123 #: participation/templates/participation/passage_detail.html:142
msgid "Opponent points" msgid "Opponent points"
msgstr "Points de l'équipe opposante" msgstr "Points de l'équipe opposante"
#: participation/templates/participation/passage_detail.html:129 #: participation/templates/participation/passage_detail.html:148
msgid "reviewer points" msgid "reviewer points"
msgstr "Points de l'équipe rapportrice" 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 #: participation/templates/participation/passage_form.html:11
msgid "Update passage" msgid "Update passage"
msgstr "Modifier le 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" msgid "Notation sheets of {tournament}.zip"
msgstr "Feuilles de notation de {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." 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." msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."

View File

@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline):
model = Passage model = Passage
extra = 0 extra = 0
ordering = ('position',) ordering = ('position',)
autocomplete_fields = ('defender', 'opponent', 'reviewer',) autocomplete_fields = ('defender', 'opponent', 'reviewer', 'observer',)
show_change_link = True show_change_link = True
@ -114,11 +114,11 @@ class PoolAdmin(admin.ModelAdmin):
@admin.register(Passage) @admin.register(Passage)
class PassageAdmin(admin.ModelAdmin): class PassageAdmin(admin.ModelAdmin):
list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reviewer_trigram', 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',) list_filter = ('pool__tournament', 'pool__round', 'pool__letter', 'solution_number',)
search_fields = ('pool__participations__team__name', 'pool__participations__team__trigram',) search_fields = ('pool__participations__team__name', 'pool__participations__team__trigram',)
ordering = ('pool__tournament', 'pool__round', 'pool__letter', 'position',) ordering = ('pool__tournament', 'pool__round', 'pool__letter', 'position',)
autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer',) autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer', 'observer',)
inlines = (NoteInline,) inlines = (NoteInline,)
@admin.display(description=_("defender"), ordering='defender__team__trigram') @admin.display(description=_("defender"), ordering='defender__team__trigram')
@ -133,6 +133,10 @@ class PassageAdmin(admin.ModelAdmin):
def reviewer_trigram(self, record: Passage): def reviewer_trigram(self, record: Passage):
return record.reviewer.team.trigram 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') @admin.display(description=_("pool"), ordering='pool__letter')
def pool_abbr(self, record): def pool_abbr(self, record):
return f"{record.pool.short_name}" return f"{record.pool.short_name}"
@ -145,10 +149,11 @@ class PassageAdmin(admin.ModelAdmin):
@admin.register(Note) @admin.register(Note)
class NoteAdmin(admin.ModelAdmin): class NoteAdmin(admin.ModelAdmin):
list_display = ('passage', 'pool', 'jury', 'defender_writing', 'defender_oral', 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', list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury',
'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral', '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',) search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__defender__team__trigram',)
autocomplete_fields = ('jury', 'passage',) autocomplete_fields = ('jury', 'passage',)

View File

@ -13,7 +13,7 @@ class NoteViewSet(ModelViewSet):
serializer_class = NoteSerializer serializer_class = NoteSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['jury', 'passage', 'defender_writing', 'defender_oral', 'opponent_writing', 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): class ParticipationViewSet(ModelViewSet):
@ -27,7 +27,7 @@ class PassageViewSet(ModelViewSet):
queryset = Passage.objects.all() queryset = Passage.objects.all()
serializer_class = PassageSerializer serializer_class = PassageSerializer
filter_backends = [DjangoFilterBackend] 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): class PoolViewSet(ModelViewSet):

View File

@ -355,7 +355,7 @@ class PassageForm(forms.ModelForm):
class Meta: class Meta:
model = Passage 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): class SynthesisForm(forms.ModelForm):
@ -386,4 +386,4 @@ class NoteForm(forms.ModelForm):
class Meta: class Meta:
model = Note model = Note
fields = ('defender_writing', 'defender_oral', 'opponent_writing', fields = ('defender_writing', 'defender_oral', 'opponent_writing',
'opponent_oral', 'reviewer_writing', 'reviewer_oral', ) 'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', )

View File

@ -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")]
),
),
]

View File

@ -859,6 +859,8 @@ class Participation(models.Model):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self) defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self) reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self)
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 = _("<p>The solutions draw is ended. You can check the result on " defender_text = _("<p>The solutions draw is ended. You can check the result on "
"<a href='{draw_url}'>this page</a>.</p>" "<a href='{draw_url}'>this page</a>.</p>"
@ -887,12 +889,26 @@ class Participation(models.Model):
solution_url=solution_url, solution_url=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url) problem=reviewer_passage.solution_number, passage_url=passage_url)
if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
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_template_begin = f"{settings.STATIC_URL}Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>" syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content
informations.append({ informations.append({
'title': _("First round"), 'title': _("First round"),
'type': "info", 'type': "info",
@ -903,6 +919,8 @@ class Participation(models.Model):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self) defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self) reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self)
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 = _("<p>For the second round, you will defend " defender_text = _("<p>For the second round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>") "<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
@ -929,12 +947,26 @@ class Participation(models.Model):
solution_url=solution_url, solution_url=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url) problem=reviewer_passage.solution_number, passage_url=passage_url)
if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
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_template_begin = f"{settings.STATIC_URL}Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>" syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
'type': "info", 'type': "info",
@ -946,6 +978,8 @@ class Participation(models.Model):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self) defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self) reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self)
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 = _("<p>For the third round, you will defend " defender_text = _("<p>For the third round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>") "<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
@ -972,12 +1006,26 @@ class Participation(models.Model):
solution_url=solution_url, solution_url=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url) problem=reviewer_passage.solution_number, passage_url=passage_url)
if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}. "
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
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_template_begin = f"{settings.STATIC_URL}Fiche_synthèse."
syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>" syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content content = defender_content + opponent_content + reviewer_content + observer_content \
+ syntheses_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
'type': "info", 'type': "info",
@ -1560,6 +1608,16 @@ class Passage(models.Model):
related_name="+", 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( defender_penalties = models.PositiveSmallIntegerField(
verbose_name=_("penalties"), verbose_name=_("penalties"),
default=0, default=0,
@ -1588,7 +1646,10 @@ class Passage(models.Model):
@property @property
def average_defender(self) -> float: 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 @property
def average_opponent_writing(self) -> float: def average_opponent_writing(self) -> float:
@ -1600,7 +1661,9 @@ class Passage(models.Model):
@property @property
def average_opponent(self) -> float: 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 @property
def average_reviewer_writing(self) -> float: def average_reviewer_writing(self) -> float:
@ -1612,7 +1675,21 @@ class Passage(models.Model):
@property @property
def average_reviewer(self) -> float: 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 @property
def averages(self): def averages(self):
@ -1622,10 +1699,19 @@ class Passage(models.Model):
yield self.average_opponent_oral yield self.average_opponent_oral
yield self.average_reviewer_writing yield self.average_reviewer_writing
yield self.average_reviewer_oral yield self.average_reviewer_oral
if self.observer:
yield self.average_observer_writing
yield self.average_observer_oral
def average(self, participation): def average(self, participation):
return self.average_defender if participation == self.defender else self.average_opponent \ 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 0 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): def get_absolute_url(self):
return reverse_lazy("participation:passage_detail", args=(self.pk,)) 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(): if self.reviewer not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.") raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.reviewer.team.trigram)) .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() return super().clean()
def __str__(self): def __str__(self):
@ -1748,6 +1837,7 @@ class Synthesis(models.Model):
choices=[ choices=[
(1, _("opponent"), ), (1, _("opponent"), ),
(2, _("reviewer"), ), (2, _("reviewer"), ),
(3, _("observer"), ),
] ]
) )
@ -1823,6 +1913,18 @@ class Note(models.Model):
default=0, 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): def get_all(self):
yield self.defender_writing yield self.defender_writing
yield self.defender_oral yield self.defender_oral
@ -1830,15 +1932,20 @@ class Note(models.Model):
yield self.opponent_oral yield self.opponent_oral
yield self.reviewer_writing yield self.reviewer_writing
yield self.reviewer_oral 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, 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_writing = defender_writing
self.defender_oral = defender_oral self.defender_oral = defender_oral
self.opponent_writing = opponent_writing self.opponent_writing = opponent_writing
self.opponent_oral = opponent_oral self.opponent_oral = opponent_oral
self.reviewer_writing = reviewer_writing self.reviewer_writing = reviewer_writing
self.reviewer_oral = reviewer_oral self.reviewer_oral = reviewer_oral
self.observer_writing = observer_writing
self.observer_oral = observer_oral
def update_spreadsheet(self): def update_spreadsheet(self):
if not self.has_any_note(): if not self.has_any_note():

View File

@ -106,6 +106,8 @@ class PoolTable(tables.Table):
class PassageTable(tables.Table): class PassageTable(tables.Table):
# FIXME Ne pas afficher l'équipe observatrice si non nécessaire
defender = tables.LinkColumn( defender = tables.LinkColumn(
"participation:passage_detail", "participation:passage_detail",
args=[tables.A("id")], args=[tables.A("id")],
@ -121,12 +123,15 @@ class PassageTable(tables.Table):
def render_reviewer(self, value): def render_reviewer(self, value):
return value.team.trigram return value.team.trigram
def render_observer(self, value):
return value.team.trigram
class Meta: class Meta:
attrs = { attrs = {
'class': 'table table-condensed table-striped text-center', 'class': 'table table-condensed table-striped text-center',
} }
model = Passage model = Passage
fields = ('defender', 'opponent', 'reviewer', 'solution_number', ) fields = ('defender', 'opponent', 'reviewer', 'observer', 'solution_number', )
class NoteTable(tables.Table): class NoteTable(tables.Table):
@ -155,4 +160,4 @@ class NoteTable(tables.Table):
} }
model = Note model = Note
fields = ('jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral', fields = ('jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral', 'update',) 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',)

View File

@ -31,9 +31,14 @@
<dt class="col-sm-3">{% trans "Opponent:" %}</dt> <dt class="col-sm-3">{% trans "Opponent:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.opponent.get_absolute_url }}">{{ passage.opponent.team }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.opponent.get_absolute_url }}">{{ passage.opponent.team }}</a></dd>
<dt class="col-sm-3">{% trans "reviewer:" %}</dt> <dt class="col-sm-3">{% trans "Reviewer:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.reviewer.get_absolute_url }}">{{ passage.reviewer.team }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.reviewer.get_absolute_url }}">{{ passage.reviewer.team }}</a></dd>
{% if passage.observer %}
<dt class="col-sm-3">{% trans "Observer:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.observer.get_absolute_url }}">{{ passage.observer.team }}</a></dd>
{% endif %}
<dt class="col-sm-3">{% trans "Defended solution:" %}</dt> <dt class="col-sm-3">{% trans "Defended solution:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.defended_solution.file.url }}">{{ passage.defended_solution }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.defended_solution.file.url }}">{{ passage.defended_solution }}</a></dd>
@ -108,6 +113,20 @@
({{ passage.reviewer.team.trigram }}) : ({{ passage.reviewer.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4">{{ passage.average_reviewer_oral|floatformat }}/10</dd> <dd class="col-sm-4">{{ passage.average_reviewer_oral|floatformat }}/10</dd>
{% if passage.observer %}
<dt class="col-sm-8">
{% trans "Average points for the observer writing" %}
({{ passage.observer.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_observer_writing|floatformat }}/10</dd>
<dt class="col-sm-8">
{% trans "Average points for the observer oral" %}
({{ passage.observer.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_observer_oral|floatformat }}/10</dd>
{% endif %}
</dl> </dl>
<hr> <hr>
@ -130,6 +149,15 @@
({{ passage.reviewer.team.trigram }}) : ({{ passage.reviewer.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/19</dd> <dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/19</dd>
{% if passage.observer %}
<dt class="col-sm-8">
{% trans "observer points" %}
({{ passage.observer.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_observer|floatformat }}/10</dd>
{% endif %}
</dl> </dl>
</div> </div>
</div> </div>

View File

@ -1992,7 +1992,8 @@ class SynthesisUploadView(LoginRequiredMixin, FormView):
self.participation = self.request.user.registration.team.participation self.participation = self.request.user.registration.team.participation
self.passage = qs.get() 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 self.handle_no_permission()
return super().dispatch(request, *args, **kwargs) 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. It is discriminating whenever the team is selected for the final tournament or not.
""" """
form_syn = form.instance 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, syn_qs = Synthesis.objects.filter(participation=self.participation,
passage=self.passage, passage=self.passage,
type=form_syn.type).all() 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['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_writing'].label += f" ({self.object.passage.reviewer.team.trigram})"
form.fields['reviewer_oral'].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 return form
def form_valid(self, form): def form_valid(self, form):