1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-17 05:41:20 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Emmy D'Anello
2a298a3ee4
Reporter -> reviewer
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-05 11:00:11 +02:00
Emmy D'Anello
05c6333c5e
Translate draw messages
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-05 10:41:48 +02:00
16 changed files with 594 additions and 275 deletions

View File

@ -122,6 +122,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\
.prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget()
translation.activate(settings.PREFERRED_LANGUAGE_CODE)
match content['type']:
case 'set_language':
# Update the translation language
@ -233,8 +235,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': 'Tirage au sort du TFJM²',
'body': "Le tirage au sort du tournoi de "
f"{self.tournament.name} a commencé !"})
'body': _("The draw of tournament {tournament} started!")
.format(tournament=self.tournament.name)})
async def draw_start(self, content) -> None:
"""
@ -403,8 +405,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(
f"team-{dup.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²',
'body': 'Votre score de dé est identique à celui de une ou plusieurs équipes. '
'Veuillez le relancer.'}
'body': _("Your dice score is identical to the one of one or multiple teams. "
"Please relaunch it.")}
)
# Alert the tournament
await self.channel_layer.group_send(
@ -448,7 +450,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# We can add a joker team if there is not already a team in the pool that was in the same pool
# in the first round, and such that the number of such jokers is exactly the free space of the current pool.
# Exception: if there is one only pool with 5 teams, we exchange the first and the last teams of the pool.
if not self.tournament.final:
if not self.tournament.final and settings.TFJM_APP == "TFJM":
tds_copy = sorted(tds, key=lambda td: (td.passage_index, -td.pool.letter,))
jokers = [td for td in tds if td.passage_index == 4]
round2 = await self.tournament.draw.round_set.filter(number=2).aget()
@ -502,12 +504,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.tournament.draw.current_round.asave()
# Display dice result in the header of the information alert
msg = "Les résultats des dés sont les suivants : "
msg += ", ".join(f"<strong>{td.participation.team.trigram}</strong> ({td.passage_dice})" for td in tds)
msg += ". L'ordre de passage et les compositions des différentes poules sont affiché⋅es sur le côté. "
msg += "Les ordres de passage pour le premier tour sont déterminés à partir des scores des dés, "
msg += "dans l'ordre croissant. Pour le deuxième tour, les ordres de passage sont déterminés à partir "
msg += "des ordres de passage du premier tour."
trigrams = ", ".join(f"<strong>{td.participation.team.trigram}</strong> ({td.passage_dice})" for td in tds)
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.")
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -610,8 +611,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{tds[0].participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
'title': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
async def select_problem(self, **kwargs):
"""
@ -631,7 +632,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
.prefetch_related('team').aget()
# Ensure that the user can draws a problem at this time
if participation.id != td.participation_id:
return await self.alert("This is not your turn.", 'danger')
return await self.alert(_("This is not your turn."), 'danger')
while True:
# Choose a random problem
@ -702,19 +703,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
.prefetch_related('team').aget()
# Ensure that the user can accept a problem at this time
if participation.id != td.participation_id:
return await self.alert("This is not your turn.", 'danger')
return await self.alert(_("This is not your turn."), 'danger')
td.accepted = td.purposed
td.purposed = None
await td.asave()
trigram = td.participation.team.trigram
msg = f"L'équipe <strong>{trigram}</strong> a accepté le problème <strong>{td.accepted} : " \
f"{settings.PROBLEMS[td.accepted - 1]}</strong>. "
msg = _("The team <strong>{trigram}</strong> accepted the problem <string>{problem}</strong>: "
"{problem_name}. ").format(trigram=trigram, problem=td.accepted,
problem_name=settings.PROBLEMS[td.accepted - 1])
if pool.size == 5 and await pool.teamdraw_set.filter(accepted=td.accepted).acount() < 2:
msg += "Une équipe peut encore l'accepter."
msg += _("One team more can accept this problem.")
else:
msg += "Plus personne ne peut l'accepter."
msg += _("No team can accept this problem anymore.")
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -749,8 +751,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
'title': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
else:
# Pool is ended
await self.end_pool(pool)
@ -808,8 +810,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'problems': [td.accepted async for td in pool.team_draws],
})
msg += f"<br><br>Le tirage de la poule {pool.get_letter_display()}{r.number} est terminé. " \
f"Le tableau récapitulatif est en bas."
msg += "<br><br>" + _("The draw of the pool {pool} is ended. The summary is below.") \
.format(pool=f"{pool.get_letter_display()}{r.number}")
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -826,8 +828,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
'title': _("Your turn!"),
'body': _("It's your turn to launch the dice!")})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
@ -843,11 +845,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
msg = self.tournament.draw.last_message
if r.number < settings.NB_ROUNDS and not self.tournament.final:
if r.number < settings.NB_ROUNDS and not self.tournament.final and settings.TFJM_APP == "TFJM":
# Next round
next_round = await self.tournament.draw.round_set.filter(number=r.number + 1).aget()
self.tournament.draw.current_round = next_round
msg += f"<br><br>Le tirage au sort du tour {r.number} est terminé."
msg += "<br><br>" + _("The draw of the round {round} is ended.").format(round=r.number)
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -860,8 +862,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
'title': _("Your turn!"),
'body': _("It's your turn to launch the dice!")})
# Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -888,9 +890,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
elif r.number == 1 and self.tournament.final:
elif r.number == 1 and (self.tournament.final or settings.TFJM_APP == "ETEAM"):
# For the final tournament, we wait for a manual update between the two rounds.
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
msg += "<br><br>" + _("The draw of the first round is ended.")
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -919,7 +921,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
.prefetch_related('team').aget()
# Ensure that the user can reject a problem at this time
if participation.id != td.participation_id:
return await self.alert("This is not your turn.", 'danger')
return await self.alert(_("This is not your turn."), 'danger')
# Add the problem to the rejected problems list
problem = td.purposed
@ -933,15 +935,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update messages
trigram = td.participation.team.trigram
msg = f"L'équipe <strong>{trigram}</strong> a refusé le problème <strong>{problem} : " \
f"{settings.PROBLEMS[problem - 1]}</strong>. "
msg = _("The team <strong>{trigram}</strong> refused the problem <strong>{problem}</strong>: "
"{problem_name}.").format(trigram=trigram, problem=problem,
problem_name=settings.PROBLEMS[problem - 1]) + " "
if remaining >= 0:
msg += f"Il lui reste {remaining} refus sans pénalité."
msg += _("It remains {remaining} refusals without penalty.").format(remaining=remaining)
else:
if already_refused:
msg += "Cela n'ajoute pas de pénalité."
msg += _("This problem was already refused by this team.")
else:
msg += "Cela ajoute une pénalité de 25&nbsp;% sur le coefficient de l'oral de la défense."
msg += _("It adds a 25% penalty on the coefficient of the oral defense.")
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -984,8 +987,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
'title': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
@ensure_orga
async def export(self, **kwargs):
@ -1022,17 +1025,17 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
self.tournament.draw.current_round = r2
msg = "Le tirage au sort pour le tour 2 va commencer. " \
"L'ordre de passage est déterminé à partir du classement du premier tour, " \
"de sorte à mélanger les équipes entre les deux jours."
msg = _("The draw of the round 2 is starting. "
"The passage order is determined from the ranking of the first round, "
"in order to mix the teams between the two days.")
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
# Send notification to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': 'Tirage au sort du TFJM²',
'body': "Le tirage au sort pour le second tour de la finale a commencé !"})
'title': _("Draw") + " " + settings.APP_NAME,
'body': _("The draw of the second round is starting!")})
# Set the first pool of the second round as the active pool
pool = await Pool.objects.filter(round=self.tournament.draw.current_round, letter=1).aget()
@ -1082,8 +1085,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
'title': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',

View File

@ -110,58 +110,61 @@ class Draw(models.Model):
# Waiting for dices to determine pools and passage order
if self.current_round.number == 1:
# Specific information for the first round
s += """Nous allons commencer le tirage des problèmes.<br>
Vous pouvez à tout moment poser toute question si quelque chose
n'est pas clair ou ne va pas.<br><br>
Nous allons d'abord tirer les poules et l'ordre de passage
pour le premier tour avec toutes les équipes puis pour chaque poule,
nous tirerons l'ordre de tirage pour le tour et les problèmes.<br><br>"""
s += """
Les capitaines, vous pouvez désormais toustes lancer un 100,
en cliquant sur le gros bouton. Les poules et l'ordre de passage
lors du premier tour sera l'ordre croissant des dés, c'est-à-dire
que le plus petit lancer sera le premier à passer dans la poule A."""
s += _("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 and the passage order for the first round "
"with all the teams, then for each pool, we will draw the draw order and the problems.")
s += "<br><br>"
s += _("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 the increasing order "
"of the dices, ie. the smallest dice will be the first to pass in pool A.")
case 'DICE_ORDER_POULE':
# Waiting for dices to determine the choice order
s += f"""Nous passons au tirage des problèmes pour la poule
<strong>{self.current_round.current_pool}</strong>, entre les équipes
<strong>{', '.join(td.participation.team.trigram
for td in self.current_round.current_pool.teamdraw_set.all())}</strong>.
Les capitaines peuvent lancer un 100 en cliquant sur le gros bouton
pour déterminer l'ordre de tirage. L'équipe réalisant le plus gros score pourra
tirer en premier."""
s += _("We are going to start the problem draw for the pool <strong>{pool}</strong>, "
"between the teams <strong>{teams}</strong>. "
"The captains can throw a 100-sided dice by clicking on the big dice button "
"to determine the order of draw. The team with the highest score will draw first.") \
.format(pool=self.current_round.current_pool,
teams=', '.join(td.participation.team.trigram
for td in self.current_round.current_pool.teamdraw_set.all()))
case 'WAITING_DRAW_PROBLEM':
# Waiting for a problem draw
td = self.current_round.current_pool.current_team
s += f"""C'est au tour de l'équipe <strong>{td.participation.team.trigram}</strong>
de choisir son problème. Cliquez sur l'urne au milieu pour tirer un problème au sort."""
s += _("The team <strong>{trigram}</strong> is going to draw a problem. "
"Click on the urn in the middle to draw a problem.") \
.format(trigram=td.participation.team.trigram)
case 'WAITING_CHOOSE_PROBLEM':
# Waiting for the team that can accept or reject the problem
td = self.current_round.current_pool.current_team
s += f"""L'équipe <strong>{td.participation.team.trigram}</strong> a tiré le problème
<strong>{td.purposed} : {settings.PROBLEMS[td.purposed - 1]}</strong>. """
s += _("The team <strong>{trigram}</strong> drew the problem <strong>{problem}: "
"{problem_name}</strong>.") \
.format(trigram=td.participation.team.trigram,
problem=td.purposed, problem_name=settings.PROBLEMS[td.purposed - 1]) + " "
if td.purposed in td.rejected:
# The problem was previously rejected
s += """Elle a déjà refusé ce problème auparavant, elle peut donc le refuser sans pénalité et
tirer un nouveau problème immédiatement, ou bien revenir sur son choix."""
s += _("It already refused this problem before, so it can refuse it without penalty and "
"draw a new problem immediately, or change its mind.")
else:
# The problem can be rejected
s += "Elle peut décider d'accepter ou de refuser ce problème. "
s += _("It can decide to accept or refuse this problem.") + " "
if len(td.rejected) >= len(settings.PROBLEMS) - settings.RECOMMENDED_SOLUTIONS_COUNT:
s += "Refuser ce problème ajoutera une nouvelle pénalité de 25 % sur le coefficient de l'oral de la défense."
s += _("Refusing this problem will add a new 25% penalty "
"on the coefficient of the oral defense.")
else:
s += f"Il reste {len(settings.PROBLEMS) - settings.RECOMMENDED_SOLUTIONS_COUNT - len(td.rejected)} refus sans pénalité."
s += _("There are still {remaining} refusals without penalty.").format(
remaining=len(settings.PROBLEMS) - settings.RECOMMENDED_SOLUTIONS_COUNT - len(td.rejected))
case 'WAITING_FINAL':
# We are between the two rounds of the final tournament
s += "Le tirage au sort pour le tour 2 aura lieu à la fin du premier tour. Bon courage !"
s += _("The draw for the second round will take place at the end of the first round. Good luck!")
case 'DRAW_ENDED':
# The draw is ended
s += "Le tirage au sort est terminé. Les solutions des autres équipes peuvent être trouvées dans l'onglet « Ma participation »."
s += _("The draw is ended. The solutions of the other teams can be found in the tab "
"\"My participation\".")
s += "<br><br>" if s else ""
s += """Pour plus de détails sur le déroulement du tirage au sort,
le règlement est accessible sur
<a class="alert-link" href="https://tfjm.org/reglement">https://tfjm.org/reglement</a>."""
rules_link = "https://tfjm.org/reglement" if settings.TFJM_APP == "TFJM" else "https://eteam.tfjm.org/rules/"
s += _("For more details on the draw, the rules are available on "
"<a class=\"alert-link\" href=\"{link}\">{link}</a>.").format(link=rules_link)
return s
async def ainformation(self) -> str:
@ -412,7 +415,7 @@ class Pool(models.Model):
solution_number=tds[line[0]].accepted,
defender=tds[line[0]].participation,
opponent=tds[line[1]].participation,
reporter=tds[line[2]].participation,
reviewer=tds[line[2]].participation,
defender_penalties=tds[line[0]].penalty_int,
)

View File

@ -529,37 +529,37 @@ document.addEventListener('DOMContentLoaded', () => {
opponentTd.classList.add('text-center')
opponentTd.innerText = 'Opp'
let reporterTd = document.createElement('td')
reporterTd.classList.add('text-center')
reporterTd.innerText = 'Rap'
let reviewerTd = document.createElement('td')
reviewerTd.classList.add('text-center')
reviewerTd.innerText = 'Rap'
// Put the cells in their right places, according to the pool size and the row number.
if (poule.teams.length === 3) {
switch (i) {
case 0:
teamTr.append(defenderTd, reporterTd, opponentTd)
teamTr.append(defenderTd, reviewerTd, opponentTd)
break
case 1:
teamTr.append(opponentTd, defenderTd, reporterTd)
teamTr.append(opponentTd, defenderTd, reviewerTd)
break
case 2:
teamTr.append(reporterTd, opponentTd, defenderTd)
teamTr.append(reviewerTd, opponentTd, defenderTd)
break
}
} else if (poule.teams.length === 4) {
let emptyTd = document.createElement('td')
switch (i) {
case 0:
teamTr.append(defenderTd, emptyTd, reporterTd, opponentTd)
teamTr.append(defenderTd, emptyTd, reviewerTd, opponentTd)
break
case 1:
teamTr.append(opponentTd, defenderTd, emptyTd, reporterTd)
teamTr.append(opponentTd, defenderTd, emptyTd, reviewerTd)
break
case 2:
teamTr.append(reporterTd, opponentTd, defenderTd, emptyTd)
teamTr.append(reviewerTd, opponentTd, defenderTd, emptyTd)
break
case 3:
teamTr.append(emptyTd, reporterTd, opponentTd, defenderTd)
teamTr.append(emptyTd, reviewerTd, opponentTd, defenderTd)
break
}
} else if (poule.teams.length === 5) {
@ -567,19 +567,19 @@ document.addEventListener('DOMContentLoaded', () => {
let emptyTd2 = document.createElement('td')
switch (i) {
case 0:
teamTr.append(defenderTd, emptyTd, opponentTd, reporterTd, emptyTd2)
teamTr.append(defenderTd, emptyTd, opponentTd, reviewerTd, emptyTd2)
break
case 1:
teamTr.append(emptyTd, defenderTd, reporterTd, emptyTd2, opponentTd)
teamTr.append(emptyTd, defenderTd, reviewerTd, emptyTd2, opponentTd)
break
case 2:
teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reporterTd)
teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reviewerTd)
break
case 3:
teamTr.append(reporterTd, opponentTd, emptyTd, defenderTd, emptyTd2)
teamTr.append(reviewerTd, opponentTd, emptyTd, defenderTd, emptyTd2)
break
case 4:
teamTr.append(emptyTd, reporterTd, emptyTd2, opponentTd, defenderTd)
teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, defenderTd)
break
}
}

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-13 10:56+0200\n"
"POT-Creation-Date: 2024-07-05 10:48+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"
@ -93,7 +93,7 @@ msgstr ""
"Pour une permission qui concerne un tournoi, indique quel est le tournoi "
"concerné."
#: chat/models.py:73 draw/models.py:429 draw/models.py:456
#: 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
#: participation/tables.py:84
@ -264,12 +264,12 @@ msgstr "Connexion"
msgid "teams"
msgstr "équipes"
#: draw/admin.py:92 draw/models.py:234 draw/models.py:448
#: draw/admin.py:92 draw/models.py:237 draw/models.py:451
#: participation/models.py:1016
msgid "round"
msgstr "tour"
#: draw/apps.py:10 tfjm/templates/navbar.html:68
#: draw/apps.py:10 draw/consumers.py:1037 tfjm/templates/navbar.html:68
msgid "Draw"
msgstr "Tirage au sort"
@ -277,67 +277,182 @@ msgstr "Tirage au sort"
msgid "You are not an organizer."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
#: draw/consumers.py:165
#: draw/consumers.py:167
msgid "The draw is already started."
msgstr "Le tirage a déjà commencé."
#: draw/consumers.py:171
#: draw/consumers.py:173
msgid "Invalid format"
msgstr "Format invalide"
#: draw/consumers.py:176
#: draw/consumers.py:178
#, python-brace-format
msgid "The sum must be equal to the number of teams: expected {len}, got {sum}"
msgstr ""
"La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}"
#: draw/consumers.py:181
#: draw/consumers.py:183
msgid "There can be at most one pool with 5 teams."
msgstr "Il ne peut y avoir au plus qu'une seule poule de 5 équipes."
#: draw/consumers.py:221
#: draw/consumers.py:223
msgid "Draw started!"
msgstr "Le tirage a commencé !"
#: draw/consumers.py:243
#: draw/consumers.py:238
#, python-brace-format
msgid "The draw of tournament {tournament} started!"
msgstr "Le tirage au sort du tournoi {tournament} a commencé !"
#: draw/consumers.py:245
#, python-brace-format
msgid "The draw for the tournament {tournament} will start."
msgstr "Le tirage au sort du tournoi {tournament} va commencer."
#: draw/consumers.py:254 draw/consumers.py:280 draw/consumers.py:690
#: draw/consumers.py:907 draw/consumers.py:996 draw/consumers.py:1018
#: draw/consumers.py:1109 draw/templates/draw/tournament_content.html:5
#: draw/consumers.py:256 draw/consumers.py:282 draw/consumers.py:691
#: draw/consumers.py:909 draw/consumers.py:999 draw/consumers.py:1021
#: draw/consumers.py:1112 draw/templates/draw/tournament_content.html:5
msgid "The draw has not started yet."
msgstr "Le tirage au sort n'a pas encore commencé."
#: draw/consumers.py:267
#: draw/consumers.py:269
#, python-brace-format
msgid "The draw for the tournament {tournament} is aborted."
msgstr "Le tirage au sort du tournoi {tournament} est annulé."
#: draw/consumers.py:307 draw/consumers.py:328 draw/consumers.py:624
#: draw/consumers.py:695 draw/consumers.py:912
#: draw/consumers.py:309 draw/consumers.py:330 draw/consumers.py:625
#: draw/consumers.py:696 draw/consumers.py:914
msgid "This is not the time for this."
msgstr "Ce n'est pas le moment pour cela."
#: draw/consumers.py:320 draw/consumers.py:323
#: draw/consumers.py:322 draw/consumers.py:325
msgid "You've already launched the dice."
msgstr "Vous avez déjà lancé le dé."
#: draw/consumers.py:326
#: draw/consumers.py:328
msgid "It is not your turn."
msgstr "Ce n'est pas votre tour."
#: draw/consumers.py:413
#: draw/consumers.py:408
msgid ""
"Your dice score is identical to the one of one or multiple teams. Please "
"relaunch it."
msgstr ""
"Votre score de dé est identique à celui d'une ou plusieurs équipes. Merci de "
"le relancer."
#: draw/consumers.py:415
#, python-brace-format
msgid "Dices from teams {teams} are identical. Please relaunch your dices."
msgstr ""
"Les dés des équipes {teams} sont identiques. Merci de relancer vos dés."
#: draw/consumers.py:1021
#: draw/consumers.py:508
#, python-brace-format
msgid ""
"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."
msgstr ""
"Les résultats des dés sont les suivants : {trigrams}. L'ordre de passage et "
"les compositions des différentes poules sont affichés sur le côté. Les "
"ordres de passage pour le premier tour sont déterminés à partir des scores "
"de dés, par ordre croissant. Pour le deuxième tour, les ordres de passage "
"sont déterminés à partir des ordres de passage du premier tour."
#: draw/consumers.py:614 draw/consumers.py:754 draw/consumers.py:831
#: draw/consumers.py:865 draw/consumers.py:990 draw/consumers.py:1088
msgid "Your turn!"
msgstr "À votre tour !"
#: draw/consumers.py:615 draw/consumers.py:755 draw/consumers.py:991
#: draw/consumers.py:1089
msgid "It's your turn to draw a problem!"
msgstr "C'est à vous de tirer un problème !"
#: draw/consumers.py:635 draw/consumers.py:706 draw/consumers.py:924
msgid "This is not your turn."
msgstr "Ce n'est pas votre tour."
#: draw/consumers.py:713
#, python-brace-format
msgid ""
"The team <strong>{trigram}</strong> accepted the problem <string>{problem}</"
"strong>: {problem_name}. "
msgstr ""
"L'équipe <strong>{trigram}</strong> a accepté le problème <strong>{problem}</"
"strong> : {problem_name}. "
#: draw/consumers.py:717
msgid "One team more can accept this problem."
msgstr "Une équipe de plus peut accepter ce problème."
#: draw/consumers.py:719
msgid "No team can accept this problem anymore."
msgstr "Aucune autre équipe ne peut accepter ce problème."
#: draw/consumers.py:813
#, python-brace-format
msgid "The draw of the pool {pool} is ended. The summary is below."
msgstr "Le tirage de la poule {pool} est terminé. Le résumé est ci-dessous."
#: draw/consumers.py:832 draw/consumers.py:866
msgid "It's your turn to launch the dice!"
msgstr "C'est à vous de lancer le dé !"
#: draw/consumers.py:852
#, python-brace-format
msgid "The draw of the round {round} is ended."
msgstr "Le tirage au sort du tour {round} est annulé."
#: draw/consumers.py:895
msgid "The draw of the first round is ended."
msgstr "Le tirage au sort du premier tour est terminé."
#: draw/consumers.py:938
#, python-brace-format
msgid ""
"The team <strong>{trigram}</strong> refused the problem <strong>{problem}</"
"strong>: {problem_name}."
msgstr ""
"L'équipe <strong>{trigram}</strong> a refusé le problème <strong>{problem}</"
"strong> : {problem_name}."
#: draw/consumers.py:942
#, python-brace-format
msgid "It remains {remaining} refusals without penalty."
msgstr "Il reste {remaining} refus sans pénalité."
#: draw/consumers.py:945
msgid "This problem was already refused by this team."
msgstr "Ce problème a déjà été refusé par cette équipe."
#: draw/consumers.py:947
msgid "It adds a 25% penalty on the coefficient of the oral defense."
msgstr ""
"Cela ajoute une pénalité de 25&nbsp;% sur le coefficient de l'oral de la "
"défense."
#: draw/consumers.py:1024
msgid "This is only available for the final tournament."
msgstr "Cela n'est possible que pour la finale."
#: draw/consumers.py:1028
msgid ""
"The draw of the round 2 is starting. The passage order is determined from "
"the ranking of the first round, in order to mix the teams between the two "
"days."
msgstr ""
"Le tirage au sort du tour 2 commence. L'ordre de passage est déterminé à "
"partir du classement du premier tour, afin de mélanger les équipes entre les "
"deux jours."
#: draw/consumers.py:1038
msgid "The draw of the second round is starting!"
msgstr "Le tirage au sort du deuxième tour commence !"
#: draw/models.py:27
msgid "The associated tournament."
msgstr "Le tournoi associé."
@ -362,154 +477,261 @@ msgstr "Le dernier message qui est affiché sur l'interface de tirage."
msgid "State"
msgstr "État"
#: draw/models.py:174
#: draw/models.py:113
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 "
"and the passage order for the first round with all the teams, then for each "
"pool, we will draw the draw order and the problems."
msgstr ""
"Nous allons commencer le tirage des problèmes.<br>Vous pouvez poser des "
"questions si quelque chose n'est pas clair ou faux.<br><br>Nous allons "
"d'abord tirer les poules et l'ordre de passage pour le premier tour avec "
"toutes les équipes, puis pour chaque poule, nous allons tirer l'ordre de "
"tirage et les problèmes."
#: draw/models.py:118
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 "
"the increasing order of the dices, ie. the smallest dice will be the first "
"to pass in pool A."
msgstr ""
"Les capitaines, vous pouvez maintenant tous lancer un dé à 100 faces, en "
"cliquant sur le gros bouton de dé. Les poules et l'ordre de passage pendant "
"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
#, python-brace-format
msgid ""
"We are going to start the problem draw for the pool <strong>{pool}</strong>, "
"between the teams <strong>{teams}</strong>. The captains can throw a 100-"
"sided dice by clicking on the big dice button to determine the order of "
"draw. The team with the highest score will draw first."
msgstr ""
"Nous allons commencer le tirage des problèmes pour la poule <strong>{pool}</"
"strong>, entre les équipes <strong>{teams}</strong>. Les capitaines peuvent "
"lancer un dé à 100 faces en cliquant sur le gros bouton de dé pour "
"déterminer l'ordre de tirage. L'équipe avec le score le plus élevé tirera en "
"premier."
#: draw/models.py:133
#, python-brace-format
msgid ""
"The team <strong>{trigram}</strong> is going to draw a problem. Click on the "
"urn in the middle to draw a problem."
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
#, python-brace-format
msgid ""
"The team <strong>{trigram}</strong> drew the problem <strong>{problem}: "
"{problem_name}</strong>."
msgstr ""
"L'équipe <strong>{trigram}</strong> a tiré le problème <strong>{problem} : "
"{problem_name}</strong>."
#: draw/models.py:145
msgid ""
"It already refused this problem before, so it can refuse it without penalty "
"and draw a new problem immediately, or change its mind."
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
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
msgid ""
"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
#, python-brace-format
msgid "There are still {remaining} refusals without penalty."
msgstr "Il reste {remaining} refus sans pénalité."
#: draw/models.py:158
msgid ""
"The draw for the second round will take place at the end of the first round. "
"Good luck!"
msgstr ""
"Le tirage au sort du deuxième tour aura lieu à la fin du premier tour. Bonne "
"chance !"
#: draw/models.py:161
msgid ""
"The draw is ended. The solutions of the other teams can be found in the tab "
"\"My participation\"."
msgstr ""
"Le tirage est terminé. Les solutions des autres équipes peuvent être "
"trouvées dans l'onglet « Ma participation »."
#: draw/models.py:166
#, python-brace-format
msgid ""
"For more details on the draw, the rules are available on <a class=\"alert-"
"link\" href=\"{link}\">{link}</a>."
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
#, python-brace-format
msgid "Draw of tournament {tournament}"
msgstr "Tirage au sort du tournoi {tournament}"
#: draw/models.py:177 draw/models.py:189
#: draw/models.py:180 draw/models.py:192
msgid "draw"
msgstr "tirage au sort"
#: draw/models.py:178
#: draw/models.py:181
msgid "draws"
msgstr "tirages au sort"
#: draw/models.py:194
#: draw/models.py:197
msgid "Round 1"
msgstr "Tour 1"
#: draw/models.py:195
#: draw/models.py:198
msgid "Round 2"
msgstr "Tour 2"
#: draw/models.py:196
#: draw/models.py:199
msgid "Round 3"
msgstr "Tour 3"
#: draw/models.py:197
#: draw/models.py:200
msgid "number"
msgstr "numéro"
#: draw/models.py:198
#: draw/models.py:201
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:208
#: draw/models.py:211
msgid "current pool"
msgstr "poule actuelle"
#: draw/models.py:209
#: draw/models.py:212
msgid "The current pool where teams select their problems."
msgstr "La poule en cours, où les équipes choisissent leurs problèmes"
#: draw/models.py:235
#: draw/models.py:238
msgid "rounds"
msgstr "tours"
#: draw/models.py:257 participation/models.py:1024
#: draw/models.py:260 participation/models.py:1024
msgid "letter"
msgstr "lettre"
#: draw/models.py:258
#: draw/models.py:261
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:262
#: draw/models.py:265
#: participation/templates/participation/tournament_detail.html:15
msgid "size"
msgstr "taille"
#: draw/models.py:264
#: draw/models.py:267
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:273
#: draw/models.py:276
msgid "current team"
msgstr "équipe actuelle"
#: draw/models.py:274
#: draw/models.py:277
msgid "The current team that is selecting its problem."
msgstr "L'équipe qui est en train de choisir son problème."
#: draw/models.py:283
#: draw/models.py:286
msgid "associated pool"
msgstr "poule associée"
#: draw/models.py:284
#: draw/models.py:287
msgid "The full pool instance."
msgstr "L'instance complète de la poule."
#: draw/models.py:426
#: draw/models.py:429
#, python-brace-format
msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}"
#: draw/models.py:430 participation/models.py:1516
#: draw/models.py:433 participation/models.py:1516
msgid "pools"
msgstr "poules"
#: draw/models.py:442 participation/models.py:1002 participation/models.py:1665
#: draw/models.py:445 participation/models.py:1002 participation/models.py:1665
#: participation/models.py:1695 participation/models.py:1737
msgid "participation"
msgstr "participation"
#: draw/models.py:463
#: draw/models.py:466
msgid "passage index"
msgstr "numéro de passage"
#: draw/models.py:464
#: draw/models.py:467
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:472
#: draw/models.py:475
msgid "choose index"
msgstr "numéro de choix"
#: draw/models.py:473
#: draw/models.py:476
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:479 draw/models.py:502 participation/models.py:1538
#: draw/models.py:482 draw/models.py:505 participation/models.py:1538
#: participation/models.py:1702
#, python-brace-format
msgid "Problem #{problem}"
msgstr "Problème n°{problem}"
#: draw/models.py:483
#: draw/models.py:486
msgid "accepted problem"
msgstr "problème accepté"
#: draw/models.py:490
#: draw/models.py:493
msgid "passage dice"
msgstr "dé d'ordre de passage"
#: draw/models.py:497
#: draw/models.py:500
msgid "choice dice"
msgstr "dé d'ordre de choix"
#: draw/models.py:506
#: draw/models.py:509
msgid "purposed problem"
msgstr "problème proposé"
#: draw/models.py:511
#: draw/models.py:514
msgid "rejected problems"
msgstr "problèmes rejetés"
#: draw/models.py:540
#: draw/models.py:543
#, 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:546
#: draw/models.py:549
msgid "team draw"
msgstr "tirage d'équipe"
#: draw/models.py:547
#: draw/models.py:550
msgid "team draws"
msgstr "tirages d'équipe"
@ -702,7 +924,7 @@ msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:1559
#: participation/models.py:1750
msgid "reporter"
msgid "reviewer"
msgstr "rapporteur⋅rice"
#: participation/admin.py:187 participation/models.py:1700
@ -794,7 +1016,7 @@ msgid "The following user was not found:"
msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :"
#: participation/forms.py:349
msgid "The defender, the opponent and the reporter must be different."
msgid "The defender, the opponent and the reviewer must be different."
msgstr ""
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
@ -1175,11 +1397,11 @@ msgstr ""
#: participation/models.py:966
#, python-brace-format
msgid ""
"<p>You will report the solution of the team {reporter} on the <a "
"<p>You will report the solution of the team {reviewer} 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 rapporterez la solution de l'équipe {reporter} sur le <a "
"<p>Vous rapporterez la solution de l'équipe {reviewer} 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>"
@ -1409,11 +1631,11 @@ msgid "opponent oral note"
msgstr "note d'oral opposant⋅e"
#: participation/models.py:1815
msgid "reporter writing note"
msgid "reviewer writing note"
msgstr "note d'écrit rapporteur⋅rice"
#: participation/models.py:1821
msgid "reporter oral note"
msgid "reviewer oral note"
msgstr "note d'oral du rapporteur⋅rice"
#: participation/models.py:1881
@ -1592,7 +1814,7 @@ msgid "Opponent:"
msgstr "Opposant⋅e :"
#: participation/templates/participation/passage_detail.html:34
msgid "Reporter:"
msgid "reviewer:"
msgstr "Rapporteur⋅rice :"
#: participation/templates/participation/passage_detail.html:37
@ -1644,11 +1866,11 @@ msgid "Average points for the opponent oral"
msgstr "Moyenne de l'oral de l'équipe opposante"
#: participation/templates/participation/passage_detail.html:101
msgid "Average points for the reporter writing"
msgid "Average points for the reviewer writing"
msgstr "Moyenne de l'écrit de l'équipe rapportrice"
#: participation/templates/participation/passage_detail.html:107
msgid "Average points for the reporter oral"
msgid "Average points for the reviewer oral"
msgstr "Moyenne de l'oral de l'équipe rapportrice"
#: participation/templates/participation/passage_detail.html:117
@ -1660,7 +1882,7 @@ msgid "Opponent points"
msgstr "Points de l'équipe opposante"
#: participation/templates/participation/passage_detail.html:129
msgid "Reporter points"
msgid "reviewer points"
msgstr "Points de l'équipe rapportrice"
#: participation/templates/participation/passage_detail.html:139

View File

@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline):
model = Passage
extra = 0
ordering = ('position',)
autocomplete_fields = ('defender', 'opponent', 'reporter',)
autocomplete_fields = ('defender', 'opponent', 'reviewer',)
show_change_link = True
@ -113,12 +113,12 @@ class PoolAdmin(admin.ModelAdmin):
@admin.register(Passage)
class PassageAdmin(admin.ModelAdmin):
list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reporter_trigram',
list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reviewer_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', 'reporter',)
autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer',)
inlines = (NoteInline,)
@admin.display(description=_("defender"), ordering='defender__team__trigram')
@ -129,9 +129,9 @@ class PassageAdmin(admin.ModelAdmin):
def opponent_trigram(self, record: Passage):
return record.opponent.team.trigram
@admin.display(description=_("reporter"), ordering='reporter__team__trigram')
def reporter_trigram(self, record: Passage):
return record.reporter.team.trigram
@admin.display(description=_("reviewer"), ordering='reviewer__team__trigram')
def reviewer_trigram(self, record: Passage):
return record.reviewer.team.trigram
@admin.display(description=_("pool"), ordering='pool__letter')
def pool_abbr(self, record):
@ -145,10 +145,10 @@ class PassageAdmin(admin.ModelAdmin):
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
list_display = ('passage', 'pool', 'jury', 'defender_writing', 'defender_oral',
'opponent_writing', 'opponent_oral', 'reporter_writing', 'reporter_oral',)
'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',)
list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury',
'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
'reporter_writing', 'reporter_oral')
'reviewer_writing', 'reviewer_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', 'reporter_writing', 'reporter_oral', ]
'opponent_oral', 'reviewer_writing', 'reviewer_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', 'reporter', 'pool_tournament', ]
filterset_fields = ['pool', 'solution_number', 'defender', 'opponent', 'reviewer', 'pool_tournament', ]
class PoolViewSet(ModelViewSet):

View File

@ -344,9 +344,9 @@ class UploadNotesForm(forms.Form):
class PassageForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()
if "defender" in cleaned_data and "opponent" in cleaned_data and "reporter" in cleaned_data \
and len({cleaned_data["defender"], cleaned_data["opponent"], cleaned_data["reporter"]}) < 3:
self.add_error(None, _("The defender, the opponent and the reporter must be different."))
if "defender" in cleaned_data and "opponent" in cleaned_data and "reviewer" in cleaned_data \
and len({cleaned_data["defender"], cleaned_data["opponent"], cleaned_data["reviewer"]}) < 3:
self.add_error(None, _("The defender, the opponent and the reviewer must be different."))
if "defender" in self.cleaned_data and "solution_number" in self.cleaned_data \
and not Solution.objects.filter(participation=cleaned_data["defender"],
problem=cleaned_data["solution_number"]).exists():
@ -355,7 +355,7 @@ class PassageForm(forms.ModelForm):
class Meta:
model = Passage
fields = ('position', 'solution_number', 'defender', 'opponent', 'reporter', 'defender_penalties',)
fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', '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', 'reporter_writing', 'reporter_oral', )
'opponent_oral', 'reviewer_writing', 'reviewer_oral', )

View File

@ -53,23 +53,23 @@ class Command(BaseCommand):
pool1 = tournament.pools.filter(round=1, participations=team2).first()
defender_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, defender=team2)
opponent_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, opponent=team2)
reporter_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reporter=team2)
reviewer_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reviewer=team2)
pool2 = tournament.pools.filter(round=2, participations=team2).first()
defender_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, defender=team2)
opponent_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, opponent=team2)
reporter_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reporter=team2)
reviewer_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reviewer=team2)
line.append(team2.team.trigram)
line.append(str(pool1.jury_president or ""))
line.append(f"Pb. {defender_passage_1.solution_number}")
line.extend([defender_passage_1.average_defender_writing, defender_passage_1.average_defender_oral,
opponent_passage_1.average_opponent_writing, opponent_passage_1.average_opponent_oral,
reporter_passage_1.average_reporter_writing, reporter_passage_1.average_reporter_oral])
reviewer_passage_1.average_reviewer_writing, reviewer_passage_1.average_reviewer_oral])
line.append(str(pool2.jury_president or ""))
line.append(f"Pb. {defender_passage_2.solution_number}")
line.extend([defender_passage_2.average_defender_writing, defender_passage_2.average_defender_oral,
opponent_passage_2.average_opponent_writing, opponent_passage_2.average_opponent_oral,
reporter_passage_2.average_reporter_writing, reporter_passage_2.average_reporter_oral])
reviewer_passage_2.average_reviewer_writing, reviewer_passage_2.average_reviewer_oral])
line.extend([score2, f"{score1:.1f} ({team1.team.trigram})",
f"{score3:.1f} ({team3.team.trigram})"])

View File

@ -0,0 +1,91 @@
# Generated by Django 5.0.6 on 2024-07-05 08:53
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"participation",
"0017_alter_passage_solution_number_alter_pool_round_and_more",
),
]
operations = [
migrations.RenameField(
model_name="note",
old_name="reporter_oral",
new_name="reviewer_oral",
),
migrations.RenameField(
model_name="note",
old_name="reporter_writing",
new_name="reviewer_writing",
),
migrations.RenameField(
model_name="passage",
old_name="reporter",
new_name="reviewer",
),
migrations.AlterField(
model_name="note",
name="reviewer_oral",
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="reviewer oral note",
),
),
migrations.AlterField(
model_name="note",
name="reviewer_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="reviewer writing note",
),
),
migrations.AlterField(
model_name="passage",
name="reviewer",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="participation.participation",
verbose_name="reviewer",
),
),
migrations.AlterField(
model_name="synthesis",
name="type",
field=models.PositiveSmallIntegerField(
choices=[(1, "opponent"), (2, "reviewer")]
),
),
]

View File

@ -858,7 +858,7 @@ class Participation(models.Model):
elif timezone.now() <= tournament.syntheses_first_phase_limit + timedelta(hours=2):
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)
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reporter=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self)
defender_text = _("<p>The solutions draw is ended. You can check the result on "
"<a href='{draw_url}'>this page</a>.</p>"
@ -878,21 +878,21 @@ class Participation(models.Model):
solution_url=solution_url,
problem=opponent_passage.solution_number, passage_url=passage_url)
reporter_text = _("<p>You will report the solution of the team {reporter} on the "
reviewer_text = _("<p>You will report the solution of the team {reviewer} 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 = reporter_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reporter_passage.pk,))
reporter_content = format_lazy(reporter_text, reporter=reporter_passage.defender.team.trigram,
solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
solution_url=solution_url,
problem=reporter_passage.solution_number, passage_url=passage_url)
problem=reviewer_passage.solution_number, passage_url=passage_url)
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 + reporter_content + syntheses_templates_content
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content
informations.append({
'title': _("First round"),
'type': "info",
@ -902,7 +902,7 @@ class Participation(models.Model):
elif timezone.now() <= tournament.syntheses_second_phase_limit + timedelta(hours=2):
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)
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reporter=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self)
defender_text = _("<p>For the second round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
@ -920,21 +920,21 @@ class Participation(models.Model):
solution_url=solution_url,
problem=opponent_passage.solution_number, passage_url=passage_url)
reporter_text = _("<p>You will report the solution of the team {reporter} on the "
reviewer_text = _("<p>You will report the solution of the team {reviewer} 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 = reporter_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reporter_passage.pk,))
reporter_content = format_lazy(reporter_text, reporter=reporter_passage.defender.team.trigram,
solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
solution_url=solution_url,
problem=reporter_passage.solution_number, passage_url=passage_url)
problem=reviewer_passage.solution_number, passage_url=passage_url)
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 + reporter_content + syntheses_templates_content
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content
informations.append({
'title': _("Second round"),
'type': "info",
@ -945,7 +945,7 @@ class Participation(models.Model):
and timezone.now() <= tournament.syntheses_third_phase_limit + timedelta(hours=2):
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)
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reporter=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self)
defender_text = _("<p>For the third round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
@ -963,21 +963,21 @@ class Participation(models.Model):
solution_url=solution_url,
problem=opponent_passage.solution_number, passage_url=passage_url)
reporter_text = _("<p>You will report the solution of the team {reporter} on the "
reviewer_text = _("<p>You will report the solution of the team {reviewer} 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 = reporter_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reporter_passage.pk,))
reporter_content = format_lazy(reporter_text, reporter=reporter_passage.defender.team.trigram,
solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
solution_url=solution_url,
problem=reporter_passage.solution_number, passage_url=passage_url)
problem=reviewer_passage.solution_number, passage_url=passage_url)
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 + reporter_content + syntheses_templates_content
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content
informations.append({
'title': _("Second round"),
'type': "info",
@ -1132,7 +1132,7 @@ class Pool(models.Model):
for passage in passages), start=["Problème", ""]),
sum(([f"Défenseur⋅se ({passage.defender.team.trigram})", "",
f"Opposant⋅e ({passage.opponent.team.trigram})", "",
f"Rapporteur⋅rice ({passage.reporter.team.trigram})", ""]
f"Rapporteur⋅rice ({passage.reviewer.team.trigram})", ""]
for passage in passages), start=["Rôle", ""]),
sum((["Écrit (/20)", "Oral (/20)", "Écrit (/10)", "Oral (/10)", "Écrit (/10)", "Oral (/10)"]
for _passage in passages), start=["Juré⋅e", ""]),
@ -1144,7 +1144,7 @@ class Pool(models.Model):
for passage in passages:
note = passage.notes.filter(jury=jury).first()
line.extend([note.defender_writing, note.defender_oral, note.opponent_writing, note.opponent_oral,
note.reporter_writing, note.reporter_oral])
note.reviewer_writing, note.reviewer_oral])
notes.append(line)
notes.append([]) # Add empty line to ensure pretty design
@ -1204,18 +1204,18 @@ class Pool(models.Model):
opponent_row = 5 + opponent_passage.pool.juries.count()
opponent_col = opponent_passage.position - 1
reporter_passage = Passage.objects.get(reporter=participation,
reviewer_passage = Passage.objects.get(reviewer=participation,
pool__tournament=self.tournament, pool__round=self.round)
reporter_row = 5 + reporter_passage.pool.juries.count()
reporter_col = reporter_passage.position - 1
reviewer_row = 5 + reviewer_passage.pool.juries.count()
reviewer_col = reviewer_passage.position - 1
formula = "="
formula += (f"'Poule {defender_passage.pool.short_name}'"
f"!{getcol(min_column + defender_col * passage_width)}{defender_row + 3}") # Defender
formula += (f" + 'Poule {opponent_passage.pool.short_name}'"
f"!{getcol(min_column + opponent_col * passage_width + 2)}{opponent_row + 3}") # Opponent
formula += (f" + 'Poule {reporter_passage.pool.short_name}'"
f"!{getcol(min_column + reporter_col * passage_width + 4)}{reporter_row + 3}") # Reporter
formula += (f" + 'Poule {reviewer_passage.pool.short_name}'"
f"!{getcol(min_column + reviewer_col * passage_width + 4)}{reviewer_row + 3}") # reviewer
ranking.append([f"{participation.team.name} ({participation.team.trigram})", "",
f"='Poule {defender_passage.pool.short_name}'"
f"!${getcol(3 + defender_col * passage_width)}$1",
@ -1553,10 +1553,10 @@ class Passage(models.Model):
related_name="+",
)
reporter = models.ForeignKey(
reviewer = models.ForeignKey(
Participation,
on_delete=models.PROTECT,
verbose_name=_("reporter"),
verbose_name=_("reviewer"),
related_name="+",
)
@ -1603,16 +1603,16 @@ class Passage(models.Model):
return 0.9 * self.average_opponent_writing + 2 * self.average_opponent_oral
@property
def average_reporter_writing(self) -> float:
return self.avg(note.reporter_writing for note in self.notes.all())
def average_reviewer_writing(self) -> float:
return self.avg(note.reviewer_writing for note in self.notes.all())
@property
def average_reporter_oral(self) -> float:
return self.avg(note.reporter_oral for note in self.notes.all())
def average_reviewer_oral(self) -> float:
return self.avg(note.reviewer_oral for note in self.notes.all())
@property
def average_reporter(self) -> float:
return 0.9 * self.average_reporter_writing + self.average_reporter_oral
def average_reviewer(self) -> float:
return 0.9 * self.average_reviewer_writing + self.average_reviewer_oral
@property
def averages(self):
@ -1620,12 +1620,12 @@ class Passage(models.Model):
yield self.average_defender_oral
yield self.average_opponent_writing
yield self.average_opponent_oral
yield self.average_reporter_writing
yield self.average_reporter_oral
yield self.average_reviewer_writing
yield self.average_reviewer_oral
def average(self, participation):
return self.average_defender if participation == self.defender else self.average_opponent \
if participation == self.opponent else self.average_reporter if participation == self.reporter else 0
if participation == self.opponent else self.average_reviewer if participation == self.reviewer else 0
def get_absolute_url(self):
return reverse_lazy("participation:passage_detail", args=(self.pk,))
@ -1637,9 +1637,9 @@ class Passage(models.Model):
if self.opponent not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.opponent.team.trigram))
if self.reporter not in self.pool.participations.all():
if self.reviewer not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.reporter.team.trigram))
.format(trigram=self.reviewer.team.trigram))
return super().clean()
def __str__(self):
@ -1747,7 +1747,7 @@ class Synthesis(models.Model):
type = models.PositiveSmallIntegerField(
choices=[
(1, _("opponent"), ),
(2, _("reporter"), ),
(2, _("reviewer"), ),
]
)
@ -1811,14 +1811,14 @@ class Note(models.Model):
default=0,
)
reporter_writing = models.PositiveSmallIntegerField(
verbose_name=_("reporter writing note"),
reviewer_writing = models.PositiveSmallIntegerField(
verbose_name=_("reviewer writing note"),
choices=[(i, i) for i in range(0, 11)],
default=0,
)
reporter_oral = models.PositiveSmallIntegerField(
verbose_name=_("reporter oral note"),
reviewer_oral = models.PositiveSmallIntegerField(
verbose_name=_("reviewer oral note"),
choices=[(i, i) for i in range(0, 11)],
default=0,
)
@ -1828,17 +1828,17 @@ class Note(models.Model):
yield self.defender_oral
yield self.opponent_writing
yield self.opponent_oral
yield self.reporter_writing
yield self.reporter_oral
yield self.reviewer_writing
yield self.reviewer_oral
def set_all(self, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int,
reporter_writing: int, reporter_oral: int):
reviewer_writing: int, reviewer_oral: int):
self.defender_writing = defender_writing
self.defender_oral = defender_oral
self.opponent_writing = opponent_writing
self.opponent_oral = opponent_oral
self.reporter_writing = reporter_writing
self.reporter_oral = reporter_oral
self.reviewer_writing = reviewer_writing
self.reviewer_oral = reviewer_oral
def update_spreadsheet(self):
if not self.has_any_note():

View File

@ -118,7 +118,7 @@ class PassageTable(tables.Table):
def render_opponent(self, value):
return value.team.trigram
def render_reporter(self, value):
def render_reviewer(self, value):
return value.team.trigram
class Meta:
@ -126,7 +126,7 @@ class PassageTable(tables.Table):
'class': 'table table-condensed table-striped text-center',
}
model = Passage
fields = ('defender', 'opponent', 'reporter', 'solution_number', )
fields = ('defender', 'opponent', 'reviewer', 'solution_number', )
class NoteTable(tables.Table):
@ -155,4 +155,4 @@ class NoteTable(tables.Table):
}
model = Note
fields = ('jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
'reporter_writing', 'reporter_oral', 'update',)
'reviewer_writing', 'reviewer_oral', 'update',)

View File

@ -31,8 +31,8 @@
<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 "Reporter:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.reporter.get_absolute_url }}">{{ passage.reporter.team }}</a></dd>
<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>
<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>
@ -98,16 +98,16 @@
<dd class="col-sm-4">{{ passage.average_opponent_oral|floatformat }}/10</dd>
<dt class="col-sm-8">
{% trans "Average points for the reporter writing" %}
({{ passage.reporter.team.trigram }}) :
{% trans "Average points for the reviewer writing" %}
({{ passage.reviewer.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_reporter_writing|floatformat }}/10</dd>
<dd class="col-sm-4">{{ passage.average_reviewer_writing|floatformat }}/10</dd>
<dt class="col-sm-8">
{% trans "Average points for the reporter oral" %}
({{ passage.reporter.team.trigram }}) :
{% trans "Average points for the reviewer oral" %}
({{ passage.reviewer.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_reporter_oral|floatformat }}/10</dd>
<dd class="col-sm-4">{{ passage.average_reviewer_oral|floatformat }}/10</dd>
</dl>
<hr>
@ -126,10 +126,10 @@
<dd class="col-sm-4">{{ passage.average_opponent|floatformat }}/29</dd>
<dt class="col-sm-8">
{% trans "Reporter points" %}
({{ passage.reporter.team.trigram }}) :
{% trans "reviewer points" %}
({{ passage.reviewer.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_reporter|floatformat }}/19</dd>
<dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/19</dd>
</dl>
</div>
</div>

View File

@ -100,7 +100,7 @@
%%%%%%%%%%%%%%%%%%%%%%RAPPORTEUR.RICE
\begin{tabular}{|c|p{24mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline
\multicolumn{4}{|l|}{{\bf Rapporteur\textperiodcentered{}rice} \normalsize \'evalue le d\'ebat entre læ D\'efenseur\textperiodcentered{}se et l'Opposant\textperiodcentered{}e.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.reporter.team.trigram }} {% endfor %}\\ \hline \hline
\multicolumn{4}{|l|}{{\bf Rapporteur\textperiodcentered{}rice} \normalsize \'evalue le d\'ebat entre læ D\'efenseur\textperiodcentered{}se et l'Opposant\textperiodcentered{}e.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.reviewer.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT
\multirow{4}{3mm}{\centering\bf\'E\\ C\\ R\\ I\\ T} &\multirow{3}{20mm}{Partie scientifique} & Recul et esprit critique par rapport à la solution proposée & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}

View File

@ -56,7 +56,7 @@ Tour {{ pool.round }} \;-- Poule {{ pool.get_letter_display }}{% if pool.partici
& \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 10$
& \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 10$
{% endfor %} & \hline
\multirow{2}{35mm}{\LARGE Rapporteur\textperiodcentered{}rice} {% for passage in passages.all %}& \multicolumn{2}{c|}{\Large {{ passage.reporter.team.trigram }}}{% endfor %} \\ \cline{2-{{ passages.count|add:passages.count|add:1 }}}
\multirow{2}{35mm}{\LARGE Rapporteur\textperiodcentered{}rice} {% for passage in passages.all %}& \multicolumn{2}{c|}{\Large {{ passage.reviewer.team.trigram }}}{% endfor %} \\ \cline{2-{{ passages.count|add:passages.count|add:1 }}}
{% for passage in passages.all %}
& \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 10$
& \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 10$

View File

@ -1507,11 +1507,11 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
header_role.addElement(opponent_tc)
header_role.addElement(CoveredTableCell())
reporter_tc = TableCell(valuetype="string",
reviewer_tc = TableCell(valuetype="string",
stylename=title_style_right)
reporter_tc.addElement(P(text="Rapporteur⋅rice"))
reporter_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(reporter_tc)
reviewer_tc.addElement(P(text="Rapporteur⋅rice"))
reviewer_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(reviewer_tc)
header_role.addElement(CoveredTableCell())
# Add maximum notes on the third line
@ -1540,13 +1540,13 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
opponent_o_tc.addElement(P(text="Oral (/10)"))
header_notes.addElement(opponent_o_tc)
reporter_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
reporter_w_tc.addElement(P(text="Écrit (/10)"))
header_notes.addElement(reporter_w_tc)
reviewer_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
reviewer_w_tc.addElement(P(text="Écrit (/10)"))
header_notes.addElement(reviewer_w_tc)
reporter_o_tc = TableCell(valuetype="string", stylename=title_style_botright)
reporter_o_tc.addElement(P(text="Oral (/10)"))
header_notes.addElement(reporter_o_tc)
reviewer_o_tc = TableCell(valuetype="string", stylename=title_style_botright)
reviewer_o_tc.addElement(P(text="Oral (/10)"))
header_notes.addElement(reviewer_o_tc)
# Add a notation line for each jury
for jury in self.object.juries.all():
@ -1617,13 +1617,13 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
opponent_o_tc.addElement(P(text="2"))
coeff_row.addElement(opponent_o_tc)
reporter_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
reporter_w_tc.addElement(P(text="1"))
coeff_row.addElement(reporter_w_tc)
reviewer_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
reviewer_w_tc.addElement(P(text="1"))
coeff_row.addElement(reviewer_w_tc)
reporter_o_tc = TableCell(valuetype="float", value=1, stylename=style_right)
reporter_o_tc.addElement(P(text="1"))
coeff_row.addElement(reporter_o_tc)
reviewer_o_tc = TableCell(valuetype="float", value=1, stylename=style_right)
reviewer_o_tc.addElement(P(text="1"))
coeff_row.addElement(reviewer_o_tc)
# Add the subtotal on the next line
subtotal_row = TableRow()
@ -1656,12 +1656,12 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
rep_w_col = getcol(min_column + passage_width * i + 4)
rep_o_col = getcol(min_column + passage_width * i + 5)
reporter_tc = TableCell(valuetype="float", value=passage.average_reporter, stylename=style_botright)
reporter_tc.addElement(P(text=str(passage.average_reporter)))
reporter_tc.setAttribute('numbercolumnsspanned', "2")
reporter_tc.setAttribute("formula", f"of:=[.{rep_w_col}{max_row + 1}] * [.{rep_w_col}{max_row + 2}]"
reviewer_tc = TableCell(valuetype="float", value=passage.average_reviewer, stylename=style_botright)
reviewer_tc.addElement(P(text=str(passage.average_reviewer)))
reviewer_tc.setAttribute('numbercolumnsspanned', "2")
reviewer_tc.setAttribute("formula", f"of:=[.{rep_w_col}{max_row + 1}] * [.{rep_w_col}{max_row + 2}]"
f" + [.{rep_o_col}{max_row + 1}] * [.{rep_o_col}{max_row + 2}]")
subtotal_row.addElement(reporter_tc)
subtotal_row.addElement(reviewer_tc)
subtotal_row.addElement(CoveredTableCell())
table.addElement(TableRow())
@ -1713,7 +1713,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
defender_pos = passage.position - 1
opponent_pos = self.object.passages.get(opponent=passage.defender).position - 1
reporter_pos = self.object.passages.get(reporter=passage.defender).position - 1
reviewer_pos = self.object.passages.get(reviewer=passage.defender).position - 1
score_tc = TableCell(valuetype="float", value=self.object.average(passage.defender),
stylename=style_bot if passage.position == pool_size else style)
@ -1721,7 +1721,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
formula = "of:="
formula += getcol(min_column + defender_pos * passage_width) + str(max_row + 3) # Defender
formula += " + " + getcol(min_column + opponent_pos * passage_width + 2) + str(max_row + 3) # Opponent
formula += " + " + getcol(min_column + reporter_pos * passage_width + 4) + str(max_row + 3) # Reporter
formula += " + " + getcol(min_column + reviewer_pos * passage_width + 4) + str(max_row + 3) # reviewer
score_tc.setAttribute("formula", formula)
team_row.addElement(score_tc)
@ -1931,7 +1931,7 @@ class PassageDetailView(LoginRequiredMixin, DetailView):
or reg in passage.pool.juries.all()
or reg.pools_presided.filter(tournament=passage.pool.tournament).exists()) \
or reg.participates and reg.team \
and reg.team.participation in [passage.defender, passage.opponent, passage.reporter]:
and reg.team.participation in [passage.defender, passage.opponent, passage.reviewer]:
return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission()
@ -1956,8 +1956,8 @@ class PassageDetailView(LoginRequiredMixin, DetailView):
context['notes'].columns['defender_oral'].column.verbose_name += f" ({passage.defender.team.trigram})"
context['notes'].columns['opponent_writing'].column.verbose_name += f" ({passage.opponent.team.trigram})"
context['notes'].columns['opponent_oral'].column.verbose_name += f" ({passage.opponent.team.trigram})"
context['notes'].columns['reporter_writing'].column.verbose_name += f" ({passage.reporter.team.trigram})"
context['notes'].columns['reporter_oral'].column.verbose_name += f" ({passage.reporter.team.trigram})"
context['notes'].columns['reviewer_writing'].column.verbose_name += f" ({passage.reviewer.team.trigram})"
context['notes'].columns['reviewer_oral'].column.verbose_name += f" ({passage.reviewer.team.trigram})"
return context
@ -1992,7 +1992,7 @@ 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.reporter]:
if self.participation not in [self.passage.opponent, self.passage.reviewer]:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
@ -2050,8 +2050,8 @@ class NoteUpdateView(VolunteerMixin, UpdateView):
form.fields['defender_oral'].label += f" ({self.object.passage.defender.team.trigram})"
form.fields['opponent_writing'].label += f" ({self.object.passage.opponent.team.trigram})"
form.fields['opponent_oral'].label += f" ({self.object.passage.opponent.team.trigram})"
form.fields['reporter_writing'].label += f" ({self.object.passage.reporter.team.trigram})"
form.fields['reporter_oral'].label += f" ({self.object.passage.reporter.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})"
return form
def form_valid(self, form):

View File

@ -839,7 +839,7 @@ class SolutionView(LoginRequiredMixin, View):
if user.registration.participates:
passage_participant_qs = Passage.objects.filter(Q(defender=user.registration.team.participation)
| Q(opponent=user.registration.team.participation)
| Q(reporter=user.registration.team.participation),
| Q(reviewer=user.registration.team.participation),
defender=solution.participation,
solution_number=solution.problem)
else: