mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-25 01:00:32 +02:00
@ -1,4 +1,7 @@
|
||||
import json
|
||||
# Copyright (C) 2023 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from random import randint
|
||||
|
||||
from asgiref.sync import sync_to_async
|
||||
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
||||
@ -24,14 +27,16 @@ def ensure_orga(f):
|
||||
class DrawConsumer(AsyncJsonWebsocketConsumer):
|
||||
async def connect(self):
|
||||
tournament_id = self.scope['url_route']['kwargs']['tournament_id']
|
||||
self.tournament = await sync_to_async(Tournament.objects.get)(pk=tournament_id)
|
||||
self.tournament = await Tournament.objects.filter(pk=tournament_id)\
|
||||
.prefetch_related('draw__current_round__current_pool__current_team').aget()
|
||||
|
||||
self.participations = await sync_to_async(lambda: list(Participation.objects\
|
||||
.filter(tournament=self.tournament, valid=True)\
|
||||
.prefetch_related('team').all()))()
|
||||
self.participations = []
|
||||
async for participation in Participation.objects.filter(tournament=self.tournament, valid=True)\
|
||||
.prefetch_related('team'):
|
||||
self.participations.append(participation)
|
||||
|
||||
user = self.scope['user']
|
||||
reg = await sync_to_async(Registration.objects.get)(user=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:
|
||||
@ -41,11 +46,19 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
||||
|
||||
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'):
|
||||
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):
|
||||
@ -54,6 +67,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
||||
match content['type']:
|
||||
case 'start_draw':
|
||||
await self.start_draw(**content)
|
||||
case 'dice':
|
||||
await self.process_dice(**content)
|
||||
|
||||
@ensure_orga
|
||||
async def start_draw(self, fmt, **kwargs):
|
||||
@ -70,21 +85,204 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
||||
_("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 sync_to_async(Draw.objects.create)(tournament=self.tournament)
|
||||
draw = await Draw.objects.acreate(tournament=self.tournament)
|
||||
r1 = None
|
||||
for i in [1, 2]:
|
||||
r = await sync_to_async(Round.objects.create)(draw=draw, number=i)
|
||||
r = await Round.objects.acreate(draw=draw, number=i)
|
||||
if i == 1:
|
||||
r1 = r
|
||||
|
||||
for j, f in enumerate(fmt):
|
||||
await sync_to_async(Pool.objects.create)(round=r, letter=j + 1, size=f)
|
||||
await Pool.objects.acreate(round=r, letter=j + 1, size=f)
|
||||
for participation in self.participations:
|
||||
await sync_to_async(TeamDraw.objects.create)(participation=participation)
|
||||
await TeamDraw.objects.acreate(participation=participation, round=r)
|
||||
|
||||
draw.current_round = r1
|
||||
await sync_to_async(draw.save)()
|
||||
|
||||
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})
|
||||
|
||||
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]})
|
||||
|
||||
|
||||
async def process_dice(self, trigram: str | None = None, **kwargs):
|
||||
if self.registration.is_volunteer:
|
||||
participation = await Participation.objects.filter(team__trigram=trigram).prefetch_related('team').aget()
|
||||
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()
|
||||
|
||||
state = await sync_to_async(self.tournament.draw.get_state)()
|
||||
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, number=1).all():
|
||||
async for p2 in Pool.objects.filter(round_id=self.tournament.draw.current_round_id).all():
|
||||
if set(await p1.teamdraw_set.avalues('id')) \
|
||||
== set(await p2.teamdraw_set.avalues('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)()
|
||||
|
||||
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"tournament-{self.tournament.id}",
|
||||
{'type': 'draw.set_info', '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)
|
||||
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)()
|
||||
|
||||
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
|
||||
{'type': 'draw.set_info', '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']})
|
||||
|
Reference in New Issue
Block a user