1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2024-12-24 18:22:24 +00:00

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 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,
)

View File

@ -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 <emmy.danello@animath.fr>\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."
#: 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.<br>You can ask any question if "
"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 "
"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 <strong>{pool}</strong>, "
@ -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 <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 "
"au milieu pour tirer un problème."
#: draw/models.py:139
#: draw/models.py:140
#, python-brace-format
msgid ""
"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} : "
"{problem_name}</strong>."
#: 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 <a class=\"alert-"
@ -584,154 +584,159 @@ msgstr ""
"Pour plus de détails sur le tirage, les règles sont disponibles sur <a "
"class=\"alert-link\" href=\"{link}\">{link}</a>."
#: 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 ""
"<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 "
"{problem}</a>.</p>"
#: 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 ""
"<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 "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: 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 ""
"<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 "
"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"
msgstr "Premier tour"
#: participation/models.py:907
#: participation/models.py:925
#, python-brace-format
msgid ""
"<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 "
"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
msgid "Second round"
msgstr "Second tour"
#: participation/models.py:950
#: participation/models.py:984
#, python-brace-format
msgid ""
"<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 "
"solution du problème {problem}</a>.</p>"
#: participation/models.py:988
#: participation/models.py:1036
#, python-brace-format
msgid ""
"<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 "
"sur la <a href='{url}'>page du tournoi</a>.</p>"
#: 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."

View File

@ -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',)

View File

@ -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):

View File

@ -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', )

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)
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 = _("<p>The solutions draw is ended. You can check the result on "
"<a href='{draw_url}'>this page</a>.</p>"
@ -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 = _("<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_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"])
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({
'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 = _("<p>For the second round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
@ -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 = _("<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_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"])
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({
'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 = _("<p>For the third round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
@ -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 = _("<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_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"])
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({
'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():

View File

@ -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',)

View File

@ -31,9 +31,14 @@
<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>
<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>
{% 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>
<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 }}) :
</dt>
<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>
<hr>
@ -130,6 +149,15 @@
({{ passage.reviewer.team.trigram }}) :
</dt>
<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>
</div>
</div>

View File

@ -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):