702 lines
36 KiB
Python
702 lines
36 KiB
Python
# Copyright (C) 2023 by Animath
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from collections import OrderedDict
|
|
from random import randint, shuffle
|
|
|
|
from asgiref.sync import sync_to_async
|
|
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
|
from django.conf import settings
|
|
from django.utils import translation
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from draw.models import Draw, Round, Pool, TeamDraw
|
|
from participation.models import Tournament, Participation
|
|
from registration.models import Registration
|
|
|
|
|
|
def ensure_orga(f):
|
|
async def func(self, *args, **kwargs):
|
|
reg = self.registration
|
|
if reg.is_volunteer and not reg.is_admin and self.tournament not in reg.interesting_tournaments \
|
|
or not reg.is_volunteer:
|
|
return await self.alert(_("You are not an organizer."), 'danger')
|
|
|
|
return await f(self, *args, **kwargs)
|
|
|
|
return func
|
|
|
|
|
|
class DrawConsumer(AsyncJsonWebsocketConsumer):
|
|
async def connect(self):
|
|
self.tournament_id = self.scope['url_route']['kwargs']['tournament_id']
|
|
self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\
|
|
.prefetch_related('draw__current_round__current_pool__current_team').aget()
|
|
|
|
self.participations = []
|
|
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'):
|
|
self.participations.append(participation)
|
|
|
|
user = self.scope['user']
|
|
reg = await Registration.objects.aget(user=user)
|
|
self.registration = reg
|
|
if reg.is_volunteer and not reg.is_admin and self.tournament not in reg.interesting_tournaments \
|
|
or not reg.is_volunteer and reg.team.participation.tournament != self.tournament:
|
|
# This user may not have access to the drawing session
|
|
await self.close()
|
|
return
|
|
|
|
await self.accept()
|
|
await self.channel_layer.group_add(f"tournament-{self.tournament.id}", self.channel_name)
|
|
if not self.registration.is_volunteer:
|
|
await self.channel_layer.group_add(f"team-{self.registration.team.trigram}", self.channel_name)
|
|
else:
|
|
await self.channel_layer.group_add(f"volunteer-{self.tournament.id}", self.channel_name)
|
|
|
|
async def disconnect(self, close_code):
|
|
await self.channel_layer.group_discard(f"tournament-{self.tournament.id}", self.channel_name)
|
|
if not self.registration.is_volunteer:
|
|
await self.channel_layer.group_discard(f"team-{self.registration.team.trigram}", self.channel_name)
|
|
else:
|
|
await self.channel_layer.group_discard(f"volunteer-{self.tournament.id}", self.channel_name)
|
|
|
|
async def alert(self, message: str, alert_type: str = 'info', **kwargs):
|
|
return await self.send_json({'type': 'alert', 'alert_type': alert_type, 'message': str(message)})
|
|
|
|
async def receive_json(self, content, **kwargs):
|
|
print(content)
|
|
|
|
# Refresh tournament
|
|
self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\
|
|
.prefetch_related('draw__current_round__current_pool__current_team').aget()
|
|
|
|
match content['type']:
|
|
case 'set_language':
|
|
translation.activate(content['language'])
|
|
case 'start_draw':
|
|
await self.start_draw(**content)
|
|
case 'abort':
|
|
await self.abort(**content)
|
|
case 'dice':
|
|
await self.process_dice(**content)
|
|
case 'draw_problem':
|
|
await self.select_problem(**content)
|
|
case 'accept':
|
|
await self.accept_problem(**content)
|
|
case 'reject':
|
|
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):
|
|
try:
|
|
fmt = list(map(int, fmt.split('+')))
|
|
except ValueError as e:
|
|
return await self.alert(_("Invalid format"), 'danger')
|
|
|
|
if sum(fmt) != len(self.participations):
|
|
return await self.alert(
|
|
_("The sum must be equal to the number of teams: expected {len}, got {sum}")\
|
|
.format(len=len(self.participations), sum=sum(fmt)), 'danger')
|
|
|
|
draw = await Draw.objects.acreate(tournament=self.tournament)
|
|
r1 = None
|
|
for i in [1, 2]:
|
|
r = await Round.objects.acreate(draw=draw, number=i)
|
|
if i == 1:
|
|
r1 = r
|
|
|
|
for j, f in enumerate(fmt):
|
|
await Pool.objects.acreate(round=r, letter=j + 1, size=f)
|
|
for participation in self.participations:
|
|
await TeamDraw.objects.acreate(participation=participation, round=r)
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.send_poules', 'round': r})
|
|
|
|
draw.current_round = r1
|
|
await sync_to_async(draw.save)()
|
|
|
|
async for td in r1.teamdraw_set.prefetch_related('participation__team').all():
|
|
await self.channel_layer.group_send(f"team-{td.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.alert(_("Draw started!"), 'success')
|
|
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.start', 'fmt': fmt, 'draw': draw})
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.set_info', 'draw': draw})
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.set_active', 'draw': self.tournament.draw})
|
|
|
|
async def draw_start(self, content):
|
|
await self.alert(_("The draw for the tournament {tournament} will start.")\
|
|
.format(tournament=self.tournament.name), 'warning')
|
|
await self.send_json({'type': 'draw_start', 'fmt': content['fmt'],
|
|
'trigrams': [p.team.trigram for p in self.participations]})
|
|
|
|
@ensure_orga
|
|
async def abort(self, **kwargs):
|
|
await sync_to_async(self.tournament.draw.delete)()
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {'type': 'draw_abort'})
|
|
|
|
async def draw_abort(self, content):
|
|
await self.alert(_("The draw for the tournament {tournament} is aborted.")\
|
|
.format(tournament=self.tournament.name), 'danger')
|
|
await self.send_json({'type': 'abort'})
|
|
|
|
|
|
async def process_dice(self, trigram: str | None = None, **kwargs):
|
|
state = await sync_to_async(self.tournament.draw.get_state)()
|
|
|
|
if self.registration.is_volunteer:
|
|
if trigram:
|
|
participation = await Participation.objects.filter(team__trigram=trigram)\
|
|
.prefetch_related('team').aget()
|
|
else:
|
|
# First free team
|
|
if state == 'DICE_ORDER_POULE':
|
|
participation = await Participation.objects\
|
|
.filter(teamdraw__pool=self.tournament.draw.current_round.current_pool,
|
|
teamdraw__last_dice__isnull=True).prefetch_related('team').afirst()
|
|
else:
|
|
participation = await Participation.objects\
|
|
.filter(teamdraw__round=self.tournament.draw.current_round,
|
|
teamdraw__last_dice__isnull=True).prefetch_related('team').afirst()
|
|
trigram = participation.team.trigram
|
|
else:
|
|
participation = await Participation.objects.filter(team__participants=self.registration)\
|
|
.prefetch_related('team').aget()
|
|
trigram = participation.team.trigram
|
|
|
|
team_draw = await TeamDraw.objects.filter(participation=participation,
|
|
round_id=self.tournament.draw.current_round_id).aget()
|
|
|
|
match state:
|
|
case 'DICE_SELECT_POULES':
|
|
if team_draw.last_dice is not None:
|
|
return await self.alert(_("You've already launched the dice."), 'danger')
|
|
case 'DICE_ORDER_POULE':
|
|
if team_draw.last_dice is not None:
|
|
return await self.alert(_("You've already launched the dice."), 'danger')
|
|
if not await self.tournament.draw.current_round.current_pool.teamdraw_set\
|
|
.filter(participation=participation).aexists():
|
|
return await self.alert(_("It is not your turn."), 'danger')
|
|
case _:
|
|
return await self.alert(_("This is not the time for this."), 'danger')
|
|
|
|
res = randint(1, 100)
|
|
team_draw.last_dice = res
|
|
await sync_to_async(team_draw.save)()
|
|
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}", {'type': 'draw.dice', 'team': trigram, 'result': res})
|
|
|
|
if state == 'DICE_SELECT_POULES' and \
|
|
not await TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id,
|
|
last_dice__isnull=True).aexists():
|
|
tds = []
|
|
async for td in TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id)\
|
|
.prefetch_related('participation__team'):
|
|
tds.append(td)
|
|
|
|
dices = {td: td.last_dice for td in tds}
|
|
values = list(dices.values())
|
|
error = False
|
|
for v in set(values):
|
|
if values.count(v) > 1:
|
|
dups = [td for td in tds if td.last_dice == v]
|
|
|
|
for dup in dups:
|
|
dup.last_dice = None
|
|
await sync_to_async(dup.save)()
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice', 'team': dup.participation.team.trigram, 'result': None})
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.alert',
|
|
'message': _('Dices from teams {teams} are identical. Please relaunch your dices.').format(
|
|
teams=', '.join(td.participation.team.trigram for td in dups)),
|
|
'alert_type': 'warning'})
|
|
error = True
|
|
|
|
if error:
|
|
return
|
|
|
|
tds.sort(key=lambda td: td.last_dice)
|
|
tds_copy = tds.copy()
|
|
|
|
async for p in Pool.objects.filter(round_id=self.tournament.draw.current_round_id).order_by('letter').all():
|
|
while (c := await TeamDraw.objects.filter(pool=p).acount()) < p.size:
|
|
td = tds_copy.pop(0)
|
|
td.pool = p
|
|
td.passage_index = c
|
|
await sync_to_async(td.save)()
|
|
|
|
if self.tournament.draw.current_round.number == 2 \
|
|
and await self.tournament.draw.current_round.pool_set.acount() >= 2:
|
|
# Check that we don't have a same pool as the first day
|
|
async for p1 in Pool.objects.filter(round__draw=self.tournament.draw, round__number=1).all():
|
|
async for p2 in Pool.objects.filter(round_id=self.tournament.draw.current_round_id).all():
|
|
if await sync_to_async(lambda: set(td['id'] for td in p1.teamdraw_set.values('id')))() \
|
|
== await sync_to_async(lambda:set(td['id'] for td in p2.teamdraw_set.values('id')))():
|
|
await TeamDraw.objects.filter(round=self.tournament.draw.current_round)\
|
|
.aupdate(last_dice=None, pool=None, passage_index=None)
|
|
for td in tds:
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None})
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.alert',
|
|
'message': _('Two pools are identical. Please relaunch your dices.'),
|
|
'alert_type': 'warning'})
|
|
return
|
|
|
|
pool = await Pool.objects.filter(round=self.tournament.draw.current_round, letter=1).aget()
|
|
self.tournament.draw.current_round.current_pool = pool
|
|
await sync_to_async(self.tournament.draw.current_round.save)()
|
|
|
|
msg = "Les résultats des dés sont les suivants : "
|
|
msg += await sync_to_async(lambda: ", ".join(
|
|
f"<strong>{td.participation.team.trigram}</strong> ({td.last_dice})"
|
|
for td in self.tournament.draw.current_round.team_draws))()
|
|
msg += ". L'ordre de passage et les compositions des différentes poules sont affiché⋅es sur le côté."
|
|
self.tournament.draw.last_message = msg
|
|
await sync_to_async(self.tournament.draw.save)()
|
|
|
|
await TeamDraw.objects.filter(round=self.tournament.draw.current_round).aupdate(last_dice=None)
|
|
for td in tds:
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None})
|
|
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice_visibility', 'visible': False})
|
|
|
|
async for td in pool.teamdraw_set.prefetch_related('participation__team').all():
|
|
await self.channel_layer.group_send(f"team-{td.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"tournament-{self.tournament.id}",
|
|
{'type': 'draw.send_poules',
|
|
'round': self.tournament.draw.current_round})
|
|
|
|
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})
|
|
elif state == 'DICE_ORDER_POULE' and \
|
|
not await TeamDraw.objects.filter(pool=self.tournament.draw.current_round.current_pool,
|
|
last_dice__isnull=True).aexists():
|
|
pool = self.tournament.draw.current_round.current_pool
|
|
|
|
tds = []
|
|
async for td in TeamDraw.objects.filter(pool=pool)\
|
|
.prefetch_related('participation__team'):
|
|
tds.append(td)
|
|
|
|
dices = {td: td.last_dice for td in tds}
|
|
values = list(dices.values())
|
|
error = False
|
|
for v in set(values):
|
|
if values.count(v) > 1:
|
|
dups = [td for td in tds if td.last_dice == v]
|
|
|
|
for dup in dups:
|
|
dup.last_dice = None
|
|
await sync_to_async(dup.save)()
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice', 'team': dup.participation.team.trigram, 'result': None})
|
|
await self.channel_layer.group_send(
|
|
f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.alert',
|
|
'message': _('Dices from teams {teams} are identical. Please relaunch your dices.').format(
|
|
teams=', '.join(td.participation.team.trigram for td in dups)),
|
|
'alert_type': 'warning'})
|
|
error = True
|
|
|
|
if error:
|
|
return
|
|
|
|
tds.sort(key=lambda x: -x.last_dice)
|
|
for i, td in enumerate(tds):
|
|
td.choose_index = i
|
|
await sync_to_async(td.save)()
|
|
|
|
pool.current_team = tds[0]
|
|
await sync_to_async(pool.save)()
|
|
|
|
self.tournament.draw.last_message = ""
|
|
await sync_to_async(self.tournament.draw.save)()
|
|
|
|
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})
|
|
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice_visibility', 'visible': False})
|
|
|
|
trigram = await sync_to_async(lambda: pool.current_team.participation.team.trigram)()
|
|
await self.channel_layer.group_send(f"team-{trigram}",
|
|
{'type': 'draw.box_visibility', 'visible': True})
|
|
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
|
|
{'type': 'draw.box_visibility', 'visible': True})
|
|
|
|
async def select_problem(self, **kwargs):
|
|
state = await sync_to_async(self.tournament.draw.get_state)()
|
|
|
|
if state != 'WAITING_DRAW_PROBLEM':
|
|
return await self.alert(_("This is not the time for this."), 'danger')
|
|
|
|
pool = await sync_to_async(lambda: self.tournament.draw.current_round.current_pool)()
|
|
td = await sync_to_async(lambda: pool.current_team)()
|
|
if not self.registration.is_volunteer:
|
|
participation = await Participation.objects.filter(team__participants=self.registration)\
|
|
.prefetch_related('team').aget()
|
|
if participation.id != td.participation_id:
|
|
return await self.alert("This is not your turn.", 'danger')
|
|
|
|
while True:
|
|
problem = randint(1, settings.PROBLEM_COUNT)
|
|
if await pool.teamdraw_set.filter(accepted=problem).acount() < (2 if pool.size == 5 else 1):
|
|
break
|
|
|
|
td.purposed = problem
|
|
await sync_to_async(td.save)()
|
|
|
|
trigram = await sync_to_async(lambda: td.participation.team.trigram)()
|
|
await self.channel_layer.group_send(f"team-{trigram}",
|
|
{'type': 'draw.box_visibility', 'visible': False})
|
|
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
|
|
{'type': 'draw.box_visibility', 'visible': False})
|
|
await self.channel_layer.group_send(f"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"team-{self.tournament.id}",
|
|
{'type': 'draw.draw_problem', 'team': trigram, 'problem': problem})
|
|
|
|
self.tournament.draw.last_message = ""
|
|
await sync_to_async(self.tournament.draw.save)()
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.set_info', 'draw': self.tournament.draw})
|
|
|
|
async def accept_problem(self, **kwargs):
|
|
state = await sync_to_async(self.tournament.draw.get_state)()
|
|
|
|
if state != 'WAITING_CHOOSE_PROBLEM':
|
|
return await self.alert(_("This is not the time for this."), 'danger')
|
|
|
|
r = await sync_to_async(lambda: self.tournament.draw.current_round)()
|
|
pool = await sync_to_async(lambda: r.current_pool)()
|
|
td = await sync_to_async(lambda: pool.current_team)()
|
|
if not self.registration.is_volunteer:
|
|
participation = await Participation.objects.filter(team__participants=self.registration)\
|
|
.prefetch_related('team').aget()
|
|
if participation.id != td.participation_id:
|
|
return await self.alert("This is not your turn.", 'danger')
|
|
|
|
td.accepted = td.purposed
|
|
td.purposed = None
|
|
await sync_to_async(td.save)()
|
|
|
|
trigram = await sync_to_async(lambda: td.participation.team.trigram)()
|
|
msg = f"L'équipe <strong>{trigram}</strong> a accepté le problème <strong>{td.accepted}</strong>. "
|
|
if pool.size == 5 and await pool.teamdraw_set.filter(accepted=td.accepted).acount() < 2:
|
|
msg += "Une équipe peut encore l'accepter."
|
|
else:
|
|
msg += "Plus personne ne peut l'accepter."
|
|
self.tournament.draw.last_message = msg
|
|
await sync_to_async(self.tournament.draw.save)()
|
|
|
|
await self.channel_layer.group_send(f"team-{trigram}",
|
|
{'type': 'draw.buttons_visibility', 'visible': False})
|
|
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
|
|
{'type': 'draw.buttons_visibility', 'visible': False})
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.set_problem',
|
|
'round': r.number,
|
|
'team': trigram,
|
|
'problem': td.accepted})
|
|
|
|
if await pool.teamdraw_set.filter(accepted__isnull=True).aexists():
|
|
# Continue
|
|
next_td = await pool.next_td()
|
|
pool.current_team = next_td
|
|
await sync_to_async(pool.save)()
|
|
|
|
new_trigram = await sync_to_async(lambda: next_td.participation.team.trigram)()
|
|
await self.channel_layer.group_send(f"team-{new_trigram}",
|
|
{'type': 'draw.box_visibility', 'visible': True})
|
|
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
|
|
{'type': 'draw.box_visibility', 'visible': True})
|
|
else:
|
|
# Pool is ended
|
|
if pool.size == 5:
|
|
# Maybe reorder teams if the same problem is presented twice
|
|
problems = OrderedDict()
|
|
async for td in pool.team_draws:
|
|
problems.setdefault(td.accepted, [])
|
|
problems[td.accepted].append(td)
|
|
p_index = 0
|
|
for pb, tds in problems.items():
|
|
if len(tds) == 2:
|
|
# Le règlement demande à ce que l'ordre soit tiré au sort
|
|
shuffle(tds)
|
|
tds[0].passage_index = p_index
|
|
tds[1].passage_index = p_index + 1
|
|
p_index += 2
|
|
await sync_to_async(tds[0].save)()
|
|
await sync_to_async(tds[1].save)()
|
|
for pb, tds in problems.items():
|
|
if len(tds) == 1:
|
|
tds[0].passage_index = p_index
|
|
p_index += 1
|
|
await sync_to_async(tds[0].save)()
|
|
|
|
print(p_index)
|
|
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {
|
|
'type': 'draw.reorder_pool',
|
|
'round': r.number,
|
|
'pool': pool.get_letter_display(),
|
|
'teams': [td.participation.team.trigram
|
|
async for td in pool.team_draws.prefetch_related('participation__team')],
|
|
'problems': [td.accepted async for td in pool.team_draws],
|
|
})
|
|
|
|
msg += f"<br><br>Le tirage de la poule {pool.get_letter_display()}{r.number} est terminé. " \
|
|
f"Le tableau récapitulatif est en bas."
|
|
self.tournament.draw.last_message = msg
|
|
await sync_to_async(self.tournament.draw.save)()
|
|
if await r.teamdraw_set.filter(accepted__isnull=True).aexists():
|
|
# Next pool
|
|
next_pool = await r.next_pool()
|
|
r.current_pool = next_pool
|
|
await sync_to_async(r.save)()
|
|
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.dice_visibility', 'visible': True})
|
|
else:
|
|
# Round is ended
|
|
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
|
|
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)()
|
|
|
|
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})
|
|
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}",
|
|
{'type': 'draw.export_visibility', 'visible': True})
|
|
|
|
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 reject_problem(self, **kwargs):
|
|
state = await sync_to_async(self.tournament.draw.get_state)()
|
|
|
|
if state != 'WAITING_CHOOSE_PROBLEM':
|
|
return await self.alert(_("This is not the time for this."), 'danger')
|
|
|
|
r = await sync_to_async(lambda: self.tournament.draw.current_round)()
|
|
pool = await sync_to_async(lambda: r.current_pool)()
|
|
td = await sync_to_async(lambda: pool.current_team)()
|
|
if not self.registration.is_volunteer:
|
|
participation = await Participation.objects.filter(team__participants=self.registration)\
|
|
.prefetch_related('team').aget()
|
|
if participation.id != td.participation_id:
|
|
return await self.alert("This is not your turn.", 'danger')
|
|
|
|
problem = td.purposed
|
|
already_refused = problem in td.rejected
|
|
if not already_refused:
|
|
td.rejected.append(problem)
|
|
td.purposed = None
|
|
await sync_to_async(td.save)()
|
|
|
|
remaining = settings.PROBLEM_COUNT - 5 - len(td.rejected)
|
|
|
|
trigram = await sync_to_async(lambda: td.participation.team.trigram)()
|
|
msg = f"L'équipe <strong>{trigram}</strong> a refusé le problème <strong>{problem}</strong>. "
|
|
if remaining >= 0:
|
|
msg += f"Il lui reste {remaining} refus sans pénalité."
|
|
else:
|
|
if already_refused:
|
|
msg += "Cela n'ajoute pas de pénalité."
|
|
else:
|
|
msg += "Cela ajoute une pénalité de 0.5 sur le coefficient de l'oral de læ défenseur⋅se."
|
|
self.tournament.draw.last_message = msg
|
|
await sync_to_async(self.tournament.draw.save)()
|
|
|
|
await self.channel_layer.group_send(f"team-{trigram}",
|
|
{'type': 'draw.buttons_visibility', 'visible': False})
|
|
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
|
|
{'type': 'draw.buttons_visibility', 'visible': False})
|
|
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
|
{'type': 'draw.reject_problem',
|
|
'round': r.number, 'team': trigram, 'rejected': td.rejected})
|
|
|
|
if already_refused:
|
|
next_td = td
|
|
else:
|
|
next_td = await pool.next_td()
|
|
|
|
pool.current_team = next_td
|
|
await sync_to_async(pool.save)()
|
|
|
|
new_trigram = await sync_to_async(lambda: next_td.participation.team.trigram)()
|
|
await self.channel_layer.group_send(f"team-{new_trigram}",
|
|
{'type': 'draw.box_visibility', 'visible': True})
|
|
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
|
|
{'type': 'draw.box_visibility', 'visible': True})
|
|
|
|
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 export(self, **kwargs):
|
|
async for r in self.tournament.draw.round_set.all():
|
|
async for pool in r.pool_set.all():
|
|
if await sync_to_async(lambda: pool.exportable)():
|
|
await sync_to_async(pool.export)()
|
|
|
|
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)
|
|
|
|
async def draw_notify(self, content):
|
|
await self.send_json({'type': 'notification', 'title': content['title'], 'body': content['body']})
|
|
|
|
async def draw_set_info(self, content):
|
|
await self.send_json({'type': 'set_info', 'information': await content['draw'].ainformation()})
|
|
|
|
async def draw_dice(self, content):
|
|
await self.send_json({'type': 'dice', 'team': content['team'], 'result': content['result']})
|
|
|
|
async def draw_dice_visibility(self, content):
|
|
await self.send_json({'type': 'dice_visibility', 'visible': content['visible']})
|
|
|
|
async def draw_box_visibility(self, content):
|
|
await self.send_json({'type': 'box_visibility', 'visible': content['visible']})
|
|
|
|
async def draw_buttons_visibility(self, content):
|
|
await self.send_json({'type': 'buttons_visibility', 'visible': content['visible']})
|
|
|
|
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()}
|
|
async for pool in content['round'].pool_set.order_by('letter').all()]})
|
|
|
|
async def draw_set_active(self, content):
|
|
r = content['draw'].current_round
|
|
await self.send_json(
|
|
await sync_to_async(lambda: {
|
|
'type': 'set_active',
|
|
'round': r.number,
|
|
'poule': r.current_pool.get_letter_display() if r.current_pool else None,
|
|
'team': r.current_pool.current_team.participation.team.trigram \
|
|
if r.current_pool and r.current_pool.current_team else None,
|
|
})())
|
|
|
|
async def draw_set_problem(self, content):
|
|
await self.send_json({'type': 'set_problem', 'round': content['round'],
|
|
'team': content['team'], 'problem': content['problem']})
|
|
|
|
async def draw_reject_problem(self, content):
|
|
await self.send_json({'type': 'reject_problem', 'round': content['round'],
|
|
'team': content['team'], 'rejected': content['rejected']})
|
|
|
|
async def draw_reorder_pool(self, content):
|
|
await self.send_json({'type': 'reorder_poule', 'round': content['round'],
|
|
'poule': content['pool'], 'teams': content['teams'],
|
|
'problems': content['problems']})
|