From 8778f58fe4ce9391b468a92e1b6c0febcbb6b571 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Thu, 6 Apr 2023 00:19:24 +0200 Subject: [PATCH] The draw is now fully reversible Signed-off-by: Emmy D'Anello --- draw/consumers.py | 213 +++++++++++++++++++- draw/templates/draw/tournament_content.html | 31 +-- 2 files changed, 220 insertions(+), 24 deletions(-) diff --git a/draw/consumers.py b/draw/consumers.py index d7c644f..bb69436 100644 --- a/draw/consumers.py +++ b/draw/consumers.py @@ -1,7 +1,8 @@ # Copyright (C) 2023 by Animath # SPDX-License-Identifier: GPL-3.0-or-later -import json + from collections import OrderedDict +import json from random import randint, shuffle from channels.generic.websocket import AsyncJsonWebsocketConsumer @@ -350,11 +351,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): if values.count(v) > 1: # v is a duplicate value # Get all teams that have the same result - dups = [td for td in tds if td.passage_dice == v] + dups = [td for td in tds if (td.passage_dice if state == 'DICE_SELECT_POULES' else td.choice_dice) == v] for dup in dups: # Reset the dice - dup.passage_dice = None + if state == 'DICE_SELECT_POULES': + dup.passage_dice = None + else: + dup.choice_dice = None await dup.asave() await self.channel_layer.group_send( f"tournament-{self.tournament.id}", @@ -938,6 +942,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): if not await Draw.objects.filter(tournament=self.tournament).aexists(): return await self.alert(_("The draw has not started yet."), 'danger') + content_type = await ContentType.objects.aget(app_label=TeamDraw._meta.app_label, + model=TeamDraw._meta.model_name) + state = self.tournament.draw.get_state() self.tournament.draw.last_message = "" @@ -952,7 +959,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): await td.asave() await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", - {'type': 'draw.continue_visibility', 'visible': False}) + {'type': 'draw.continue_visibility', 'visible': False}) await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", {'type': 'draw.buttons_visibility', 'visible': True}) @@ -989,8 +996,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): if accepted_tds or has_rejected_one_tds: # One team of the already accepted or its problem, we fetch the last one changelogs = Changelog.objects.filter( - model=await ContentType.objects.aget(app_label=TeamDraw._meta.app_label, - model=TeamDraw._meta.model_name), + model=content_type, action='edit', instance_pk__in=set(accepted_tds.keys()).union(set(has_rejected_one_tds.keys())) ).order_by('-timestamp') @@ -999,7 +1005,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): previous = json.loads(changelog.previous) data = json.loads(changelog.data) pk = int(changelog.instance_pk) - print(previous, data, pk) + if 'accepted' in data and data['accepted'] and pk in accepted_tds: # Undo the last acceptance last_td = accepted_tds[pk] @@ -1043,7 +1049,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): # Return to the dice choice pool_tds = {td.id: td async for td in p.team_draws.prefetch_related('participation__team')} changelogs = Changelog.objects.filter( - model=ContentType.objects.get_for_model(TeamDraw), + model=content_type, action='edit', instance_pk__in=set(pool_tds.keys()) ).order_by('-timestamp') @@ -1076,13 +1082,202 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {'type': 'draw.box_visibility', 'visible': False}) + elif state == 'DICE_ORDER_POULE': + p = r.current_pool + already_launched_tds = {td.id: td async for td in p.team_draws.filter(choice_dice__isnull=False) + .prefetch_related('participation__team')} + + if already_launched_tds: + # Reset the last dice + changelogs = Changelog.objects.filter( + model=content_type, + action='edit', + instance_pk__in=set(already_launched_tds.keys()) + ).order_by('-timestamp') + + # Find the last dice that was launched + async for changelog in changelogs: + data = json.loads(changelog.data) + if 'choice_dice' in data and data['choice_dice']: + last_td = already_launched_tds[int(changelog.instance_pk)] + # Reset the dice + last_td.choice_dice = None + await last_td.asave() + + # Reset the dice on the interface + await self.channel_layer.group_send( + f"tournament-{self.tournament.id}", {'type': 'draw.dice', + 'team': last_td.participation.team.trigram, + 'result': None}) + break + else: + # Go to the previous pool if possible + if p.letter > 1: + # Go to the previous pool + previous_pool = await r.pool_set.prefetch_related('current_team__participation__team')\ + .aget(letter=p.letter - 1) + r.current_pool = previous_pool + await r.asave() + + 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}", + {'type': 'draw.dice_visibility', 'visible': False}) + + await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", + {'type': 'draw.buttons_visibility', 'visible': True}) + await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", + {'type': 'draw.buttons_visibility', 'visible': True}) + + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.set_problem', + 'round': r.number, + 'team': td.participation.team.trigram, + 'problem': td.accepted}) + elif r.number == 2: + if not self.tournament.final: + # Go to the previous round + r1 = await self.tournament.draw.round_set\ + .prefetch_related('current_pool__current_team__participation__team').aget(number=1) + self.tournament.draw.current_round = r1 + await self.tournament.draw.asave() + + async for td in r1.team_draws.prefetch_related('participation__team').all(): + await self.channel_layer.group_send( + f"tournament-{self.tournament.id}", {'type': 'draw.dice', + 'team': td.participation.team.trigram, + 'result': td.choice_dice}) + + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.send_poules', 'round': r1}) + + previous_pool = r1.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}", + {'type': 'draw.dice_visibility', 'visible': False}) + + await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", + {'type': 'draw.buttons_visibility', 'visible': True}) + await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", + {'type': 'draw.buttons_visibility', 'visible': True}) + + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.set_problem', + 'round': r1.number, + 'team': td.participation.team.trigram, + 'problem': td.accepted}) + else: + # Don't continue the final tournament + r1 = await self.tournament.draw.round_set \ + .prefetch_related('current_pool__current__team__participation__team').aget(number=1) + self.tournament.draw.current_round = r1 + await self.tournament.draw.asave() + + async for td in r.teamdraw_set.all(): + td.pool = None + td.choose_index = None + td.choice_dice = None + await td.asave() + + async for td in r1.team_draws.prefetch_related('participation__team').all(): + await self.channel_layer.group_send( + f"tournament-{self.tournament.id}", {'type': 'draw.dice', + 'team': td.participation.team.trigram, + 'result': td.choice_dice}) + + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.dice_visibility', 'visible': False}) + await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", + {'type': 'draw.continue_visibility', '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() + + r.current_pool = None + await r.asave() + + round_tds = {td.id: td async for td in r.team_draws.prefetch_related('participation__team')} + + # Reset the last dice + changelogs = Changelog.objects.filter( + model=content_type, + action='edit', + instance_pk__in=set(round_tds.keys()) + ).order_by('-timestamp') + + # Find the last dice that was launched + async for changelog in changelogs: + data = json.loads(changelog.data) + if 'passage_dice' in data and data['passage_dice']: + last_td = round_tds[int(changelog.instance_pk)] + # Reset the dice + last_td.passage_dice = None + await last_td.asave() + + # Reset the dice on the interface + await self.channel_layer.group_send( + f"tournament-{self.tournament.id}", {'type': 'draw.dice', + 'team': last_td.participation.team.trigram, + 'result': None}) + break + + async for td in r.team_draws.prefetch_related('participation__team').all(): + await self.channel_layer.group_send( + f"tournament-{self.tournament.id}", {'type': 'draw.dice', + 'team': td.participation.team.trigram, + 'result': td.passage_dice}) + + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.dice_visibility', 'visible': True}) + elif state == 'DICE_SELECT_POULES': + already_launched_tds = {td.id: td async for td in r.team_draws.filter(passage_dice__isnull=False) + .prefetch_related('participation__team')} + + if already_launched_tds: + # Reset the last dice + changelogs = Changelog.objects.filter( + model=content_type, + action='edit', + instance_pk__in=set(already_launched_tds.keys()) + ).order_by('-timestamp') + + # Find the last dice that was launched + async for changelog in changelogs: + data = json.loads(changelog.data) + if 'passage_dice' in data and data['passage_dice']: + last_td = already_launched_tds[int(changelog.instance_pk)] + # Reset the dice + last_td.passage_dice = None + await last_td.asave() + + # Reset the dice on the interface + await self.channel_layer.group_send( + f"tournament-{self.tournament.id}", {'type': 'draw.dice', + 'team': last_td.participation.team.trigram, + 'result': None}) + break + else: + await self.abort() 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): """ Send alert to the current user. diff --git a/draw/templates/draw/tournament_content.html b/draw/templates/draw/tournament_content.html index 6269920..765a1d7 100644 --- a/draw/templates/draw/tournament_content.html +++ b/draw/templates/draw/tournament_content.html @@ -54,6 +54,13 @@
Recap + {% if user.registration.is_volunteer %} + + {% endif %}
@@ -177,12 +184,6 @@
{% endif %} - {% endif %}
@@ -326,16 +327,16 @@ {% endfor %} - -{% if user.registration.is_volunteer %} - {# Volunteers can click on this button to abort the draw #} -
- -
-{% endif %} + {% if user.registration.is_volunteer %} + {# Volunteers can click on this button to abort the draw #} +
+ +
+ {% endif %} +