The draw is now fully reversible

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2023-04-06 00:19:24 +02:00
parent 751e35ac62
commit 8778f58fe4
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
2 changed files with 220 additions and 24 deletions

View File

@ -1,7 +1,8 @@
# Copyright (C) 2023 by Animath # Copyright (C) 2023 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import json
from collections import OrderedDict from collections import OrderedDict
import json
from random import randint, shuffle from random import randint, shuffle
from channels.generic.websocket import AsyncJsonWebsocketConsumer from channels.generic.websocket import AsyncJsonWebsocketConsumer
@ -350,11 +351,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
if values.count(v) > 1: if values.count(v) > 1:
# v is a duplicate value # v is a duplicate value
# Get all teams that have the same result # 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: for dup in dups:
# Reset the dice # 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 dup.asave()
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", f"tournament-{self.tournament.id}",
@ -938,6 +942,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
if not await Draw.objects.filter(tournament=self.tournament).aexists(): if not await Draw.objects.filter(tournament=self.tournament).aexists():
return await self.alert(_("The draw has not started yet."), 'danger') 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() state = self.tournament.draw.get_state()
self.tournament.draw.last_message = "" self.tournament.draw.last_message = ""
@ -952,7 +959,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave() await td.asave()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", 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}", await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'type': 'draw.buttons_visibility', 'visible': True})
@ -989,8 +996,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
if accepted_tds or has_rejected_one_tds: if accepted_tds or has_rejected_one_tds:
# One team of the already accepted or its problem, we fetch the last one # One team of the already accepted or its problem, we fetch the last one
changelogs = Changelog.objects.filter( changelogs = Changelog.objects.filter(
model=await ContentType.objects.aget(app_label=TeamDraw._meta.app_label, model=content_type,
model=TeamDraw._meta.model_name),
action='edit', action='edit',
instance_pk__in=set(accepted_tds.keys()).union(set(has_rejected_one_tds.keys())) instance_pk__in=set(accepted_tds.keys()).union(set(has_rejected_one_tds.keys()))
).order_by('-timestamp') ).order_by('-timestamp')
@ -999,7 +1005,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
previous = json.loads(changelog.previous) previous = json.loads(changelog.previous)
data = json.loads(changelog.data) data = json.loads(changelog.data)
pk = int(changelog.instance_pk) pk = int(changelog.instance_pk)
print(previous, data, pk)
if 'accepted' in data and data['accepted'] and pk in accepted_tds: if 'accepted' in data and data['accepted'] and pk in accepted_tds:
# Undo the last acceptance # Undo the last acceptance
last_td = accepted_tds[pk] last_td = accepted_tds[pk]
@ -1043,7 +1049,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Return to the dice choice # Return to the dice choice
pool_tds = {td.id: td async for td in p.team_draws.prefetch_related('participation__team')} pool_tds = {td.id: td async for td in p.team_draws.prefetch_related('participation__team')}
changelogs = Changelog.objects.filter( changelogs = Changelog.objects.filter(
model=ContentType.objects.get_for_model(TeamDraw), model=content_type,
action='edit', action='edit',
instance_pk__in=set(pool_tds.keys()) instance_pk__in=set(pool_tds.keys())
).order_by('-timestamp') ).order_by('-timestamp')
@ -1076,13 +1082,202 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': False}) {'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}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info', 'draw': self.tournament.draw})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active', 'draw': self.tournament.draw})
async def draw_alert(self, content): async def draw_alert(self, content):
""" """
Send alert to the current user. Send alert to the current user.

View File

@ -54,6 +54,13 @@
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
Recap Recap
{% if user.registration.is_volunteer %}
<button id="cancel-last-step-{{ tournament.id }}"
class="badge rounded-pill text-bg-warning"
onclick="cancelLastStep({{ tournament.id }})">
🔙 {% trans "Cancel last step" %}
</button>
{% endif %}
</div> </div>
<div class="card-body"> <div class="card-body">
<div id="recap-{{ tournament.id }}-round-list" class="row"> <div id="recap-{{ tournament.id }}-round-list" class="row">
@ -177,12 +184,6 @@
</button> </button>
</div> </div>
{% endif %} {% endif %}
<div id="cancel-last-step-{{ tournament.id }}"
class="card-footer text-center">
<button class="badge rounded-pill text-bg-warning" onclick="cancelLastStep({{ tournament.id }})">
🔙 {% trans "Cancel last step" %}
</button>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -326,16 +327,16 @@
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</div>
{% if user.registration.is_volunteer %} {% if user.registration.is_volunteer %}
{# Volunteers can click on this button to abort the draw #} {# Volunteers can click on this button to abort the draw #}
<div class="text-center mt-3"> <div class="text-center mt-3">
<button id="abort-{{ tournament.id }}" class="badge rounded-pill text-bg-danger" data-bs-toggle="modal" data-bs-target="#abort{{ tournament.id }}Modal"> <button id="abort-{{ tournament.id }}" class="badge rounded-pill text-bg-danger" data-bs-toggle="modal" data-bs-target="#abort{{ tournament.id }}Modal">
{% trans "Abort" %} {% trans "Abort" %}
</button> </button>
</div> </div>
{% endif %} {% endif %}
</div>
<div id="abort{{ tournament.id }}Modal" class="modal fade" tabindex="-1" role="dialog"> <div id="abort{{ tournament.id }}Modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">