Add continue button for the final tournament

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2023-03-26 11:08:03 +02:00
parent 1bd9cea458
commit 7d8975339e
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
6 changed files with 156 additions and 52 deletions

View File

@ -34,8 +34,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
.prefetch_related('draw__current_round__current_pool__current_team').aget() .prefetch_related('draw__current_round__current_pool__current_team').aget()
self.participations = [] self.participations = []
async for participation in Participation.objects.filter(tournament=self.tournament, valid=True)\ async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'):
.prefetch_related('team'):
self.participations.append(participation) self.participations.append(participation)
user = self.scope['user'] user = self.scope['user']
@ -88,6 +87,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.reject_problem(**content) await self.reject_problem(**content)
case 'export': case 'export':
await self.export(**content) await self.export(**content)
case 'continue_final':
await self.continue_final(**content)
@ensure_orga @ensure_orga
async def start_draw(self, fmt, **kwargs): async def start_draw(self, fmt, **kwargs):
@ -487,8 +488,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.dice_visibility', 'visible': True}) {'type': 'draw.dice_visibility', 'visible': True})
else: else:
# Round is ended # Round is ended
# TODO: For the final tournament, add some adjustments if r.number == 1 and not self.tournament.final:
if r.number == 1:
# Next round # Next round
r2 = await self.tournament.draw.round_set.filter(number=2).aget() r2 = await self.tournament.draw.round_set.filter(number=2).aget()
self.tournament.draw.current_round = r2 self.tournament.draw.current_round = r2
@ -505,6 +505,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.dice_visibility', 'visible': True}) {'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'type': 'draw.dice_visibility', 'visible': True})
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>Le tirage au sort du tour 1 est terminé."
self.tournament.draw.last_message = msg
await sync_to_async(self.tournament.draw.save)()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.export_visibility', 'visible': True}) {'type': 'draw.export_visibility', 'visible': True})
@ -587,6 +592,56 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.export_visibility', 'visible': False}) {'type': 'draw.export_visibility', 'visible': False})
@ensure_orga
async def continue_final(self, **kwargs):
if not self.tournament.final:
return await self.alert(_("This is only available for the final tournament."), 'danger')
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."
self.tournament.draw.last_message = msg
await sync_to_async(self.tournament.draw.save)()
pool = await Pool.objects.filter(round=self.tournament.draw.current_round, letter=1).aget()
r2.current_pool = pool
await sync_to_async(r2.save)()
notes = dict()
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team').all():
notes[participation] = sum([await sync_to_async(pool.average)(participation)
async for pool in self.tournament.pools.filter(participations=participation)
if pool.results_available])
print(participation.team.trigram, notes[participation])
ordered_participations = sorted(notes.keys(), key=lambda x: -notes[x])
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 sync_to_async(td.save)()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r2})
for participation in self.participations:
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
await self.channel_layer.group_send(f"team-{participation.team.trigram}",
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.continue_visibility', 'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
async def draw_alert(self, content): async def draw_alert(self, content):
return await self.alert(**content) return await self.alert(**content)
@ -611,6 +666,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async def draw_export_visibility(self, content): async def draw_export_visibility(self, content):
await self.send_json({'type': 'export_visibility', 'visible': content['visible']}) await self.send_json({'type': 'export_visibility', 'visible': content['visible']})
async def draw_continue_visibility(self, content):
await self.send_json({'type': 'continue_visibility', 'visible': content['visible']})
async def draw_send_poules(self, content): async def draw_send_poules(self, content):
await self.send_json({'type': 'set_poules', 'round': content['round'].number, await self.send_json({'type': 'set_poules', 'round': content['round'].number,
'poules': [{'letter': pool.get_letter_display(), 'teams': await pool.atrigrams()} 'poules': [{'letter': pool.get_letter_display(), 'teams': await pool.atrigrams()}

View File

@ -42,7 +42,10 @@ class Draw(models.Model):
elif self.current_round.current_pool.current_team is None: elif self.current_round.current_pool.current_team is None:
return 'DICE_ORDER_POULE' return 'DICE_ORDER_POULE'
elif self.current_round.current_pool.current_team.accepted is not None: elif self.current_round.current_pool.current_team.accepted is not None:
return 'DRAW_ENDED' if self.current_round.number == 1:
return 'WAITING_FINAL'
else:
return 'DRAW_ENDED'
elif self.current_round.current_pool.current_team.purposed is None: elif self.current_round.current_pool.current_team.purposed is None:
return 'WAITING_DRAW_PROBLEM' return 'WAITING_DRAW_PROBLEM'
else: else:
@ -93,6 +96,8 @@ class Draw(models.Model):
s += "Refuser ce problème ajoutera une nouvelle pénalité de 0.5 sur le coefficient de l'oral de læ défenseur⋅se." s += "Refuser ce problème ajoutera une nouvelle pénalité de 0.5 sur le coefficient de l'oral de læ défenseur⋅se."
else: else:
s += f"Il reste {settings.PROBLEM_COUNT - 5 - len(td.rejected)} refus sans pénalité." s += f"Il reste {settings.PROBLEM_COUNT - 5 - len(td.rejected)} refus sans pénalité."
case 'WAITING_FINAL':
s += "Le tirage au sort pour le tour 2 aura lieu à la fin du premier tour. Bon courage !"
case 'DRAW_ENDED': case 'DRAW_ENDED':
s += "Le tirage au sort est terminé. Les solutions des autres équipes peuvent être trouvées dans l'onglet « Ma participation »." s += "Le tirage au sort est terminé. Les solutions des autres équipes peuvent être trouvées dans l'onglet « Ma participation »."
@ -210,7 +215,8 @@ class Pool(models.Model):
@property @property
def exportable(self): def exportable(self):
return self.associated_pool is None and all(td.accepted is not None for td in self.teamdraw_set.all()) return self.associated_pool is None and self.teamdraw_set.exists() \
and all(td.accepted is not None for td in self.teamdraw_set.all())
def export(self): def export(self):
from django.db import transaction from django.db import transaction
@ -222,6 +228,7 @@ class Pool(models.Model):
self.associated_pool.juries.set(self.round.draw.tournament.organizers.all()) self.associated_pool.juries.set(self.round.draw.tournament.organizers.all())
tds = list(self.team_draws) tds = list(self.team_draws)
self.associated_pool.participations.set([td.participation for td in tds]) self.associated_pool.participations.set([td.participation for td in tds])
self.save()
if len(tds) == 3: if len(tds) == 3:
table = [ table = [

View File

@ -32,6 +32,10 @@ function exportDraw(tid) {
sockets[tid].send(JSON.stringify({'type': 'export'})) sockets[tid].send(JSON.stringify({'type': 'export'}))
} }
function continueFinal(tid) {
sockets[tid].send(JSON.stringify({'type': 'continue_final'}))
}
function showNotification(title, body, timeout = 5000) { function showNotification(title, body, timeout = 5000) {
let notif = new Notification(title, {'body': body, 'icon': "/static/tfjm.svg"}) let notif = new Notification(title, {'body': body, 'icon': "/static/tfjm.svg"})
if (timeout) if (timeout)
@ -151,6 +155,14 @@ document.addEventListener('DOMContentLoaded', () => {
div.classList.add('d-none') div.classList.add('d-none')
} }
function updateContinueVisibility(visible) {
let div = document.getElementById(`continue-${tournament.id}`)
if (visible)
div.classList.remove('d-none')
else
div.classList.add('d-none')
}
function updatePoules(round, poules) { function updatePoules(round, poules) {
let roundList = document.getElementById(`recap-${tournament.id}-round-list`) let roundList = document.getElementById(`recap-${tournament.id}-round-list`)
let poolListId = `recap-${tournament.id}-round-${round}-pool-list` let poolListId = `recap-${tournament.id}-round-${round}-pool-list`
@ -508,6 +520,9 @@ document.addEventListener('DOMContentLoaded', () => {
case 'export_visibility': case 'export_visibility':
updateExportVisibility(data.visible) updateExportVisibility(data.visible)
break break
case 'continue_visibility':
updateContinueVisibility(data.visible)
break
case 'set_poules': case 'set_poules':
updatePoules(data.round, data.poules) updatePoules(data.round, data.poules)
break break

View File

@ -155,6 +155,14 @@
📁 {% trans "Export" %} 📁 {% trans "Export" %}
</button> </button>
</div> </div>
{% if tournament.final %}
<div id="continue-{{ tournament.id }}"
class="card-footer text-center{% if tournament.draw.get_state != 'WAITING_FINAL' %} d-none{% endif %}">
<button class="btn btn-success text-center" onclick="continueFinal({{ tournament.id }})">
➡️ {% trans "Continue draw" %}
</button>
</div>
{% endif %}
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -17,7 +17,7 @@ class DisplayView(LoginRequiredMixin, TemplateView):
if reg.is_admin: if reg.is_admin:
tournaments = Tournament.objects.order_by('id').all() tournaments = Tournament.objects.order_by('id').all()
elif reg.is_volunteer: elif reg.is_volunteer:
tournaments = reg.interesting_tournaments.order_by('id').all() tournaments = reg.interesting_tournaments
else: else:
tournaments = [reg.team.participation.tournament] tournaments = [reg.team.participation.tournament]
context['tournaments'] = tournaments context['tournaments'] = tournaments

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-03-25 07:20+0100\n" "POT-Creation-Date: 2023-03-26 11:05+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -25,162 +25,170 @@ msgstr "API"
msgid "Draw" msgid "Draw"
msgstr "Tirage au sort" msgstr "Tirage au sort"
#: draw/consumers.py:21 #: draw/consumers.py:23
msgid "You are not an organizer." msgid "You are not an organizer."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
#: draw/consumers.py:91 #: draw/consumers.py:98
msgid "Invalid format" msgid "Invalid format"
msgstr "Format invalide" msgstr "Format invalide"
#: draw/consumers.py:95 #: draw/consumers.py:102
#, python-brace-format #, python-brace-format
msgid "The sum must be equal to the number of teams: expected {len}, got {sum}" msgid "The sum must be equal to the number of teams: expected {len}, got {sum}"
msgstr "" msgstr ""
"La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}" "La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}"
#: draw/consumers.py:121 #: draw/consumers.py:128
msgid "Draw started!" msgid "Draw started!"
msgstr "Le tirage a commencé !" msgstr "Le tirage a commencé !"
#: draw/consumers.py:131 #: draw/consumers.py:138
#, python-brace-format #, python-brace-format
msgid "The draw for the tournament {tournament} will start." msgid "The draw for the tournament {tournament} will start."
msgstr "Le tirage au sort du tournoi {tournament} va commencer." msgstr "Le tirage au sort du tournoi {tournament} va commencer."
#: draw/consumers.py:142 #: draw/consumers.py:149
#, python-brace-format #, python-brace-format
msgid "The draw for the tournament {tournament} is aborted." msgid "The draw for the tournament {tournament} is aborted."
msgstr "Le tirage au sort du tournoi {tournament} est annulé." msgstr "Le tirage au sort du tournoi {tournament} est annulé."
#: draw/consumers.py:176 draw/consumers.py:179 #: draw/consumers.py:183 draw/consumers.py:186
msgid "You've already launched the dice." msgid "You've already launched the dice."
msgstr "Vous avez déjà lancé le dé." msgstr "Vous avez déjà lancé le dé."
#: draw/consumers.py:182 #: draw/consumers.py:189
msgid "It is not your turn." msgid "It is not your turn."
msgstr "Ce n'est pas votre tour." msgstr "Ce n'est pas votre tour."
#: draw/consumers.py:184 draw/consumers.py:353 draw/consumers.py:392 #: draw/consumers.py:191 draw/consumers.py:360 draw/consumers.py:399
#: draw/consumers.py:510 #: draw/consumers.py:526
msgid "This is not the time for this." msgid "This is not the time for this."
msgstr "Ce n'est pas le moment pour cela." msgstr "Ce n'est pas le moment pour cela."
#: draw/consumers.py:217 draw/consumers.py:316 #: draw/consumers.py:224 draw/consumers.py:323
#, python-brace-format #, python-brace-format
msgid "Dices from teams {teams} are identical. Please relaunch your dices." msgid "Dices from teams {teams} are identical. Please relaunch your dices."
msgstr "" msgstr ""
"Les dés des équipes {teams} sont identiques. Merci de relancer vos dés." "Les dés des équipes {teams} sont identiques. Merci de relancer vos dés."
#: draw/consumers.py:251 #: draw/consumers.py:258
msgid "Two pools are identical. Please relaunch your dices." msgid "Two pools are identical. Please relaunch your dices."
msgstr "Deux poules sont identiques. Merci de relancer vos dés." msgstr "Deux poules sont identiques. Merci de relancer vos dés."
#: draw/models.py:16 participation/models.py:295 participation/models.py:319 #: draw/consumers.py:598
msgid "This is only available for the final tournament."
msgstr "Cela n'est possible que pour la finale."
#: draw/models.py:17 participation/models.py:295 participation/models.py:319
#: participation/models.py:351 #: participation/models.py:351
msgid "tournament" msgid "tournament"
msgstr "tournoi" msgstr "tournoi"
#: draw/models.py:25 #: draw/models.py:26
msgid "current round" msgid "current round"
msgstr "tour actuel" msgstr "tour actuel"
#: draw/models.py:31 #: draw/models.py:32
msgid "last message" msgid "last message"
msgstr "dernier message" msgstr "dernier message"
#: draw/models.py:104 draw/models.py:112 #: draw/models.py:114 draw/models.py:122
msgid "draw" msgid "draw"
msgstr "tirage au sort" msgstr "tirage au sort"
#: draw/models.py:105 #: draw/models.py:115
msgid "draws" msgid "draws"
msgstr "tirages au sort" msgstr "tirages au sort"
#: draw/models.py:117 #: draw/models.py:127
msgid "Round 1" msgid "Round 1"
msgstr "Tour 1" msgstr "Tour 1"
#: draw/models.py:118 #: draw/models.py:128
msgid "Round 2" msgid "Round 2"
msgstr "Tour 2" msgstr "Tour 2"
#: draw/models.py:120 #: draw/models.py:130
msgid "number" msgid "number"
msgstr "numéro" msgstr "numéro"
#: draw/models.py:129 #: draw/models.py:139
msgid "current pool" msgid "current pool"
msgstr "poule actuelle" msgstr "poule actuelle"
#: draw/models.py:144 draw/models.py:215 participation/models.py:355 #: draw/models.py:154 draw/models.py:283 participation/models.py:355
msgid "round" msgid "round"
msgstr "tour" msgstr "tour"
#: draw/models.py:145 #: draw/models.py:155
msgid "rounds" msgid "rounds"
msgstr "tours" msgstr "tours"
#: draw/models.py:160 #: draw/models.py:170
msgid "letter" msgid "letter"
msgstr "lettre" msgstr "lettre"
#: draw/models.py:164 #: draw/models.py:174
#: participation/templates/participation/tournament_detail.html:15 #: participation/templates/participation/tournament_detail.html:15
msgid "size" msgid "size"
msgstr "taille" msgstr "taille"
#: draw/models.py:173 #: draw/models.py:183
msgid "current team" msgid "current team"
msgstr "équipe actuelle" msgstr "équipe actuelle"
#: draw/models.py:201 draw/models.py:223 #: draw/models.py:192
msgid "associated pool"
msgstr "poule associée"
#: draw/models.py:269 draw/models.py:291
#: draw/templates/draw/tournament_content.html:70 #: draw/templates/draw/tournament_content.html:70
#: draw/templates/draw/tournament_content.html:169 participation/models.py:407 #: draw/templates/draw/tournament_content.html:185 participation/models.py:407
#: participation/models.py:415 #: participation/models.py:415
msgid "pool" msgid "pool"
msgstr "poule" msgstr "poule"
#: draw/models.py:202 participation/models.py:408 #: draw/models.py:270 participation/models.py:408
msgid "pools" msgid "pools"
msgstr "poules" msgstr "poules"
#: draw/models.py:209 participation/models.py:342 participation/models.py:539 #: draw/models.py:277 participation/models.py:342 participation/models.py:539
#: participation/models.py:569 participation/models.py:607 #: participation/models.py:569 participation/models.py:607
msgid "participation" msgid "participation"
msgstr "participation" msgstr "participation"
#: draw/models.py:230 #: draw/models.py:298
msgid "passage index" msgid "passage index"
msgstr "numéro de passage" msgstr "numéro de passage"
#: draw/models.py:237 #: draw/models.py:305
msgid "choose index" msgid "choose index"
msgstr "numéro de choix" msgstr "numéro de choix"
#: draw/models.py:242 draw/models.py:258 participation/models.py:422 #: draw/models.py:310 draw/models.py:326 participation/models.py:422
#: participation/models.py:576 #: participation/models.py:576
#, python-brace-format #, python-brace-format
msgid "Problem #{problem}" msgid "Problem #{problem}"
msgstr "Problème n°{problem}" msgstr "Problème n°{problem}"
#: draw/models.py:246 draw/models.py:262 #: draw/models.py:314 draw/models.py:330
msgid "accepted problem" msgid "accepted problem"
msgstr "problème accepté" msgstr "problème accepté"
#: draw/models.py:253 #: draw/models.py:321
msgid "last dice" msgid "last dice"
msgstr "dernier dé" msgstr "dernier dé"
#: draw/models.py:267 #: draw/models.py:335
msgid "rejected problems" msgid "rejected problems"
msgstr "problèmes rejetés" msgstr "problèmes rejetés"
#: draw/models.py:275 #: draw/models.py:347
msgid "team draw" msgid "team draw"
msgstr "tirage d'équipe" msgstr "tirage d'équipe"
#: draw/models.py:276 #: draw/models.py:348
msgid "team draws" msgid "team draws"
msgstr "tirages d'équipe" msgstr "tirages d'équipe"
@ -220,16 +228,24 @@ msgstr "Accepter"
msgid "Decline" msgid "Decline"
msgstr "Refuser" msgstr "Refuser"
#: draw/templates/draw/tournament_content.html:176 participation/models.py:125 #: draw/templates/draw/tournament_content.html:155
msgid "Export"
msgstr "Exporter"
#: draw/templates/draw/tournament_content.html:162
msgid "Continue draw"
msgstr "Continuer le tirage"
#: draw/templates/draw/tournament_content.html:192 participation/models.py:125
#: participation/models.py:310 registration/models.py:127 #: participation/models.py:310 registration/models.py:127
msgid "team" msgid "team"
msgstr "équipe" msgstr "équipe"
#: draw/templates/draw/tournament_content.html:186 #: draw/templates/draw/tournament_content.html:202
#: draw/templates/draw/tournament_content.html:187 #: draw/templates/draw/tournament_content.html:203
#: draw/templates/draw/tournament_content.html:188 #: draw/templates/draw/tournament_content.html:204
#: draw/templates/draw/tournament_content.html:189 #: draw/templates/draw/tournament_content.html:205
#: draw/templates/draw/tournament_content.html:190 #: draw/templates/draw/tournament_content.html:206
msgid "Room" msgid "Room"
msgstr "Salle" msgstr "Salle"