import json from asgiref.sync import sync_to_async from channels.generic.websocket import AsyncJsonWebsocketConsumer 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): tournament_id = self.scope['url_route']['kwargs']['tournament_id'] self.tournament = await sync_to_async(Tournament.objects.get)(pk=tournament_id) self.participations = await sync_to_async(lambda: list(Participation.objects\ .filter(tournament=self.tournament, valid=True)\ .prefetch_related('team').all()))() user = self.scope['user'] reg = await sync_to_async(Registration.objects.get)(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) async def disconnect(self, close_code): await self.channel_layer.group_discard(f"tournament-{self.tournament.id}", self.channel_name) async def alert(self, message: str, alert_type: str = 'info'): return await self.send_json({'type': 'alert', 'alert_type': alert_type, 'message': str(message)}) async def receive_json(self, content, **kwargs): print(content) match content['type']: case 'start_draw': await self.start_draw(**content) @ensure_orga async def start_draw(self, fmt, **kwargs): print(fmt, kwargs) try: fmt = list(map(int, fmt.split('+'))) except ValueError as e: return await self.alert(_("Invalid format"), 'danger') print(fmt, sum(fmt), len(self.participations)) 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 sync_to_async(Draw.objects.create)(tournament=self.tournament) r = await sync_to_async(Round.objects.create)(draw=draw, number=1) for i, f in enumerate(fmt): sync_to_async(Pool.objects.create)(round=r, letter=i + 1, size=f) for participation in self.participations: sync_to_async(TeamDraw.objects.create)(participation=participation) await self.alert(_("Draw started!"), 'success') await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {'type': 'draw.start', 'fmt': fmt, 'draw': draw, 'round': r}) 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]})