1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2024-11-27 02:13:06 +00:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Emmy D'Anello
dd397ae7c0
Fix string
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-05 12:02:40 +02:00
Emmy D'Anello
3f2a757414
Allow observers to access solutions
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-05 12:02:08 +02:00
Emmy D'Anello
d20d5f6266
Fix CSV export
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-05 11:50:13 +02:00
Emmy D'Anello
05a6570bed
Add observer team
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-05 11:47:19 +02:00
12 changed files with 492 additions and 195 deletions

View File

@ -508,7 +508,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
msg = _("The dice results are the following: {trigrams}. "
"The passage order and the compositions of the different pools are displayed on the side. "
"The passage orders for the first round are determined from the dice scores, in increasing order. "
"For the second round, the passage orders are determined from the passage orders of the first round.")
"For the second round, the passage orders are determined from the passage orders of the first round.") \
.format(trigrams=trigrams)
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()

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

@ -685,7 +685,7 @@ class TournamentExportCSVView(VolunteerMixin, DetailView):
)
writer = csv.DictWriter(resp, ('Tournoi', 'Équipe', 'Trigramme', 'Sélectionnée',
'Nom', 'Prénom', 'Email', 'Type', 'Genre', 'Date de naissance',
'Adresse', 'Code postal', 'Ville', 'Téléphone',
'Adresse', 'Code postal', 'Ville', 'Pays', 'Téléphone',
'Classe', 'Établissement',
'Nom responsable légal⋅e', 'Téléphone responsable légal⋅e',
'Email responsable légal⋅e',
@ -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):

View File

@ -836,10 +836,11 @@ class SolutionView(LoginRequiredMixin, View):
raise Http404
solution = Solution.objects.get(file__endswith=filename)
user = request.user
if user.registration.participates:
if user.registration.participates and user.registration.team.participation:
passage_participant_qs = Passage.objects.filter(Q(defender=user.registration.team.participation)
| Q(opponent=user.registration.team.participation)
| Q(reviewer=user.registration.team.participation),
| Q(reviewer=user.registration.team.participation)
| Q(observer=user.registration.team.participation),
defender=solution.participation,
solution_number=solution.problem)
else: