1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-21 12:21:19 +00:00

Compare commits

..

No commits in common. "2a298a3ee4bbd2dcbfdee2450a5d34f86eec3eac" and "d84db949c6459521aa4d5be2d8157085c04f0119" have entirely different histories.

16 changed files with 275 additions and 594 deletions

View File

@ -122,8 +122,6 @@ 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
@ -235,8 +233,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': _("The draw of tournament {tournament} started!")
.format(tournament=self.tournament.name)})
'body': "Le tirage au sort du tournoi de "
f"{self.tournament.name} a commencé !"})
async def draw_start(self, content) -> None:
"""
@ -405,8 +403,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': _("Your dice score is identical to the one of one or multiple teams. "
"Please relaunch it.")}
'body': 'Votre score de dé est identique à celui de une ou plusieurs équipes. '
'Veuillez le relancer.'}
)
# Alert the tournament
await self.channel_layer.group_send(
@ -450,7 +448,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 and settings.TFJM_APP == "TFJM":
if not self.tournament.final:
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()
@ -504,11 +502,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.tournament.draw.current_round.asave()
# Display dice result in the header of the information alert
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.")
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."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -611,8 +610,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': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
async def select_problem(self, **kwargs):
"""
@ -632,7 +631,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
@ -703,20 +702,19 @@ 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 = _("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])
msg = f"L'équipe <strong>{trigram}</strong> a accepté le problème <strong>{td.accepted} : " \
f"{settings.PROBLEMS[td.accepted - 1]}</strong>. "
if pool.size == 5 and await pool.teamdraw_set.filter(accepted=td.accepted).acount() < 2:
msg += _("One team more can accept this problem.")
msg += "Une équipe peut encore l'accepter."
else:
msg += _("No team can accept this problem anymore.")
msg += "Plus personne ne peut l'accepter."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -751,8 +749,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': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
else:
# Pool is ended
await self.end_pool(pool)
@ -810,8 +808,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'problems': [td.accepted async for td in pool.team_draws],
})
msg += "<br><br>" + _("The draw of the pool {pool} is ended. The summary is below.") \
.format(pool=f"{pool.get_letter_display()}{r.number}")
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."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -828,8 +826,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': _("Your turn!"),
'body': _("It's your turn to launch the dice!")})
'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
@ -845,11 +843,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
msg = self.tournament.draw.last_message
if r.number < settings.NB_ROUNDS and not self.tournament.final and settings.TFJM_APP == "TFJM":
if r.number < settings.NB_ROUNDS and not self.tournament.final:
# Next round
next_round = await self.tournament.draw.round_set.filter(number=r.number + 1).aget()
self.tournament.draw.current_round = next_round
msg += "<br><br>" + _("The draw of the round {round} is ended.").format(round=r.number)
msg += f"<br><br>Le tirage au sort du tour {r.number} est terminé."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -862,8 +860,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': _("Your turn!"),
'body': _("It's your turn to launch the dice!")})
'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
# Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -890,9 +888,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 or settings.TFJM_APP == "ETEAM"):
elif r.number == 1 and self.tournament.final:
# For the final tournament, we wait for a manual update between the two rounds.
msg += "<br><br>" + _("The draw of the first round is ended.")
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -921,7 +919,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
@ -935,16 +933,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update messages
trigram = td.participation.team.trigram
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]) + " "
msg = f"L'équipe <strong>{trigram}</strong> a refusé le problème <strong>{problem} : " \
f"{settings.PROBLEMS[problem - 1]}</strong>. "
if remaining >= 0:
msg += _("It remains {remaining} refusals without penalty.").format(remaining=remaining)
msg += f"Il lui reste {remaining} refus sans pénalité."
else:
if already_refused:
msg += _("This problem was already refused by this team.")
msg += "Cela n'ajoute pas de pénalité."
else:
msg += _("It adds a 25% penalty on the coefficient of the oral defense.")
msg += "Cela ajoute une pénalité de 25&nbsp;% sur le coefficient de l'oral de la défense."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -987,8 +984,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': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
@ensure_orga
async def export(self, **kwargs):
@ -1025,17 +1022,17 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
self.tournament.draw.current_round = r2
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.")
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."
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': _("Draw") + " " + settings.APP_NAME,
'body': _("The draw of the second round is starting!")})
'title': 'Tirage au sort du TFJM²',
'body': "Le tirage au sort pour le second tour de la finale a commencé !"})
# 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()
@ -1085,8 +1082,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': _("Your turn!"),
'body': _("It's your turn to draw a problem!")})
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',

View File

@ -110,61 +110,58 @@ 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 += _("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.")
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."""
case 'DICE_ORDER_POULE':
# Waiting for dices to determine the choice order
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()))
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."""
case 'WAITING_DRAW_PROBLEM':
# Waiting for a problem draw
td = self.current_round.current_pool.current_team
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)
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."""
case 'WAITING_CHOOSE_PROBLEM':
# Waiting for the team that can accept or reject the problem
td = self.current_round.current_pool.current_team
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]) + " "
s += f"""L'équipe <strong>{td.participation.team.trigram}</strong> a tiré le problème
<strong>{td.purposed} : {settings.PROBLEMS[td.purposed - 1]}</strong>. """
if td.purposed in td.rejected:
# The problem was previously rejected
s += _("It already refused this problem before, so it can refuse it without penalty and "
"draw a new problem immediately, or change its mind.")
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."""
else:
# The problem can be rejected
s += _("It can decide to accept or refuse this problem.") + " "
s += "Elle peut décider d'accepter ou de refuser ce problème. "
if len(td.rejected) >= len(settings.PROBLEMS) - settings.RECOMMENDED_SOLUTIONS_COUNT:
s += _("Refusing this problem will add a new 25% penalty "
"on the coefficient of the oral defense.")
s += "Refuser ce problème ajoutera une nouvelle pénalité de 25 % sur le coefficient de l'oral de la défense."
else:
s += _("There are still {remaining} refusals without penalty.").format(
remaining=len(settings.PROBLEMS) - settings.RECOMMENDED_SOLUTIONS_COUNT - len(td.rejected))
s += f"Il reste {len(settings.PROBLEMS) - settings.RECOMMENDED_SOLUTIONS_COUNT - len(td.rejected)} refus sans pénalité."
case 'WAITING_FINAL':
# We are between the two rounds of the final tournament
s += _("The draw for the second round will take place at the end of the first round. Good luck!")
s += "Le tirage au sort pour le tour 2 aura lieu à la fin du premier tour. Bon courage !"
case 'DRAW_ENDED':
# The draw is ended
s += _("The draw is ended. The solutions of the other teams can be found in the tab "
"\"My participation\".")
s += "Le tirage au sort est terminé. Les solutions des autres équipes peuvent être trouvées dans l'onglet « Ma participation »."
s += "<br><br>" if s else ""
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)
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>."""
return s
async def ainformation(self) -> str:
@ -415,7 +412,7 @@ class Pool(models.Model):
solution_number=tds[line[0]].accepted,
defender=tds[line[0]].participation,
opponent=tds[line[1]].participation,
reviewer=tds[line[2]].participation,
reporter=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 reviewerTd = document.createElement('td')
reviewerTd.classList.add('text-center')
reviewerTd.innerText = 'Rap'
let reporterTd = document.createElement('td')
reporterTd.classList.add('text-center')
reporterTd.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, reviewerTd, opponentTd)
teamTr.append(defenderTd, reporterTd, opponentTd)
break
case 1:
teamTr.append(opponentTd, defenderTd, reviewerTd)
teamTr.append(opponentTd, defenderTd, reporterTd)
break
case 2:
teamTr.append(reviewerTd, opponentTd, defenderTd)
teamTr.append(reporterTd, opponentTd, defenderTd)
break
}
} else if (poule.teams.length === 4) {
let emptyTd = document.createElement('td')
switch (i) {
case 0:
teamTr.append(defenderTd, emptyTd, reviewerTd, opponentTd)
teamTr.append(defenderTd, emptyTd, reporterTd, opponentTd)
break
case 1:
teamTr.append(opponentTd, defenderTd, emptyTd, reviewerTd)
teamTr.append(opponentTd, defenderTd, emptyTd, reporterTd)
break
case 2:
teamTr.append(reviewerTd, opponentTd, defenderTd, emptyTd)
teamTr.append(reporterTd, opponentTd, defenderTd, emptyTd)
break
case 3:
teamTr.append(emptyTd, reviewerTd, opponentTd, defenderTd)
teamTr.append(emptyTd, reporterTd, 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, reviewerTd, emptyTd2)
teamTr.append(defenderTd, emptyTd, opponentTd, reporterTd, emptyTd2)
break
case 1:
teamTr.append(emptyTd, defenderTd, reviewerTd, emptyTd2, opponentTd)
teamTr.append(emptyTd, defenderTd, reporterTd, emptyTd2, opponentTd)
break
case 2:
teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reviewerTd)
teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reporterTd)
break
case 3:
teamTr.append(reviewerTd, opponentTd, emptyTd, defenderTd, emptyTd2)
teamTr.append(reporterTd, opponentTd, emptyTd, defenderTd, emptyTd2)
break
case 4:
teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, defenderTd)
teamTr.append(emptyTd, reporterTd, 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-07-05 10:48+0200\n"
"POT-Creation-Date: 2024-06-13 10:56+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:432 draw/models.py:459
#: chat/models.py:73 draw/models.py:429 draw/models.py:456
#: 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:237 draw/models.py:451
#: draw/admin.py:92 draw/models.py:234 draw/models.py:448
#: participation/models.py:1016
msgid "round"
msgstr "tour"
#: draw/apps.py:10 draw/consumers.py:1037 tfjm/templates/navbar.html:68
#: draw/apps.py:10 tfjm/templates/navbar.html:68
msgid "Draw"
msgstr "Tirage au sort"
@ -277,182 +277,67 @@ msgstr "Tirage au sort"
msgid "You are not an organizer."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
#: draw/consumers.py:167
#: draw/consumers.py:165
msgid "The draw is already started."
msgstr "Le tirage a déjà commencé."
#: draw/consumers.py:173
#: draw/consumers.py:171
msgid "Invalid format"
msgstr "Format invalide"
#: draw/consumers.py:178
#: draw/consumers.py:176
#, 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:183
#: draw/consumers.py:181
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:223
#: draw/consumers.py:221
msgid "Draw started!"
msgstr "Le tirage a commencé !"
#: 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
#: draw/consumers.py:243
#, 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: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
#: 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
msgid "The draw has not started yet."
msgstr "Le tirage au sort n'a pas encore commencé."
#: draw/consumers.py:269
#: draw/consumers.py:267
#, 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:309 draw/consumers.py:330 draw/consumers.py:625
#: draw/consumers.py:696 draw/consumers.py:914
#: draw/consumers.py:307 draw/consumers.py:328 draw/consumers.py:624
#: draw/consumers.py:695 draw/consumers.py:912
msgid "This is not the time for this."
msgstr "Ce n'est pas le moment pour cela."
#: draw/consumers.py:322 draw/consumers.py:325
#: draw/consumers.py:320 draw/consumers.py:323
msgid "You've already launched the dice."
msgstr "Vous avez déjà lancé le dé."
#: draw/consumers.py:328
#: draw/consumers.py:326
msgid "It is not your turn."
msgstr "Ce n'est pas votre tour."
#: 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
#: draw/consumers.py:413
#, 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: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
#: draw/consumers.py:1021
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é."
@ -477,261 +362,154 @@ msgstr "Le dernier message qui est affiché sur l'interface de tirage."
msgid "State"
msgstr "État"
#: 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
#: draw/models.py:174
#, 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:177 draw/models.py:189
msgid "draw"
msgstr "tirage au sort"
#: draw/models.py:181
#: draw/models.py:178
msgid "draws"
msgstr "tirages au sort"
#: draw/models.py:197
#: draw/models.py:194
msgid "Round 1"
msgstr "Tour 1"
#: draw/models.py:198
#: draw/models.py:195
msgid "Round 2"
msgstr "Tour 2"
#: draw/models.py:199
#: draw/models.py:196
msgid "Round 3"
msgstr "Tour 3"
#: draw/models.py:200
#: draw/models.py:197
msgid "number"
msgstr "numéro"
#: draw/models.py:201
#: draw/models.py:198
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:208
msgid "current pool"
msgstr "poule actuelle"
#: draw/models.py:212
#: draw/models.py:209
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:235
msgid "rounds"
msgstr "tours"
#: draw/models.py:260 participation/models.py:1024
#: draw/models.py:257 participation/models.py:1024
msgid "letter"
msgstr "lettre"
#: draw/models.py:261
#: draw/models.py:258
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:262
#: participation/templates/participation/tournament_detail.html:15
msgid "size"
msgstr "taille"
#: draw/models.py:267
#: draw/models.py:264
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:273
msgid "current team"
msgstr "équipe actuelle"
#: draw/models.py:277
#: draw/models.py:274
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:283
msgid "associated pool"
msgstr "poule associée"
#: draw/models.py:287
#: draw/models.py:284
msgid "The full pool instance."
msgstr "L'instance complète de la poule."
#: draw/models.py:429
#: draw/models.py:426
#, python-brace-format
msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}"
#: draw/models.py:433 participation/models.py:1516
#: draw/models.py:430 participation/models.py:1516
msgid "pools"
msgstr "poules"
#: draw/models.py:445 participation/models.py:1002 participation/models.py:1665
#: draw/models.py:442 participation/models.py:1002 participation/models.py:1665
#: participation/models.py:1695 participation/models.py:1737
msgid "participation"
msgstr "participation"
#: draw/models.py:466
#: draw/models.py:463
msgid "passage index"
msgstr "numéro de passage"
#: draw/models.py:467
#: draw/models.py:464
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:472
msgid "choose index"
msgstr "numéro de choix"
#: draw/models.py:476
#: draw/models.py:473
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
#: draw/models.py:479 draw/models.py:502 participation/models.py:1538
#: participation/models.py:1702
#, python-brace-format
msgid "Problem #{problem}"
msgstr "Problème n°{problem}"
#: draw/models.py:486
#: draw/models.py:483
msgid "accepted problem"
msgstr "problème accepté"
#: draw/models.py:493
#: draw/models.py:490
msgid "passage dice"
msgstr "dé d'ordre de passage"
#: draw/models.py:500
#: draw/models.py:497
msgid "choice dice"
msgstr "dé d'ordre de choix"
#: draw/models.py:509
#: draw/models.py:506
msgid "purposed problem"
msgstr "problème proposé"
#: draw/models.py:514
#: draw/models.py:511
msgid "rejected problems"
msgstr "problèmes rejetés"
#: draw/models.py:543
#: draw/models.py:540
#, 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:546
msgid "team draw"
msgstr "tirage d'équipe"
#: draw/models.py:550
#: draw/models.py:547
msgid "team draws"
msgstr "tirages d'équipe"
@ -924,7 +702,7 @@ msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:1559
#: participation/models.py:1750
msgid "reviewer"
msgid "reporter"
msgstr "rapporteur⋅rice"
#: participation/admin.py:187 participation/models.py:1700
@ -1016,7 +794,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 reviewer must be different."
msgid "The defender, the opponent and the reporter must be different."
msgstr ""
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
@ -1397,11 +1175,11 @@ msgstr ""
#: participation/models.py:966
#, python-brace-format
msgid ""
"<p>You will report the solution of the team {reviewer} on the <a "
"<p>You will report the solution of the team {reporter} 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 {reviewer} sur le <a "
"<p>Vous rapporterez la solution de l'équipe {reporter} 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>"
@ -1631,11 +1409,11 @@ msgid "opponent oral note"
msgstr "note d'oral opposant⋅e"
#: participation/models.py:1815
msgid "reviewer writing note"
msgid "reporter writing note"
msgstr "note d'écrit rapporteur⋅rice"
#: participation/models.py:1821
msgid "reviewer oral note"
msgid "reporter oral note"
msgstr "note d'oral du rapporteur⋅rice"
#: participation/models.py:1881
@ -1814,7 +1592,7 @@ msgid "Opponent:"
msgstr "Opposant⋅e :"
#: participation/templates/participation/passage_detail.html:34
msgid "reviewer:"
msgid "Reporter:"
msgstr "Rapporteur⋅rice :"
#: participation/templates/participation/passage_detail.html:37
@ -1866,11 +1644,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 reviewer writing"
msgid "Average points for the reporter writing"
msgstr "Moyenne de l'écrit de l'équipe rapportrice"
#: participation/templates/participation/passage_detail.html:107
msgid "Average points for the reviewer oral"
msgid "Average points for the reporter oral"
msgstr "Moyenne de l'oral de l'équipe rapportrice"
#: participation/templates/participation/passage_detail.html:117
@ -1882,7 +1660,7 @@ msgid "Opponent points"
msgstr "Points de l'équipe opposante"
#: participation/templates/participation/passage_detail.html:129
msgid "reviewer points"
msgid "Reporter 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', 'reviewer',)
autocomplete_fields = ('defender', 'opponent', 'reporter',)
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', 'reviewer_trigram',
list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reporter_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', 'reporter',)
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=_("reviewer"), ordering='reviewer__team__trigram')
def reviewer_trigram(self, record: Passage):
return record.reviewer.team.trigram
@admin.display(description=_("reporter"), ordering='reporter__team__trigram')
def reporter_trigram(self, record: Passage):
return record.reporter.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', 'reviewer_writing', 'reviewer_oral',)
'opponent_writing', 'opponent_oral', 'reporter_writing', 'reporter_oral',)
list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury',
'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral')
'reporter_writing', 'reporter_oral')
search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__defender__team__trigram',)
autocomplete_fields = ('jury', 'passage',)

View File

@ -13,7 +13,7 @@ class NoteViewSet(ModelViewSet):
serializer_class = NoteSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['jury', 'passage', 'defender_writing', 'defender_oral', 'opponent_writing',
'opponent_oral', 'reviewer_writing', 'reviewer_oral', ]
'opponent_oral', 'reporter_writing', 'reporter_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', 'reporter', '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 "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 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 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', 'reviewer', 'defender_penalties',)
fields = ('position', 'solution_number', 'defender', 'opponent', 'reporter', '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', 'reporter_writing', 'reporter_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)
reviewer_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reviewer=team2)
reporter_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reporter=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)
reviewer_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reviewer=team2)
reporter_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reporter=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,
reviewer_passage_1.average_reviewer_writing, reviewer_passage_1.average_reviewer_oral])
reporter_passage_1.average_reporter_writing, reporter_passage_1.average_reporter_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,
reviewer_passage_2.average_reviewer_writing, reviewer_passage_2.average_reviewer_oral])
reporter_passage_2.average_reporter_writing, reporter_passage_2.average_reporter_oral])
line.extend([score2, f"{score1:.1f} ({team1.team.trigram})",
f"{score3:.1f} ({team3.team.trigram})"])

View File

@ -1,91 +0,0 @@
# 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)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self)
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reporter=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)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
reporter_text = _("<p>You will report the solution of the team {reporter} 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 = 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 = 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=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url)
problem=reporter_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 + reviewer_content + syntheses_templates_content
content = defender_content + opponent_content + reporter_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)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self)
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reporter=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)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
reporter_text = _("<p>You will report the solution of the team {reporter} 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 = 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 = 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=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url)
problem=reporter_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 + reviewer_content + syntheses_templates_content
content = defender_content + opponent_content + reporter_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)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self)
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reporter=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)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
reporter_text = _("<p>You will report the solution of the team {reporter} 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 = 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 = 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=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url)
problem=reporter_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 + reviewer_content + syntheses_templates_content
content = defender_content + opponent_content + reporter_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.reviewer.team.trigram})", ""]
f"Rapporteur⋅rice ({passage.reporter.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.reviewer_writing, note.reviewer_oral])
note.reporter_writing, note.reporter_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
reviewer_passage = Passage.objects.get(reviewer=participation,
reporter_passage = Passage.objects.get(reporter=participation,
pool__tournament=self.tournament, pool__round=self.round)
reviewer_row = 5 + reviewer_passage.pool.juries.count()
reviewer_col = reviewer_passage.position - 1
reporter_row = 5 + reporter_passage.pool.juries.count()
reporter_col = reporter_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 {reviewer_passage.pool.short_name}'"
f"!{getcol(min_column + reviewer_col * passage_width + 4)}{reviewer_row + 3}") # reviewer
formula += (f" + 'Poule {reporter_passage.pool.short_name}'"
f"!{getcol(min_column + reporter_col * passage_width + 4)}{reporter_row + 3}") # Reporter
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="+",
)
reviewer = models.ForeignKey(
reporter = models.ForeignKey(
Participation,
on_delete=models.PROTECT,
verbose_name=_("reviewer"),
verbose_name=_("reporter"),
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_reviewer_writing(self) -> float:
return self.avg(note.reviewer_writing for note in self.notes.all())
def average_reporter_writing(self) -> float:
return self.avg(note.reporter_writing for note in self.notes.all())
@property
def average_reviewer_oral(self) -> float:
return self.avg(note.reviewer_oral for note in self.notes.all())
def average_reporter_oral(self) -> float:
return self.avg(note.reporter_oral for note in self.notes.all())
@property
def average_reviewer(self) -> float:
return 0.9 * self.average_reviewer_writing + self.average_reviewer_oral
def average_reporter(self) -> float:
return 0.9 * self.average_reporter_writing + self.average_reporter_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_reviewer_writing
yield self.average_reviewer_oral
yield self.average_reporter_writing
yield self.average_reporter_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
if participation == self.opponent else self.average_reporter if participation == self.reporter 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.reviewer not in self.pool.participations.all():
if self.reporter not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.reviewer.team.trigram))
.format(trigram=self.reporter.team.trigram))
return super().clean()
def __str__(self):
@ -1747,7 +1747,7 @@ class Synthesis(models.Model):
type = models.PositiveSmallIntegerField(
choices=[
(1, _("opponent"), ),
(2, _("reviewer"), ),
(2, _("reporter"), ),
]
)
@ -1811,14 +1811,14 @@ class Note(models.Model):
default=0,
)
reviewer_writing = models.PositiveSmallIntegerField(
verbose_name=_("reviewer writing note"),
reporter_writing = models.PositiveSmallIntegerField(
verbose_name=_("reporter writing note"),
choices=[(i, i) for i in range(0, 11)],
default=0,
)
reviewer_oral = models.PositiveSmallIntegerField(
verbose_name=_("reviewer oral note"),
reporter_oral = models.PositiveSmallIntegerField(
verbose_name=_("reporter 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.reviewer_writing
yield self.reviewer_oral
yield self.reporter_writing
yield self.reporter_oral
def set_all(self, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int,
reviewer_writing: int, reviewer_oral: int):
reporter_writing: int, reporter_oral: int):
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.reporter_writing = reporter_writing
self.reporter_oral = reporter_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_reviewer(self, value):
def render_reporter(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', 'reviewer', 'solution_number', )
fields = ('defender', 'opponent', 'reporter', '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',
'reviewer_writing', 'reviewer_oral', 'update',)
'reporter_writing', 'reporter_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 "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 "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 "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 reviewer writing" %}
({{ passage.reviewer.team.trigram }}) :
{% trans "Average points for the reporter writing" %}
({{ passage.reporter.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_reviewer_writing|floatformat }}/10</dd>
<dd class="col-sm-4">{{ passage.average_reporter_writing|floatformat }}/10</dd>
<dt class="col-sm-8">
{% trans "Average points for the reviewer oral" %}
({{ passage.reviewer.team.trigram }}) :
{% trans "Average points for the reporter oral" %}
({{ passage.reporter.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_reviewer_oral|floatformat }}/10</dd>
<dd class="col-sm-4">{{ passage.average_reporter_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 "reviewer points" %}
({{ passage.reviewer.team.trigram }}) :
{% trans "Reporter points" %}
({{ passage.reporter.team.trigram }}) :
</dt>
<dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/19</dd>
<dd class="col-sm-4">{{ passage.average_reporter|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.reviewer.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.reporter.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.reviewer.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.reporter.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())
reviewer_tc = TableCell(valuetype="string",
reporter_tc = TableCell(valuetype="string",
stylename=title_style_right)
reviewer_tc.addElement(P(text="Rapporteur⋅rice"))
reviewer_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(reviewer_tc)
reporter_tc.addElement(P(text="Rapporteur⋅rice"))
reporter_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(reporter_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)
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_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
reporter_w_tc.addElement(P(text="Écrit (/10)"))
header_notes.addElement(reporter_w_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)
reporter_o_tc = TableCell(valuetype="string", stylename=title_style_botright)
reporter_o_tc.addElement(P(text="Oral (/10)"))
header_notes.addElement(reporter_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)
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_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
reporter_w_tc.addElement(P(text="1"))
coeff_row.addElement(reporter_w_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)
reporter_o_tc = TableCell(valuetype="float", value=1, stylename=style_right)
reporter_o_tc.addElement(P(text="1"))
coeff_row.addElement(reporter_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)
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}]"
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}]"
f" + [.{rep_o_col}{max_row + 1}] * [.{rep_o_col}{max_row + 2}]")
subtotal_row.addElement(reviewer_tc)
subtotal_row.addElement(reporter_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
reviewer_pos = self.object.passages.get(reviewer=passage.defender).position - 1
reporter_pos = self.object.passages.get(reporter=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 + reviewer_pos * passage_width + 4) + str(max_row + 3) # reviewer
formula += " + " + getcol(min_column + reporter_pos * passage_width + 4) + str(max_row + 3) # Reporter
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.reviewer]:
and reg.team.participation in [passage.defender, passage.opponent, passage.reporter]:
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['reviewer_writing'].column.verbose_name += f" ({passage.reviewer.team.trigram})"
context['notes'].columns['reviewer_oral'].column.verbose_name += f" ({passage.reviewer.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})"
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.reviewer]:
if self.participation not in [self.passage.opponent, self.passage.reporter]:
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['reviewer_writing'].label += f" ({self.object.passage.reviewer.team.trigram})"
form.fields['reviewer_oral'].label += f" ({self.object.passage.reviewer.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})"
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(reviewer=user.registration.team.participation),
| Q(reporter=user.registration.team.participation),
defender=solution.participation,
solution_number=solution.problem)
else: