Add observer team
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
2a298a3ee4
commit
05a6570bed
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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."
|
||||
|
||||
|
|
|
@ -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',)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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', )
|
||||
|
|
|
@ -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")]
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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():
|
||||
|
|
|
@ -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',)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue