Compare commits

...

4 Commits

Author SHA1 Message Date
Emmy D'Anello 1b24e90635
Fix team reorder for 5-teams pools in draw recap
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-09 13:51:50 +02:00
Emmy D'Anello 338f0d456a
Fix undo draw step
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-09 13:47:59 +02:00
Emmy D'Anello 2c4de8cec3
Adapt the random draw for the next rounds of ETEAM
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-09 13:26:39 +02:00
Emmy D'Anello 6b7d52c79b
Fix the passage table with observers
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-07-09 12:44:04 +02:00
6 changed files with 181 additions and 93 deletions

View File

@ -1021,14 +1021,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
if not await Draw.objects.filter(tournament=self.tournament).aexists():
return await self.alert(_("The draw has not started yet."), 'danger')
if not self.tournament.final:
if not self.tournament.final and settings.TFJM_APP == "TFJM":
return await self.alert(_("This is only available for the final tournament."), 'danger')
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
r2 = await self.tournament.draw.round_set.filter(number=self.tournament.draw.current_round.number + 1).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.")
if settings.TFJM_APP == "TFJM":
msg = str(_("The draw of the round {round} is starting. "
"The passage order is determined from the ranking of the first round, "
"in order to mix the teams between the two days.").format(round=r2.number))
else:
msg = str(_("The draw of the round {round} is starting. "
"The passage order is another time randomly drawn.").format(round=r2.number))
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
@ -1036,29 +1040,30 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
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!")})
'body': str(_("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()
r2.current_pool = pool
await r2.asave()
if settings.TFJM_APP == "TFJM":
# 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()
r2.current_pool = pool
await r2.asave()
# Fetch notes from the first round
notes = dict()
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team').all():
notes[participation] = sum([await pool.aaverage(participation)
async for pool in self.tournament.pools.filter(participations=participation)
.prefetch_related('passages')])
# Sort notes in a decreasing order
ordered_participations = sorted(notes.keys(), key=lambda x: -notes[x])
# Define pools and passage orders from the ranking of the first round
async for pool in r2.pool_set.order_by('letter').all():
for i in range(pool.size):
participation = ordered_participations.pop(0)
td = await TeamDraw.objects.aget(round=r2, participation=participation)
td.pool = pool
td.passage_index = i
await td.asave()
# Fetch notes from the first round
notes = dict()
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team').all():
notes[participation] = sum([await pool.aaverage(participation)
async for pool in self.tournament.pools.filter(participations=participation)
.prefetch_related('passages')])
# Sort notes in a decreasing order
ordered_participations = sorted(notes.keys(), key=lambda x: -notes[x])
# Define pools and passage orders from the ranking of the first round
async for pool in r2.pool_set.order_by('letter').all():
for i in range(pool.size):
participation = ordered_participations.pop(0)
td = await TeamDraw.objects.aget(round=r2, participation=participation)
td.pool = pool
td.passage_index = i
await td.asave()
# Send pools to users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -1078,16 +1083,23 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
async for td in r2.current_pool.team_draws.prefetch_related('participation__team'):
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
if settings.TFJM_APP == "TFJM":
async for td in r2.current_pool.team_draws.prefetch_related('participation__team'):
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
# 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!")})
else:
async for td in r2.team_draws.prefetch_related('participation__team'):
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
# 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!")})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
@ -1102,7 +1114,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r2.number,
'pool': r2.current_pool.get_letter_display()})
'pool': r2.current_pool.get_letter_display() if r2.current_pool else None})
@ensure_orga
async def cancel_last_step(self, **kwargs):
@ -1376,7 +1388,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'round': r.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
elif r.number >= 2:
elif r.number >= 2 and settings.TFJM_APP == "TFJM":
if not self.tournament.final:
# Go to the previous round
previous_round = await self.tournament.draw.round_set \
@ -1390,21 +1402,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'team': td.participation.team.trigram,
'result': td.choice_dice})
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{
'tid': self.tournament_id,
'type': 'draw.send_poules',
'round': previous_round.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in previous_round.pool_set.order_by('letter').all()
]
})
previous_pool = previous_round.current_pool
td = previous_pool.current_team
@ -1468,17 +1465,31 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'visible': True})
else:
# Go to the dice order
async for r0 in self.tournament.draw.round_set.all():
async for td in r0.teamdraw_set.all():
td.pool = None
td.passage_index = None
td.choose_index = None
td.choice_dice = None
await td.asave()
async for td in r.teamdraw_set.all():
td.pool = None
td.passage_index = None
td.choose_index = None
td.choice_dice = None
await td.asave()
r.current_pool = None
await r.asave()
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{
'tid': self.tournament_id,
'type': 'draw.send_poules',
'round': r.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r.pool_set.order_by('letter').all()
]
})
round_tds = {td.id: td async for td in r.team_draws.prefetch_related('participation__team')}
# Reset the last dice
@ -1548,8 +1559,45 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'team': last_td.participation.team.trigram,
'result': None})
break
else:
elif r.number == 1:
# Cancel the draw if it is the first round
await self.abort()
else:
# Go back to the first round after resetting all
previous_round = await self.tournament.draw.round_set \
.prefetch_related('current_pool__current_team__participation__team').aget(number=r.number - 1)
self.tournament.draw.current_round = previous_round
await self.tournament.draw.asave()
async for td in previous_round.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.choice_dice})
previous_pool = previous_round.current_pool
td = previous_pool.current_team
td.purposed = td.accepted
td.accepted = None
await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_problem',
'round': previous_round.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
async def draw_alert(self, content):
"""

View File

@ -0,0 +1,27 @@
# Generated by Django 5.0.6 on 2024-07-09 11:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("draw", "0005_alter_round_number_alter_teamdraw_accepted_and_more"),
]
operations = [
migrations.AlterField(
model_name="round",
name="current_pool",
field=models.ForeignKey(
default=None,
help_text="The current pool where teams select their problems.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to="draw.pool",
verbose_name="current pool",
),
),
]

View File

@ -82,7 +82,7 @@ class Draw(models.Model):
elif self.current_round.current_pool.current_team is None:
return 'DICE_ORDER_POULE'
elif self.current_round.current_pool.current_team.accepted is not None:
if self.current_round.number == 1:
if self.current_round.number < settings.NB_ROUNDS:
# The last step can be the last problem acceptation after the first round
# only for the final between the two rounds
return 'WAITING_FINAL'
@ -205,7 +205,7 @@ class Round(models.Model):
current_pool = models.ForeignKey(
'Pool',
on_delete=models.CASCADE,
on_delete=models.SET_NULL,
null=True,
default=None,
related_name='+',

View File

@ -700,6 +700,9 @@ document.addEventListener('DOMContentLoaded', () => {
let problem = problems[i]
setProblemAccepted(tid, round, team, problem)
let recapTeam = document.getElementById(`recap-${tid}-round-${round}-team-${team}`)
recapTeam.style.order = i.toString()
}
}

View File

@ -176,7 +176,7 @@
📁 {% trans "Export" %}
</button>
</div>
{% if tournament.final %}
{% if tournament.final or TFJM.APP == "ETEAM" %}
{# Volunteers can continue the second round for the final tournament #}
<div id="continue-{{ tournament.id }}"
class="card-footer text-center{% if tournament.draw.get_state != 'WAITING_FINAL' %} d-none{% endif %}">
@ -344,33 +344,33 @@
{% elif pool.size == 5 %}
{% if forloop.counter == 1 %}
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center"></td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
{% elif forloop.counter == 2 %}
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center"></td>
{% elif forloop.counter == 2 %}
<td class="text-center"></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
{% elif forloop.counter == 3 %}
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center"></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
{% elif forloop.counter == 4 %}
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center"></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center"></td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
{% elif forloop.counter == 5 %}
<td class="text-center"></td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center"></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
{% endif %}
{% endif %}

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-06 22:34+0200\n"
"POT-Creation-Date: 2024-07-09 13:22+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"
@ -269,7 +269,7 @@ msgstr "équipes"
msgid "round"
msgstr "tour"
#: draw/apps.py:10 draw/consumers.py:1038 tfjm/templates/navbar.html:68
#: draw/apps.py:10 draw/consumers.py:1042 tfjm/templates/navbar.html:68
msgid "Draw"
msgstr "Tirage au sort"
@ -311,7 +311,7 @@ msgstr "Le tirage au sort du tournoi {tournament} va commencer."
#: draw/consumers.py:256 draw/consumers.py:282 draw/consumers.py:692
#: draw/consumers.py:910 draw/consumers.py:1000 draw/consumers.py:1022
#: draw/consumers.py:1113 draw/templates/draw/tournament_content.html:5
#: draw/consumers.py:1125 draw/templates/draw/tournament_content.html:5
msgid "The draw has not started yet."
msgstr "Le tirage au sort n'a pas encore commencé."
@ -363,12 +363,12 @@ msgstr ""
"sont déterminés à partir des ordres de passage du premier tour."
#: draw/consumers.py:615 draw/consumers.py:755 draw/consumers.py:832
#: draw/consumers.py:866 draw/consumers.py:991 draw/consumers.py:1089
#: draw/consumers.py:866 draw/consumers.py:991 draw/consumers.py:1095
msgid "Your turn!"
msgstr "À votre tour !"
#: draw/consumers.py:616 draw/consumers.py:756 draw/consumers.py:992
#: draw/consumers.py:1090
#: draw/consumers.py:1096
msgid "It's your turn to draw a problem!"
msgstr "C'est à vous de tirer un problème !"
@ -439,17 +439,27 @@ msgstr ""
msgid "This is only available for the final tournament."
msgstr "Cela n'est possible que pour la finale."
#: draw/consumers.py:1029
#: draw/consumers.py:1030
#, python-brace-format
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."
"The draw of the round {round} 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é à "
"Le tirage au sort du tour {round} 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:1039
#: draw/consumers.py:1034
#, python-brace-format
msgid ""
"The draw of the round {round} is starting. The passage order is another time "
"randomly drawn."
msgstr ""
"Le tirage au sort du tour {round} commence. L'ordre de passage est à nouveau tiré "
"au hasard."
#: draw/consumers.py:1043
msgid "The draw of the second round is starting!"
msgstr "Le tirage au sort du deuxième tour commence !"
@ -866,11 +876,11 @@ msgstr "Opp"
#: draw/templates/draw/tournament_content.html:331
#: draw/templates/draw/tournament_content.html:337
#: draw/templates/draw/tournament_content.html:339
#: draw/templates/draw/tournament_content.html:350
#: draw/templates/draw/tournament_content.html:352
#: draw/templates/draw/tournament_content.html:359
#: draw/templates/draw/tournament_content.html:366
#: draw/templates/draw/tournament_content.html:373
#: draw/templates/draw/tournament_content.html:347
#: draw/templates/draw/tournament_content.html:354
#: draw/templates/draw/tournament_content.html:361
#: draw/templates/draw/tournament_content.html:368
#: draw/templates/draw/tournament_content.html:370
msgctxt "Role abbreviation"
msgid "Obs"
msgstr "Obs"
@ -2847,17 +2857,17 @@ msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e
msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1902
#: participation/views.py:1904
#, python-brace-format
msgid "Notation sheets of pool {pool} of {tournament}.zip"
msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:1907
#: participation/views.py:1909
#, python-brace-format
msgid "Notation sheets of {tournament}.zip"
msgstr "Feuilles de notation de {tournament}.zip"
#: participation/views.py:2074
#: participation/views.py:2076
msgid "You can't upload a written review after the deadline."
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."