diff --git a/draw/consumers.py b/draw/consumers.py index 4a47e7b..1101c85 100644 --- a/draw/consumers.py +++ b/draw/consumers.py @@ -34,8 +34,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): .prefetch_related('draw__current_round__current_pool__current_team').aget() self.participations = [] - async for participation in Participation.objects.filter(tournament=self.tournament, valid=True)\ - .prefetch_related('team'): + async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'): self.participations.append(participation) user = self.scope['user'] @@ -88,6 +87,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): await self.reject_problem(**content) case 'export': await self.export(**content) + case 'continue_final': + await self.continue_final(**content) @ensure_orga async def start_draw(self, fmt, **kwargs): @@ -487,8 +488,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): {'type': 'draw.dice_visibility', 'visible': True}) else: # Round is ended - # TODO: For the final tournament, add some adjustments - if r.number == 1: + if r.number == 1 and not self.tournament.final: # Next round r2 = await self.tournament.draw.round_set.filter(number=2).aget() self.tournament.draw.current_round = r2 @@ -505,6 +505,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): {'type': 'draw.dice_visibility', 'visible': True}) await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", {'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 += "

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}", {'type': 'draw.export_visibility', 'visible': True}) @@ -587,6 +592,56 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", {'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): return await self.alert(**content) @@ -611,6 +666,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): async def draw_export_visibility(self, content): 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): await self.send_json({'type': 'set_poules', 'round': content['round'].number, 'poules': [{'letter': pool.get_letter_display(), 'teams': await pool.atrigrams()} diff --git a/draw/models.py b/draw/models.py index 7d8a1f3..f91ac5d 100644 --- a/draw/models.py +++ b/draw/models.py @@ -42,7 +42,10 @@ 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: - 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: return 'WAITING_DRAW_PROBLEM' 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." else: 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': 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 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): from django.db import transaction @@ -222,6 +228,7 @@ class Pool(models.Model): self.associated_pool.juries.set(self.round.draw.tournament.organizers.all()) tds = list(self.team_draws) self.associated_pool.participations.set([td.participation for td in tds]) + self.save() if len(tds) == 3: table = [ diff --git a/draw/static/draw.js b/draw/static/draw.js index 8cd5194..d1d3b2e 100644 --- a/draw/static/draw.js +++ b/draw/static/draw.js @@ -32,6 +32,10 @@ function exportDraw(tid) { sockets[tid].send(JSON.stringify({'type': 'export'})) } +function continueFinal(tid) { + sockets[tid].send(JSON.stringify({'type': 'continue_final'})) +} + function showNotification(title, body, timeout = 5000) { let notif = new Notification(title, {'body': body, 'icon': "/static/tfjm.svg"}) if (timeout) @@ -151,6 +155,14 @@ document.addEventListener('DOMContentLoaded', () => { 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) { let roundList = document.getElementById(`recap-${tournament.id}-round-list`) let poolListId = `recap-${tournament.id}-round-${round}-pool-list` @@ -508,6 +520,9 @@ document.addEventListener('DOMContentLoaded', () => { case 'export_visibility': updateExportVisibility(data.visible) break + case 'continue_visibility': + updateContinueVisibility(data.visible) + break case 'set_poules': updatePoules(data.round, data.poules) break diff --git a/draw/templates/draw/tournament_content.html b/draw/templates/draw/tournament_content.html index f0d68fb..f3a8748 100644 --- a/draw/templates/draw/tournament_content.html +++ b/draw/templates/draw/tournament_content.html @@ -155,6 +155,14 @@ 📁 {% trans "Export" %} + {% if tournament.final %} + + {% endif %} {% endif %} diff --git a/draw/views.py b/draw/views.py index fc4ffb8..dfe655b 100644 --- a/draw/views.py +++ b/draw/views.py @@ -17,7 +17,7 @@ class DisplayView(LoginRequiredMixin, TemplateView): if reg.is_admin: tournaments = Tournament.objects.order_by('id').all() elif reg.is_volunteer: - tournaments = reg.interesting_tournaments.order_by('id').all() + tournaments = reg.interesting_tournaments else: tournaments = [reg.team.participation.tournament] context['tournaments'] = tournaments diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 3e79205..194019e 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\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" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -25,162 +25,170 @@ msgstr "API" msgid "Draw" msgstr "Tirage au sort" -#: draw/consumers.py:21 +#: draw/consumers.py:23 msgid "You are not an organizer." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice." -#: draw/consumers.py:91 +#: draw/consumers.py:98 msgid "Invalid format" msgstr "Format invalide" -#: draw/consumers.py:95 +#: draw/consumers.py:102 #, 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:121 +#: draw/consumers.py:128 msgid "Draw started!" msgstr "Le tirage a commencé !" -#: draw/consumers.py:131 +#: draw/consumers.py:138 #, 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:142 +#: draw/consumers.py:149 #, 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:176 draw/consumers.py:179 +#: draw/consumers.py:183 draw/consumers.py:186 msgid "You've already launched the dice." msgstr "Vous avez déjà lancé le dé." -#: draw/consumers.py:182 +#: draw/consumers.py:189 msgid "It is not your turn." msgstr "Ce n'est pas votre tour." -#: draw/consumers.py:184 draw/consumers.py:353 draw/consumers.py:392 -#: draw/consumers.py:510 +#: draw/consumers.py:191 draw/consumers.py:360 draw/consumers.py:399 +#: draw/consumers.py:526 msgid "This is not the time for this." 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 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:251 +#: draw/consumers.py:258 msgid "Two pools are identical. Please relaunch your dices." 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 msgid "tournament" msgstr "tournoi" -#: draw/models.py:25 +#: draw/models.py:26 msgid "current round" msgstr "tour actuel" -#: draw/models.py:31 +#: draw/models.py:32 msgid "last message" msgstr "dernier message" -#: draw/models.py:104 draw/models.py:112 +#: draw/models.py:114 draw/models.py:122 msgid "draw" msgstr "tirage au sort" -#: draw/models.py:105 +#: draw/models.py:115 msgid "draws" msgstr "tirages au sort" -#: draw/models.py:117 +#: draw/models.py:127 msgid "Round 1" msgstr "Tour 1" -#: draw/models.py:118 +#: draw/models.py:128 msgid "Round 2" msgstr "Tour 2" -#: draw/models.py:120 +#: draw/models.py:130 msgid "number" msgstr "numéro" -#: draw/models.py:129 +#: draw/models.py:139 msgid "current pool" 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" msgstr "tour" -#: draw/models.py:145 +#: draw/models.py:155 msgid "rounds" msgstr "tours" -#: draw/models.py:160 +#: draw/models.py:170 msgid "letter" msgstr "lettre" -#: draw/models.py:164 +#: draw/models.py:174 #: participation/templates/participation/tournament_detail.html:15 msgid "size" msgstr "taille" -#: draw/models.py:173 +#: draw/models.py:183 msgid "current team" 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:169 participation/models.py:407 +#: draw/templates/draw/tournament_content.html:185 participation/models.py:407 #: participation/models.py:415 msgid "pool" msgstr "poule" -#: draw/models.py:202 participation/models.py:408 +#: draw/models.py:270 participation/models.py:408 msgid "pools" 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 msgid "participation" msgstr "participation" -#: draw/models.py:230 +#: draw/models.py:298 msgid "passage index" msgstr "numéro de passage" -#: draw/models.py:237 +#: draw/models.py:305 msgid "choose index" 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 #, python-brace-format msgid "Problem #{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" msgstr "problème accepté" -#: draw/models.py:253 +#: draw/models.py:321 msgid "last dice" msgstr "dernier dé" -#: draw/models.py:267 +#: draw/models.py:335 msgid "rejected problems" msgstr "problèmes rejetés" -#: draw/models.py:275 +#: draw/models.py:347 msgid "team draw" msgstr "tirage d'équipe" -#: draw/models.py:276 +#: draw/models.py:348 msgid "team draws" msgstr "tirages d'équipe" @@ -220,16 +228,24 @@ msgstr "Accepter" msgid "Decline" 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 msgid "team" msgstr "équipe" -#: draw/templates/draw/tournament_content.html:186 -#: draw/templates/draw/tournament_content.html:187 -#: draw/templates/draw/tournament_content.html:188 -#: draw/templates/draw/tournament_content.html:189 -#: draw/templates/draw/tournament_content.html:190 +#: draw/templates/draw/tournament_content.html:202 +#: draw/templates/draw/tournament_content.html:203 +#: draw/templates/draw/tournament_content.html:204 +#: draw/templates/draw/tournament_content.html:205 +#: draw/templates/draw/tournament_content.html:206 msgid "Room" msgstr "Salle"