Use a unique socket for the drawing system

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2023-05-11 17:07:53 +02:00
parent 8c41684993
commit 0c80385958
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
4 changed files with 979 additions and 829 deletions

View File

@ -42,8 +42,60 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
We accept only if this is a user of a team of the associated tournament, or a volunteer We accept only if this is a user of a team of the associated tournament, or a volunteer
of the tournament. of the tournament.
""" """
# Get the tournament from the URL
self.tournament_id = self.scope['url_route']['kwargs']['tournament_id'] # Fetch the registration of the current user
user = self.scope['user']
reg = await Registration.objects.aget(user=user)
self.registration = reg
# Accept the connection
await self.accept()
# Register to channel layers to get updates
if self.registration.participates:
await self.channel_layer.group_add(f"team-{self.registration.team.trigram}", self.channel_name)
participation = reg.team.participation
if participation.valid:
await self.channel_layer.group_add(f"tournament-{participation.tournament.id}", self.channel_name)
else:
tids = [t.id async for t in Tournament.objects.all()] \
if reg.is_admin else [t.id for t in reg.interesting_tournaments]
for tid in tids:
await self.channel_layer.group_add(f"tournament-{tid}", self.channel_name)
await self.channel_layer.group_add(f"volunteer-{tid}", self.channel_name)
async def disconnect(self, close_code) -> None:
"""
Called when the websocket got disconnected, for any reason.
:param close_code: The error code.
"""
# Unregister from channel layers
if not self.registration.is_volunteer:
await self.channel_layer.group_discard(f"team-{self.registration.team.trigram}", self.channel_name)
participation = self.registration.team.participation
await self.channel_layer.group_discard(f"tournament-{participation.tournament.id}", self.channel_name)
else:
async for tournament in Tournament.objects.all():
await self.channel_layer.group_discard(f"tournament-{tournament.id}", self.channel_name)
await self.channel_layer.group_discard(f"volunteer-{tournament.id}", self.channel_name)
async def alert(self, message: str, alert_type: str = 'info', tid: int = -1, **kwargs):
"""
Send an alert message to the current user.
:param message: The body of the alert.
:param alert_type: The type of the alert, which is a bootstrap color (success, warning, info, danger,)
:param tid: The tournament id. Default to -1, the current tournament.
"""
tid = tid if tid > 0 else self.tournament_id
return await self.send_json({'tid': tid, 'type': 'alert', 'alert_type': alert_type, 'message': str(message)})
async def receive_json(self, content, **kwargs):
"""
Called when the client sends us some data, parsed as JSON.
:param content: The sent data, decoded from JSON text. Must content a `type` field.
"""
# Get the tournament from the message
self.tournament_id = content['tid']
self.tournament = await Tournament.objects.filter(pk=self.tournament_id) \ self.tournament = await Tournament.objects.filter(pk=self.tournament_id) \
.prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget() .prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget()
@ -52,51 +104,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'): async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'):
self.participations.append(participation) self.participations.append(participation)
# Fetch the registration of the current user
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
# Accept the connection
await self.accept()
# Register to channel layers to get updates
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) -> None:
"""
Called when the websocket got disconnected, for any reason.
:param close_code: The error code.
"""
# Unregister from channel layers
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):
"""
Send an alert message to the current user.
:param message: The body of the alert.
:param alert_type: The type of the alert, which is a bootstrap color (success, warning, info, danger,)
"""
return await self.send_json({'type': 'alert', 'alert_type': alert_type, 'message': str(message)})
async def receive_json(self, content, **kwargs):
"""
Called when the client sends us some data, parsed as JSON.
:param content: The sent data, decoded from JSON text. Must content a `type` field.
"""
# Refresh tournament # Refresh tournament
self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\ self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\
.prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget() .prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget()
@ -176,7 +183,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await TeamDraw.objects.acreate(participation=participation, round=r) await TeamDraw.objects.acreate(participation=participation, round=r)
# Send to clients the different pools # Send to clients the different pools
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {
'tid': self.tournament_id,
'type': 'draw.send_poules',
'round': r.number, 'round': r.number,
'poules': [ 'poules': [
{ {
@ -184,29 +193,32 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'teams': await pool.atrigrams(), 'teams': await pool.atrigrams(),
} }
async for pool in r.pool_set.order_by('letter').all() async for pool in r.pool_set.order_by('letter').all()
]}) ]
})
draw.current_round = r1 draw.current_round = r1
await draw.asave() await draw.asave()
# Make dice box visible # Make dice box visible
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
await self.alert(_("Draw started!"), 'success') await self.alert(_("Draw started!"), 'success')
# Update user interface # Update user interface
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.start', 'fmt': fmt, 'draw': draw}) {'tid': self.tournament_id, 'type': 'draw.start', 'fmt': fmt, 'draw': 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_info', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
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', 'round': 1}) {'tid': self.tournament_id, 'type': 'draw.set_active', 'round': 1})
# Send notification to everyone # Send notification to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²', {'tid': self.tournament_id, 'type': 'draw.notify',
'title': 'Tirage au sort du TFJM²',
'body': "Le tirage au sort du tournoi de " 'body': "Le tirage au sort du tournoi de "
f"{self.tournament.name} a commencé !"}) f"{self.tournament.name} a commencé !"})
@ -216,7 +228,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
""" """
await self.alert(_("The draw for the tournament {tournament} will start.") await self.alert(_("The draw for the tournament {tournament} will start.")
.format(tournament=self.tournament.name), 'warning') .format(tournament=self.tournament.name), 'warning')
await self.send_json({'type': 'draw_start', 'fmt': content['fmt'], await self.send_json({'tid': content['tid'], 'type': 'draw_start', 'fmt': content['fmt'],
'trigrams': [p.team.trigram for p in self.participations]}) 'trigrams': [p.team.trigram for p in self.participations]})
@ensure_orga @ensure_orga
@ -231,7 +243,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# All associated data will be deleted by cascade # All associated data will be deleted by cascade
await self.tournament.draw.adelete() await self.tournament.draw.adelete()
# Send information to all users # Send information to all users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {'type': 'draw_abort'}) await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw_abort'})
async def draw_abort(self, content) -> None: async def draw_abort(self, content) -> None:
""" """
@ -239,7 +252,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
""" """
await self.alert(_("The draw for the tournament {tournament} is aborted.") await self.alert(_("The draw for the tournament {tournament} is aborted.")
.format(tournament=self.tournament.name), 'danger') .format(tournament=self.tournament.name), 'danger')
await self.send_json({'type': 'abort'}) await self.send_json({'tid': content['tid'], 'type': 'abort'})
async def process_dice(self, trigram: str | None = None, **kwargs): async def process_dice(self, trigram: str | None = None, **kwargs):
""" """
@ -310,7 +323,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send the dice result to all users # Send the dice result to all users
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', 'team': trigram, 'result': res}) f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': trigram, 'result': res})
if state == 'DICE_SELECT_POULES' and \ if state == 'DICE_SELECT_POULES' and \
not await TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id, not await TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id,
@ -364,19 +378,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
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}",
{'type': 'draw.dice', 'team': dup.participation.team.trigram, 'result': None}) {'tid': self.tournament_id, 'type': 'draw.dice',
'team': dup.participation.team.trigram, 'result': None})
# Send notification to concerned teams # Send notification to concerned teams
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"team-{dup.participation.team.trigram}", f"team-{dup.participation.team.trigram}",
{'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²', {'tid': self.tournament_id, 'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²',
'body': 'Votre score de dé est identique à celui de une ou plusieurs équipes. ' 'body': 'Votre score de dé est identique à celui de une ou plusieurs équipes. '
'Veuillez le relancer.'} 'Veuillez le relancer.'}
) )
# Alert the tournament # Alert the tournament
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", f"tournament-{self.tournament.id}",
{'type': 'draw.alert', {'tid': self.tournament_id, 'type': 'draw.alert',
'message': _('Dices from teams {teams} are identical. Please relaunch your dices.').format( 'message': _('Dices from teams {teams} are identical. Please relaunch your dices.').format(
teams=', '.join(td.participation.team.trigram for td in dups)), teams=', '.join(td.participation.team.trigram for td in dups)),
'alert_type': 'warning'}) 'alert_type': 'warning'})
@ -452,23 +467,26 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
for td in tds: for td in tds:
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", f"tournament-{self.tournament.id}",
{'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None}) {'tid': self.tournament_id, 'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None})
# Hide dice interface # Hide dice interface
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
# Display dice interface only for the teams in the first pool, and for volunteers # Display dice interface only for the teams in the first pool, and for volunteers
async for td in pool.teamdraw_set.prefetch_related('participation__team').all(): async for td in pool.teamdraw_set.prefetch_related('participation__team').all():
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.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
# First send the second pool to have the good team order # First send the second pool to have the good team order
r2 = await self.tournament.draw.round_set.filter(number=2).aget() r2 = await self.tournament.draw.round_set.filter(number=2).aget()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'tid': self.tournament_id, 'type': 'draw.send_poules',
'round': r2.number, 'round': r2.number,
'poules': [ 'poules': [
{ {
@ -478,7 +496,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for pool in r2.pool_set.order_by('letter').all() async for pool in r2.pool_set.order_by('letter').all()
]}) ]})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'tid': self.tournament_id, 'type': 'draw.send_poules',
'round': r.number, 'round': r.number,
'poules': [ 'poules': [
{ {
@ -490,10 +508,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update information header and the active team on the recap menu # Update information header and the active team on the recap menu
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
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', {'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r.number, 'round': r.number,
'pool': pool.get_letter_display()}) 'pool': pool.get_letter_display()})
@ -521,28 +539,30 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update information header # Update information header
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
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', {'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r.number, 'round': r.number,
'pool': pool.get_letter_display(), 'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram}) 'team': pool.current_team.participation.team.trigram})
# Hide dice button to everyone # Hide dice button to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
# Display the box button to the first team and to volunteers # Display the box button to the first team and to volunteers
trigram = pool.current_team.participation.team.trigram trigram = pool.current_team.participation.team.trigram
await self.channel_layer.group_send(f"team-{trigram}", await self.channel_layer.group_send(f"team-{trigram}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
# Notify the team that it can draw a problem # Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{tds[0].participation.team.trigram}", await self.channel_layer.group_send(f"team-{tds[0].participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !", {'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"}) 'body': "C'est à vous de tirer un nouveau problème !"})
async def select_problem(self, **kwargs): async def select_problem(self, **kwargs):
@ -585,20 +605,25 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update interface # Update interface
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
await self.channel_layer.group_send(f"team-{trigram}", await self.channel_layer.group_send(f"team-{trigram}",
{'type': 'draw.box_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': False})
await self.channel_layer.group_send(f"team-{trigram}", await self.channel_layer.group_send(f"team-{trigram}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"team-{self.tournament.id}", await self.channel_layer.group_send(f"team-{self.tournament.id}",
{'type': 'draw.draw_problem', 'team': trigram, 'problem': problem}) {'tid': self.tournament_id, 'type': 'draw.draw_problem', 'team': trigram,
'problem': problem})
self.tournament.draw.last_message = "" self.tournament.draw.last_message = ""
await self.tournament.draw.asave() await self.tournament.draw.asave()
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
async def accept_problem(self, **kwargs): async def accept_problem(self, **kwargs):
@ -641,11 +666,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send the accepted problem to the users # Send the accepted problem to the users
await self.channel_layer.group_send(f"team-{trigram}", await self.channel_layer.group_send(f"team-{trigram}",
{'type': 'draw.buttons_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
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_problem', {'tid': self.tournament_id, 'type': 'draw.set_problem',
'round': r.number, 'round': r.number,
'team': trigram, 'team': trigram,
'problem': td.accepted}) 'problem': td.accepted})
@ -659,13 +686,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
new_trigram = next_td.participation.team.trigram new_trigram = next_td.participation.team.trigram
await self.channel_layer.group_send(f"team-{new_trigram}", await self.channel_layer.group_send(f"team-{new_trigram}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': True})
# Notify the team that it can draw a problem # Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}", await self.channel_layer.group_send(f"team-{new_trigram}",
{'type': 'draw.notify', 'title': "À votre tour !", {'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"}) 'body': "C'est à vous de tirer un nouveau problème !"})
else: else:
# Pool is ended # Pool is ended
@ -674,10 +704,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
pool = r.current_pool pool = r.current_pool
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
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', {'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r.number, 'round': r.number,
'pool': pool.get_letter_display(), 'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram 'team': pool.current_team.participation.team.trigram
@ -715,6 +745,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send the reordered pool # Send the reordered pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", { await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {
'tid': self.tournament_id,
'type': 'draw.reorder_pool', 'type': 'draw.reorder_pool',
'round': r.number, 'round': r.number,
'pool': pool.get_letter_display(), 'pool': pool.get_letter_display(),
@ -736,14 +767,17 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in next_pool.team_draws.prefetch_related('participation__team').all(): async for td in next_pool.team_draws.prefetch_related('participation__team').all():
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.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
# Notify the team that it can draw a dice # Notify the team that it can draw a dice
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.notify', 'title': "À votre tour !", {'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"}) 'body': "C'est à vous de lancer le dé !"})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
else: else:
# Round is ended # Round is ended
await self.end_round(r) await self.end_round(r)
@ -766,16 +800,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
for participation in self.participations: for participation in self.participations:
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", f"tournament-{self.tournament.id}",
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None}) {'tid': self.tournament_id, 'type': 'draw.dice',
'team': participation.team.trigram, 'result': None})
# Notify the team that it can draw a dice # Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{participation.team.trigram}", await self.channel_layer.group_send(f"team-{participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !", {'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"}) 'body': "C'est à vous de lancer le dé !"})
# Reorder dices # Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'tid': self.tournament_id, 'type': 'draw.send_poules',
'round': r2.number, 'round': r2.number,
'poules': [ 'poules': [
{ {
@ -793,9 +829,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in p1.teamdraw_set.prefetch_related('participation__team').all(): async for td in p1.teamdraw_set.prefetch_related('participation__team').all():
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.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
elif r.number == 1 and self.tournament.final: elif r.number == 1 and self.tournament.final:
# For the final tournament, we wait for a manual update between the two rounds. # 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é." msg += "<br><br>Le tirage au sort du tour 1 est terminé."
@ -803,7 +841,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.tournament.draw.asave() await self.tournament.draw.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.export_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.export_visibility',
'visible': True})
async def reject_problem(self, **kwargs): async def reject_problem(self, **kwargs):
""" """
@ -854,11 +893,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update interface # Update interface
await self.channel_layer.group_send(f"team-{trigram}", await self.channel_layer.group_send(f"team-{trigram}",
{'type': 'draw.buttons_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.reject_problem', {'tid': self.tournament_id, 'type': 'draw.reject_problem',
'round': r.number, 'team': trigram, 'rejected': td.rejected}) 'round': r.number, 'team': trigram, 'rejected': td.rejected})
if already_refused: if already_refused:
@ -873,22 +914,23 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
new_trigram = next_td.participation.team.trigram new_trigram = next_td.participation.team.trigram
await self.channel_layer.group_send(f"team-{new_trigram}", await self.channel_layer.group_send(f"team-{new_trigram}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
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', {'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r.number, 'round': r.number,
'pool': pool.get_letter_display(), 'pool': pool.get_letter_display(),
'team': new_trigram}) 'team': new_trigram})
# Notify the team that it can draw a problem # Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}", await self.channel_layer.group_send(f"team-{new_trigram}",
{'type': 'draw.notify', 'title': "À votre tour !", {'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"}) 'body': "C'est à vous de tirer un nouveau problème !"})
@ensure_orga @ensure_orga
@ -906,7 +948,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await pool.export() await pool.export()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.export_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.export_visibility',
'visible': False})
@ensure_orga @ensure_orga
async def continue_final(self, **kwargs): async def continue_final(self, **kwargs):
@ -951,7 +994,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send pools to users # Send pools to users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'tid': self.tournament_id, 'type': 'draw.send_poules',
'round': r2.number, 'round': r2.number,
'poules': [ 'poules': [
{ {
@ -965,27 +1008,31 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
for participation in self.participations: for participation in self.participations:
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", f"tournament-{self.tournament.id}",
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None}) {'tid': self.tournament_id, 'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
async for td in r2.current_pool.team_draws.prefetch_related('participation__team'): async for td in r2.current_pool.team_draws.prefetch_related('participation__team'):
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.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
# Notify the team that it can draw a problem # Notify the team that it can draw a problem
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.notify', 'title': "À votre tour !", {'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"}) 'body': "C'est à vous de tirer un nouveau problème !"})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
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}) {'tid': self.tournament_id, 'type': 'draw.continue_visibility',
'visible': False})
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
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', {'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r2.number, 'round': r2.number,
'pool': r2.current_pool.get_letter_display()}) 'pool': r2.current_pool.get_letter_display()})
@ -1014,12 +1061,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.undo_order_dice() await self.undo_order_dice()
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', {'tid': self.tournament_id, 'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()}) 'info': await self.tournament.draw.ainformation()})
r = self.tournament.draw.current_round r = self.tournament.draw.current_round
p = r.current_pool p = r.current_pool
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', {'tid': self.tournament_id, 'type': 'draw.set_active',
'round': r.number, 'round': r.number,
'pool': p.get_letter_display() if p else None, 'pool': p.get_letter_display() if p else None,
'team': p.current_team.participation.team.trigram 'team': p.current_team.participation.team.trigram
@ -1037,15 +1084,18 @@ 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}) {'tid': self.tournament_id, '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}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
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_problem', {'tid': self.tournament_id, 'type': 'draw.set_problem',
'round': r.number, 'round': r.number,
'team': td.participation.team.trigram, 'team': td.participation.team.trigram,
'problem': td.accepted}) 'problem': td.accepted})
@ -1061,13 +1111,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave() await td.asave()
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': False}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.buttons_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.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
async def undo_process_problem(self): async def undo_process_problem(self):
""" """
@ -1115,7 +1167,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await last_td.asave() await last_td.asave()
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_problem', {'tid': self.tournament_id, 'type': 'draw.set_problem',
'round': r.number, 'round': r.number,
'team': last_td.participation.team.trigram, 'team': last_td.participation.team.trigram,
'problem': last_td.accepted}) 'problem': last_td.accepted})
@ -1133,7 +1185,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await last_td.asave() await last_td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.reject_problem', {'tid': self.tournament_id, 'type': 'draw.reject_problem',
'round': r.number, 'round': r.number,
'team': last_td.participation.team.trigram, 'team': last_td.participation.team.trigram,
'rejected': last_td.rejected}) 'rejected': last_td.rejected})
@ -1143,9 +1195,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await r.current_pool.asave() await r.current_pool.asave()
await self.channel_layer.group_send(f"team-{last_td.participation.team.trigram}", await self.channel_layer.group_send(f"team-{last_td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
else: else:
# 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')}
@ -1166,7 +1220,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface # Reset the dice on the interface
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': last_td.participation.team.trigram, 'team': last_td.participation.team.trigram,
'result': None}) 'result': None})
break break
@ -1177,12 +1231,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Make dice box visible # Make dice box visible
for td in pool_tds.values(): for td in pool_tds.values():
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.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
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}) {'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': False})
async def undo_pool_dice(self): async def undo_pool_dice(self):
""" """
@ -1217,7 +1274,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface # Reset the dice on the interface
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': last_td.participation.team.trigram, 'team': last_td.participation.team.trigram,
'result': None}) 'result': None})
break break
@ -1236,15 +1293,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave() await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.dice_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}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
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_problem', {'tid': self.tournament_id, 'type': 'draw.set_problem',
'round': r.number, 'round': r.number,
'team': td.participation.team.trigram, 'team': td.participation.team.trigram,
'problem': td.accepted}) 'problem': td.accepted})
@ -1258,12 +1318,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in r1.team_draws.prefetch_related('participation__team').all(): async for td in r1.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': td.participation.team.trigram, 'team': td.participation.team.trigram,
'result': td.choice_dice}) 'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'tid': self.tournament_id, 'type': 'draw.send_poules',
'round': r1.number, 'round': r1.number,
'poules': [ 'poules': [
{ {
@ -1281,15 +1341,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave() await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.dice_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}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
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_problem', {'tid': self.tournament_id, 'type': 'draw.set_problem',
'round': r1.number, 'round': r1.number,
'team': td.participation.team.trigram, 'team': td.participation.team.trigram,
'problem': td.accepted}) 'problem': td.accepted})
@ -1308,14 +1371,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in r1.team_draws.prefetch_related('participation__team').all(): async for td in r1.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': td.participation.team.trigram, 'team': td.participation.team.trigram,
'result': td.choice_dice}) 'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
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': True}) {'tid': self.tournament_id, 'type': 'draw.continue_visibility',
'visible': True})
else: else:
# Go to the dice order # Go to the dice order
async for r0 in self.tournament.draw.round_set.all(): async for r0 in self.tournament.draw.round_set.all():
@ -1349,19 +1414,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface # Reset the dice on the interface
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': last_td.participation.team.trigram, 'team': last_td.participation.team.trigram,
'result': None}) 'result': None})
break break
async for td in r.team_draws.prefetch_related('participation__team').all(): async for td in r.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': td.participation.team.trigram, 'team': td.participation.team.trigram,
'result': td.passage_dice}) 'result': td.passage_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True}) {'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
async def undo_order_dice(self): async def undo_order_dice(self):
""" """
@ -1393,7 +1459,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface # Reset the dice on the interface
await self.channel_layer.group_send( await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice', f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': last_td.participation.team.trigram, 'team': last_td.participation.team.trigram,
'result': None}) 'result': None})
break break
@ -1410,55 +1476,57 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
""" """
Send a notification (with title and body) to the current user. Send a notification (with title and body) to the current user.
""" """
await self.send_json({'type': 'notification', 'title': content['title'], 'body': content['body']}) await self.send_json({'tid': content['tid'], 'type': 'notification',
'title': content['title'], 'body': content['body']})
async def draw_set_info(self, content): async def draw_set_info(self, content):
""" """
Set the information banner to the current user. Set the information banner to the current user.
""" """
await self.send_json({'type': 'set_info', 'information': content['info']}) await self.send_json({'tid': content['tid'], 'type': 'set_info', 'information': content['info']})
async def draw_dice(self, content): async def draw_dice(self, content):
""" """
Update the dice of a given team for the current user interface. Update the dice of a given team for the current user interface.
""" """
await self.send_json({'type': 'dice', 'team': content['team'], 'result': content['result']}) await self.send_json({'tid': content['tid'], 'type': 'dice',
'team': content['team'], 'result': content['result']})
async def draw_dice_visibility(self, content): async def draw_dice_visibility(self, content):
""" """
Update the visibility of the dice button for the current user. Update the visibility of the dice button for the current user.
""" """
await self.send_json({'type': 'dice_visibility', 'visible': content['visible']}) await self.send_json({'tid': content['tid'], 'type': 'dice_visibility', 'visible': content['visible']})
async def draw_box_visibility(self, content): async def draw_box_visibility(self, content):
""" """
Update the visibility of the box button for the current user. Update the visibility of the box button for the current user.
""" """
await self.send_json({'type': 'box_visibility', 'visible': content['visible']}) await self.send_json({'tid': content['tid'], 'type': 'box_visibility', 'visible': content['visible']})
async def draw_buttons_visibility(self, content): async def draw_buttons_visibility(self, content):
""" """
Update the visibility of the accept/reject buttons for the current user. Update the visibility of the accept/reject buttons for the current user.
""" """
await self.send_json({'type': 'buttons_visibility', 'visible': content['visible']}) await self.send_json({'tid': content['tid'], 'type': 'buttons_visibility', 'visible': content['visible']})
async def draw_export_visibility(self, content): async def draw_export_visibility(self, content):
""" """
Update the visibility of the export button for the current user. Update the visibility of the export button for the current user.
""" """
await self.send_json({'type': 'export_visibility', 'visible': content['visible']}) await self.send_json({'tid': content['tid'], 'type': 'export_visibility', 'visible': content['visible']})
async def draw_continue_visibility(self, content): async def draw_continue_visibility(self, content):
""" """
Update the visibility of the continue button for the current user. Update the visibility of the continue button for the current user.
""" """
await self.send_json({'type': 'continue_visibility', 'visible': content['visible']}) await self.send_json({'tid': content['tid'], 'type': 'continue_visibility', 'visible': content['visible']})
async def draw_send_poules(self, content): async def draw_send_poules(self, content):
""" """
Send the pools and the teams to the current user to update the interface. Send the pools and the teams to the current user to update the interface.
""" """
await self.send_json({'type': 'set_poules', 'round': content['round'], await self.send_json({'tid': content['tid'], 'type': 'set_poules', 'round': content['round'],
'poules': content['poules']}) 'poules': content['poules']})
async def draw_set_active(self, content): async def draw_set_active(self, content):
@ -1466,6 +1534,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
Update the user interface to highlight the current team. Update the user interface to highlight the current team.
""" """
await self.send_json({ await self.send_json({
'tid': content['tid'],
'type': 'set_active', 'type': 'set_active',
'round': content.get('round', None), 'round': content.get('round', None),
'poule': content.get('pool', None), 'poule': content.get('pool', None),
@ -1476,20 +1545,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
""" """
Send the accepted problem of a team to the current user. Send the accepted problem of a team to the current user.
""" """
await self.send_json({'type': 'set_problem', 'round': content['round'], await self.send_json({'tid': content['tid'], 'type': 'set_problem', 'round': content['round'],
'team': content['team'], 'problem': content['problem']}) 'team': content['team'], 'problem': content['problem']})
async def draw_reject_problem(self, content): async def draw_reject_problem(self, content):
""" """
Send the rejected problems of a team to the current user. Send the rejected problems of a team to the current user.
""" """
await self.send_json({'type': 'reject_problem', 'round': content['round'], await self.send_json({'tid': content['tid'], 'type': 'reject_problem', 'round': content['round'],
'team': content['team'], 'rejected': content['rejected']}) 'team': content['team'], 'rejected': content['rejected']})
async def draw_reorder_pool(self, content): async def draw_reorder_pool(self, content):
""" """
Send the new order of a pool to the current user. Send the new order of a pool to the current user.
""" """
await self.send_json({'type': 'reorder_poule', 'round': content['round'], await self.send_json({'tid': content['tid'], 'type': 'reorder_poule', 'round': content['round'],
'poule': content['pool'], 'teams': content['teams'], 'poule': content['pool'], 'teams': content['teams'],
'problems': content['problems']}) 'problems': content['problems']})

View File

@ -1,7 +1,10 @@
# Copyright (C) 2023 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from django.urls import path from django.urls import path
from . import consumers from . import consumers
websocket_urlpatterns = [ websocket_urlpatterns = [
path("ws/draw/<int:tournament_id>/", consumers.DrawConsumer.as_asgi()), path("ws/draw/", consumers.DrawConsumer.as_asgi()),
] ]

View File

@ -7,7 +7,7 @@
const problems_count = JSON.parse(document.getElementById('problems_count').textContent) const problems_count = JSON.parse(document.getElementById('problems_count').textContent)
const tournaments = JSON.parse(document.getElementById('tournaments_list').textContent) const tournaments = JSON.parse(document.getElementById('tournaments_list').textContent)
const sockets = {} let socket = null
const messages = document.getElementById('messages') const messages = document.getElementById('messages')
@ -17,7 +17,7 @@ const messages = document.getElementById('messages')
* @param tid The tournament id * @param tid The tournament id
*/ */
function abortDraw(tid) { function abortDraw(tid) {
sockets[tid].send(JSON.stringify({'type': 'abort'})) socket.send(JSON.stringify({'tid': tid, 'type': 'abort'}))
} }
/** /**
@ -26,7 +26,7 @@ function abortDraw(tid) {
* @param tid The tournament id * @param tid The tournament id
*/ */
function cancelLastStep(tid) { function cancelLastStep(tid) {
sockets[tid].send(JSON.stringify({'type': 'cancel'})) socket.send(JSON.stringify({'tid': tid, 'type': 'cancel'}))
} }
/** /**
@ -36,7 +36,7 @@ function cancelLastStep(tid) {
* @param trigram The trigram of the team that a volunteer wants to force the dice launch (default: null) * @param trigram The trigram of the team that a volunteer wants to force the dice launch (default: null)
*/ */
function drawDice(tid, trigram = null) { function drawDice(tid, trigram = null) {
sockets[tid].send(JSON.stringify({'type': 'dice', 'trigram': trigram})) socket.send(JSON.stringify({'tid': tid, 'type': 'dice', 'trigram': trigram}))
} }
/** /**
@ -44,7 +44,7 @@ function drawDice(tid, trigram = null) {
* @param tid The tournament id * @param tid The tournament id
*/ */
function drawProblem(tid) { function drawProblem(tid) {
sockets[tid].send(JSON.stringify({'type': 'draw_problem'})) socket.send(JSON.stringify({'tid': tid, 'type': 'draw_problem'}))
} }
/** /**
@ -52,7 +52,7 @@ function drawProblem(tid) {
* @param tid The tournament id * @param tid The tournament id
*/ */
function acceptProblem(tid) { function acceptProblem(tid) {
sockets[tid].send(JSON.stringify({'type': 'accept'})) socket.send(JSON.stringify({'tid': tid, 'type': 'accept'}))
} }
/** /**
@ -60,7 +60,7 @@ function acceptProblem(tid) {
* @param tid The tournament id * @param tid The tournament id
*/ */
function rejectProblem(tid) { function rejectProblem(tid) {
sockets[tid].send(JSON.stringify({'type': 'reject'})) socket.send(JSON.stringify({'tid': tid, 'type': 'reject'}))
} }
/** /**
@ -68,7 +68,7 @@ function rejectProblem(tid) {
* @param tid The tournament id * @param tid The tournament id
*/ */
function exportDraw(tid) { function exportDraw(tid) {
sockets[tid].send(JSON.stringify({'type': 'export'})) socket.send(JSON.stringify({'tid': tid, 'type': 'export'}))
} }
/** /**
@ -76,7 +76,7 @@ function exportDraw(tid) {
* @param tid The tournament id * @param tid The tournament id
*/ */
function continueFinal(tid) { function continueFinal(tid) {
sockets[tid].send(JSON.stringify({'type': 'continue_final'})) socket.send(JSON.stringify({'tid': tid, 'type': 'continue_final'}))
} }
/** /**
@ -108,13 +108,11 @@ document.addEventListener('DOMContentLoaded', () => {
elem => elem.addEventListener( elem => elem.addEventListener(
'click', () => document.location.hash = '#' + elem.innerText.toLowerCase())) 'click', () => document.location.hash = '#' + elem.innerText.toLowerCase()))
for (let tournament of tournaments) { // Open a global websocket
// Open a websocket per tournament socket = new WebSocket(
let socket = new WebSocket(
(document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host (document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host
+ '/ws/draw/' + tournament.id + '/' + '/ws/draw/'
) )
sockets[tournament.id] = socket
/** /**
* Add alert message on the top on the interface. * Add alert message on the top on the interface.
@ -137,23 +135,25 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Update the information banner. * Update the information banner.
* @param tid The tournament id
* @param info The content to updated * @param info The content to updated
*/ */
function setInfo(info) { function setInfo(tid, info) {
document.getElementById(`messages-${tournament.id}`).innerHTML = info document.getElementById(`messages-${tid}`).innerHTML = info
} }
/** /**
* Open the draw interface, given the list of teams. * Open the draw interface, given the list of teams.
* @param tid The tournament id
* @param teams The list of teams (represented by their trigrams) that are present on this draw. * @param teams The list of teams (represented by their trigrams) that are present on this draw.
*/ */
function drawStart(teams) { function drawStart(tid, teams) {
// Hide the not-started-banner // Hide the not-started-banner
document.getElementById(`banner-not-started-${tournament.id}`).classList.add('d-none') document.getElementById(`banner-not-started-${tid}`).classList.add('d-none')
// Display the full draw interface // Display the full draw interface
document.getElementById(`draw-content-${tournament.id}`).classList.remove('d-none') document.getElementById(`draw-content-${tid}`).classList.remove('d-none')
let dicesDiv = document.getElementById(`dices-${tournament.id}`) let dicesDiv = document.getElementById(`dices-${tid}`)
for (let team of teams) { for (let team of teams) {
// Add empty dice score badge for each team // Add empty dice score badge for each team
let col = document.createElement('div') let col = document.createElement('div')
@ -161,11 +161,11 @@ document.addEventListener('DOMContentLoaded', () => {
dicesDiv.append(col) dicesDiv.append(col)
let diceDiv = document.createElement('div') let diceDiv = document.createElement('div')
diceDiv.id = `dice-${tournament.id}-${team}` diceDiv.id = `dice-${tid}-${team}`
diceDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning') diceDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning')
if (document.getElementById(`abort-${tournament.id}`) !== null) { if (document.getElementById(`abort-${tid}`) !== null) {
// Check if this is a volunteer, who can launch a dice for a specific team // Check if this is a volunteer, who can launch a die for a specific team
diceDiv.onclick = (e) => drawDice(tournament.id, team) diceDiv.onclick = (_) => drawDice(tid, team)
} }
diceDiv.textContent = `${team} 🎲 ??` diceDiv.textContent = `${team} 🎲 ??`
col.append(diceDiv) col.append(diceDiv)
@ -174,28 +174,30 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Abort the current draw, and make all invisible, except the not-started-banner. * Abort the current draw, and make all invisible, except the not-started-banner.
* @param tid The tournament id
*/ */
function drawAbort() { function drawAbort(tid) {
document.getElementById(`banner-not-started-${tournament.id}`).classList.remove('d-none') document.getElementById(`banner-not-started-${tid}`).classList.remove('d-none')
document.getElementById(`draw-content-${tournament.id}`).classList.add('d-none') document.getElementById(`draw-content-${tid}`).classList.add('d-none')
document.getElementById(`dices-${tournament.id}`).innerHTML = "" document.getElementById(`dices-${tid}`).innerHTML = ""
document.getElementById(`recap-${tournament.id}-round-list`).innerHTML = "" document.getElementById(`recap-${tid}-round-list`).innerHTML = ""
document.getElementById(`tables-${tournament.id}`).innerHTML = "" document.getElementById(`tables-${tid}`).innerHTML = ""
updateDiceVisibility(false) updateDiceVisibility(tid, false)
updateBoxVisibility(false) updateBoxVisibility(tid, false)
updateButtonsVisibility(false) updateButtonsVisibility(tid, false)
updateExportVisibility(false) updateExportVisibility(tid, false)
updateContinueVisibility(false) updateContinueVisibility(tid, false)
} }
/** /**
* This function is triggered after a new dice result. We update the score of the team. * This function is triggered after a new dice result. We update the score of the team.
* Can be resetted to empty values if the result is null. * Can be resetted to empty values if the result is null.
* @param tid The tournament id
* @param trigram The trigram of the team that launched its dice * @param trigram The trigram of the team that launched its dice
* @param result The result of the dice. null if it is a reset. * @param result The result of the dice. null if it is a reset.
*/ */
function updateDiceInfo(trigram, result) { function updateDiceInfo(tid, trigram, result) {
let elem = document.getElementById(`dice-${tournament.id}-${trigram}`) let elem = document.getElementById(`dice-${tid}-${trigram}`)
if (result === null) { if (result === null) {
elem.classList.remove('text-bg-success') elem.classList.remove('text-bg-success')
elem.classList.add('text-bg-warning') elem.classList.add('text-bg-warning')
@ -210,10 +212,11 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Display or hide the dice button. * Display or hide the dice button.
* @param tid The tournament id
* @param visible The visibility status * @param visible The visibility status
*/ */
function updateDiceVisibility(visible) { function updateDiceVisibility(tid, visible) {
let div = document.getElementById(`launch-dice-${tournament.id}`) let div = document.getElementById(`launch-dice-${tid}`)
if (visible) if (visible)
div.classList.remove('d-none') div.classList.remove('d-none')
else else
@ -222,10 +225,11 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Display or hide the box button. * Display or hide the box button.
* @param tid The tournament id
* @param visible The visibility status * @param visible The visibility status
*/ */
function updateBoxVisibility(visible) { function updateBoxVisibility(tid, visible) {
let div = document.getElementById(`draw-problem-${tournament.id}`) let div = document.getElementById(`draw-problem-${tid}`)
if (visible) if (visible)
div.classList.remove('d-none') div.classList.remove('d-none')
else else
@ -234,10 +238,11 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Display or hide the accept and reject buttons. * Display or hide the accept and reject buttons.
* @param tid The tournament id
* @param visible The visibility status * @param visible The visibility status
*/ */
function updateButtonsVisibility(visible) { function updateButtonsVisibility(tid, visible) {
let div = document.getElementById(`buttons-${tournament.id}`) let div = document.getElementById(`buttons-${tid}`)
if (visible) if (visible)
div.classList.remove('d-none') div.classList.remove('d-none')
else else
@ -246,10 +251,11 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Display or hide the export button. * Display or hide the export button.
* @param tid The tournament id
* @param visible The visibility status * @param visible The visibility status
*/ */
function updateExportVisibility(visible) { function updateExportVisibility(tid, visible) {
let div = document.getElementById(`export-${tournament.id}`) let div = document.getElementById(`export-${tid}`)
if (visible) if (visible)
div.classList.remove('d-none') div.classList.remove('d-none')
else else
@ -258,32 +264,37 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* Display or hide the continuation button. * Display or hide the continuation button.
* @param tid The tournament id
* @param visible The visibility status * @param visible The visibility status
*/ */
function updateContinueVisibility(visible) { function updateContinueVisibility(tid, visible) {
let div = document.getElementById(`continue-${tournament.id}`) let div = document.getElementById(`continue-${tid}`)
if (div !== null) {
// Only present during the final
if (visible) if (visible)
div.classList.remove('d-none') div.classList.remove('d-none')
else else
div.classList.add('d-none') div.classList.add('d-none')
} }
}
/** /**
* Set the different pools for the given round, and update the interface. * Set the different pools for the given round, and update the interface.
* @param tid The tournament id
* @param round The round number, as integer (1 or 2) * @param round The round number, as integer (1 or 2)
* @param poules The list of poules, which are represented with their letters and trigrams, * @param poules The list of poules, which are represented with their letters and trigrams,
* [{'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}] * [{'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}]
*/ */
function updatePoules(round, poules) { function updatePoules(tid, round, poules) {
let roundList = document.getElementById(`recap-${tournament.id}-round-list`) let roundList = document.getElementById(`recap-${tid}-round-list`)
let poolListId = `recap-${tournament.id}-round-${round}-pool-list` let poolListId = `recap-${tid}-round-${round}-pool-list`
let poolList = document.getElementById(poolListId) let poolList = document.getElementById(poolListId)
if (poolList === null) { if (poolList === null) {
// Add a div for the round in the recap div // Add a div for the round in the recap div
let div = document.createElement('div') let div = document.createElement('div')
div.id = `recap-${tournament.id}-round-${round}` div.id = `recap-${tid}-round-${round}`
div.classList.add('col-md-6', 'px-3', 'py-3') div.classList.add('col-md-6', 'px-3', 'py-3')
div.setAttribute('data-tournament', tournament.id) div.setAttribute('data-tournament', tid)
let title = document.createElement('strong') let title = document.createElement('strong')
title.textContent = 'Tour ' + round title.textContent = 'Tour ' + round
@ -299,14 +310,14 @@ document.addEventListener('DOMContentLoaded', () => {
let c = 1 let c = 1
for (let poule of poules) { for (let poule of poules) {
let teamListId = `recap-${tournament.id}-round-${round}-pool-${poule.letter}-team-list` let teamListId = `recap-${tid}-round-${round}-pool-${poule.letter}-team-list`
let teamList = document.getElementById(teamListId) let teamList = document.getElementById(teamListId)
if (teamList === null) { if (teamList === null) {
// Add a div for the pool in the recap div // Add a div for the pool in the recap div
let li = document.createElement('li') let li = document.createElement('li')
li.id = `recap-${tournament.id}-round-${round}-pool-${poule.letter}` li.id = `recap-${tid}-round-${round}-pool-${poule.letter}`
li.classList.add('list-group-item', 'px-3', 'py-3') li.classList.add('list-group-item', 'px-3', 'py-3')
li.setAttribute('data-tournament', tournament.id) li.setAttribute('data-tournament', tid)
let title = document.createElement('strong') let title = document.createElement('strong')
title.textContent = 'Poule ' + poule.letter + round title.textContent = 'Poule ' + poule.letter + round
@ -323,11 +334,11 @@ document.addEventListener('DOMContentLoaded', () => {
// The pool is initialized // The pool is initialized
for (let team of poule.teams) { for (let team of poule.teams) {
// Reorder dices // Reorder dices
let diceDiv = document.getElementById(`dice-${tournament.id}-${team}`) let diceDiv = document.getElementById(`dice-${tid}-${team}`)
diceDiv.parentElement.style.order = c.toString() diceDiv.parentElement.style.order = c.toString()
c += 1 c += 1
let teamLiId = `recap-${tournament.id}-round-${round}-team-${team}` let teamLiId = `recap-${tid}-round-${round}-team-${team}`
let teamLi = document.getElementById(teamLiId) let teamLi = document.getElementById(teamLiId)
if (teamLi === null) { if (teamLi === null) {
@ -335,13 +346,13 @@ document.addEventListener('DOMContentLoaded', () => {
teamLi = document.createElement('li') teamLi = document.createElement('li')
teamLi.id = teamLiId teamLi.id = teamLiId
teamLi.classList.add('list-group-item') teamLi.classList.add('list-group-item')
teamLi.setAttribute('data-tournament', tournament.id) teamLi.setAttribute('data-tournament', tid)
teamList.append(teamLi) teamList.append(teamLi)
} }
// Add the accepted problem div (empty for now) // Add the accepted problem div (empty for now)
let acceptedDivId = `recap-${tournament.id}-round-${round}-team-${team}-accepted` let acceptedDivId = `recap-${tid}-round-${round}-team-${team}-accepted`
let acceptedDiv = document.getElementById(acceptedDivId) let acceptedDiv = document.getElementById(acceptedDivId)
if (acceptedDiv === null) { if (acceptedDiv === null) {
acceptedDiv = document.createElement('div') acceptedDiv = document.createElement('div')
@ -352,7 +363,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
// Add the rejected problems div (empty for now) // Add the rejected problems div (empty for now)
let rejectedDivId = `recap-${tournament.id}-round-${round}-team-${team}-rejected` let rejectedDivId = `recap-${tid}-round-${round}-team-${team}-rejected`
let rejectedDiv = document.getElementById(rejectedDivId) let rejectedDiv = document.getElementById(rejectedDivId)
if (rejectedDiv === null) { if (rejectedDiv === null) {
rejectedDiv = document.createElement('div') rejectedDiv = document.createElement('div')
@ -365,8 +376,8 @@ document.addEventListener('DOMContentLoaded', () => {
} }
// Draw tables // Draw tables
let tablesDiv = document.getElementById(`tables-${tournament.id}`) let tablesDiv = document.getElementById(`tables-${tid}`)
let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`) let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`)
if (tablesRoundDiv === null) { if (tablesRoundDiv === null) {
// Add the tables div for the current round if necessary // Add the tables div for the current round if necessary
let card = document.createElement('div') let card = document.createElement('div')
@ -379,7 +390,7 @@ document.addEventListener('DOMContentLoaded', () => {
card.append(cardHeader) card.append(cardHeader)
tablesRoundDiv = document.createElement('div') tablesRoundDiv = document.createElement('div')
tablesRoundDiv.id = `tables-${tournament.id}-round-${round}` tablesRoundDiv.id = `tables-${tid}-round-${round}`
tablesRoundDiv.classList.add('card-body', 'd-flex', 'flex-wrap') tablesRoundDiv.classList.add('card-body', 'd-flex', 'flex-wrap')
card.append(tablesRoundDiv) card.append(tablesRoundDiv)
} }
@ -389,20 +400,21 @@ document.addEventListener('DOMContentLoaded', () => {
continue continue
// Display the table for the pool // Display the table for the pool
updatePouleTable(round, poule) updatePouleTable(tid, round, poule)
} }
} }
} }
/** /**
* Update the table for the given round and the given pool, where there will be the chosen problems. * Update the table for the given round and the given pool, where there will be the chosen problems.
* @param tid The tournament id
* @param round The round number, as integer (1 or 2) * @param round The round number, as integer (1 or 2)
* @param poule The current pool, which id represented with its letter and trigrams, * @param poule The current pool, which id represented with its letter and trigrams,
* {'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']} * {'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}
*/ */
function updatePouleTable(round, poule) { function updatePouleTable(tid, round, poule) {
let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`) let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`)
let pouleTable = document.getElementById(`table-${tournament.id}-${round}-${poule.letter}`) let pouleTable = document.getElementById(`table-${tid}-${round}-${poule.letter}`)
if (pouleTable === null) { if (pouleTable === null) {
// Create table // Create table
let card = document.createElement('div') let card = document.createElement('div')
@ -419,7 +431,7 @@ document.addEventListener('DOMContentLoaded', () => {
card.append(cardBody) card.append(cardBody)
pouleTable = document.createElement('table') pouleTable = document.createElement('table')
pouleTable.id = `table-${tournament.id}-${round}-${poule.letter}` pouleTable.id = `table-${tid}-${round}-${poule.letter}`
pouleTable.classList.add('table', 'table-stripped') pouleTable.classList.add('table', 'table-stripped')
cardBody.append(pouleTable) cardBody.append(pouleTable)
@ -464,7 +476,7 @@ document.addEventListener('DOMContentLoaded', () => {
let problemTh = document.createElement('th') let problemTh = document.createElement('th')
problemTh.classList.add('text-center') problemTh.classList.add('text-center')
// Problem is unknown for now // Problem is unknown for now
problemTh.innerHTML = `Pb. <span id="table-${tournament.id}-round-${round}-problem-${team}">?</span>` problemTh.innerHTML = `Pb. <span id="table-${tid}-round-${round}-problem-${team}">?</span>`
problemTr.append(problemTh) problemTr.append(problemTh)
} }
@ -553,45 +565,47 @@ document.addEventListener('DOMContentLoaded', () => {
} }
/** /**
* Highligh the team that is currently choosing its problem. * Highlight the team that is currently choosing its problem.
* @param tid The tournament id
* @param round The current round number, as integer (1 or 2) * @param round The current round number, as integer (1 or 2)
* @param pool The current pool letter (A, B, C or D) (null if non-relevant) * @param pool The current pool letter (A, B, C or D) (null if non-relevant)
* @param team The current team trigram (null if non-relevant) * @param team The current team trigram (null if non-relevant)
*/ */
function updateActiveRecap(round, pool, team) { function updateActiveRecap(tid, round, pool, team) {
// Remove the previous highlights // Remove the previous highlights
document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tournament.id}"]`) document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tid}"]`)
.forEach(elem => elem.classList.remove('text-bg-secondary')) .forEach(elem => elem.classList.remove('text-bg-secondary'))
document.querySelectorAll(`li.list-group-item-success[data-tournament="${tournament.id}"]`) document.querySelectorAll(`li.list-group-item-success[data-tournament="${tid}"]`)
.forEach(elem => elem.classList.remove('list-group-item-success')) .forEach(elem => elem.classList.remove('list-group-item-success'))
document.querySelectorAll(`li.list-group-item-info[data-tournament="${tournament.id}"]`) document.querySelectorAll(`li.list-group-item-info[data-tournament="${tid}"]`)
.forEach(elem => elem.classList.remove('list-group-item-info')) .forEach(elem => elem.classList.remove('list-group-item-info'))
// Highlight current round, if existing // Highlight current round, if existing
let roundDiv = document.getElementById(`recap-${tournament.id}-round-${round}`) let roundDiv = document.getElementById(`recap-${tid}-round-${round}`)
if (roundDiv !== null) if (roundDiv !== null)
roundDiv.classList.add('text-bg-secondary') roundDiv.classList.add('text-bg-secondary')
// Highlight current pool, if existing // Highlight current pool, if existing
let poolLi = document.getElementById(`recap-${tournament.id}-round-${round}-pool-${pool}`) let poolLi = document.getElementById(`recap-${tid}-round-${round}-pool-${pool}`)
if (poolLi !== null) if (poolLi !== null)
poolLi.classList.add('list-group-item-success') poolLi.classList.add('list-group-item-success')
// Highlight current team, if existing // Highlight current team, if existing
let teamLi = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}`) let teamLi = document.getElementById(`recap-${tid}-round-${round}-team-${team}`)
if (teamLi !== null) if (teamLi !== null)
teamLi.classList.add('list-group-item-info') teamLi.classList.add('list-group-item-info')
} }
/** /**
* Update the recap and the table when a team accepts a problem. * Update the recap and the table when a team accepts a problem.
* @param tid The tournament id
* @param round The current round, as integer (1 or 2) * @param round The current round, as integer (1 or 2)
* @param team The current team trigram * @param team The current team trigram
* @param problem The accepted problem, as integer * @param problem The accepted problem, as integer
*/ */
function setProblemAccepted(round, team, problem) { function setProblemAccepted(tid, round, team, problem) {
// Update recap // Update recap
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-accepted`) let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-accepted`)
if (problem !== null) { if (problem !== null) {
recapDiv.classList.remove('text-bg-warning') recapDiv.classList.remove('text-bg-warning')
recapDiv.classList.add('text-bg-success') recapDiv.classList.add('text-bg-success')
@ -603,27 +617,28 @@ document.addEventListener('DOMContentLoaded', () => {
recapDiv.textContent = `${team} 📃 ${problem ? problem : '?'}` recapDiv.textContent = `${team} 📃 ${problem ? problem : '?'}`
// Update table // Update table
let tableSpan = document.getElementById(`table-${tournament.id}-round-${round}-problem-${team}`) let tableSpan = document.getElementById(`table-${tid}-round-${round}-problem-${team}`)
tableSpan.textContent = problem ? problem : '?' tableSpan.textContent = problem ? problem : '?'
} }
/** /**
* Update the recap when a team rejects a problem. * Update the recap when a team rejects a problem.
* @param tid The tournament id
* @param round The current round, as integer (1 or 2) * @param round The current round, as integer (1 or 2)
* @param team The current team trigram * @param team The current team trigram
* @param rejected The full list of rejected problems * @param rejected The full list of rejected problems
*/ */
function setProblemRejected(round, team, rejected) { function setProblemRejected(tid, round, team, rejected) {
// Update recap // Update recap
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-rejected`) let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-rejected`)
recapDiv.textContent = `🗑️ ${rejected.join(', ')}` recapDiv.textContent = `🗑️ ${rejected.join(', ')}`
let penaltyDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-penalty`) let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`)
if (rejected.length > problems_count - 5) { if (rejected.length > problems_count - 5) {
// If more than P - 5 problems were rejected, add a penalty of 0.5 of the coefficient of the oral defender // If more than P - 5 problems were rejected, add a penalty of 0.5 of the coefficient of the oral defender
if (penaltyDiv === null) { if (penaltyDiv === null) {
penaltyDiv = document.createElement('div') penaltyDiv = document.createElement('div')
penaltyDiv.id = `recap-${tournament.id}-round-${round}-team-${team}-penalty` penaltyDiv.id = `recap-${tid}-round-${round}-team-${team}-penalty`
penaltyDiv.classList.add('badge', 'rounded-pill', 'text-bg-info') penaltyDiv.classList.add('badge', 'rounded-pill', 'text-bg-info')
recapDiv.parentNode.append(penaltyDiv) recapDiv.parentNode.append(penaltyDiv)
} }
@ -639,32 +654,34 @@ document.addEventListener('DOMContentLoaded', () => {
/** /**
* For a 5-teams pool, we may reorder the pool if two teams select the same problem. * For a 5-teams pool, we may reorder the pool if two teams select the same problem.
* Then, we redraw the table and set the accepted problems. * Then, we redraw the table and set the accepted problems.
* @param tid The tournament id
* @param round The current round, as integer (1 or 2) * @param round The current round, as integer (1 or 2)
* @param poule The pool represented by its letter * @param poule The pool represented by its letter
* @param teams The teams list represented by their trigrams, ["ABC", "DEF", "GHI", "JKL", "MNO"] * @param teams The teams list represented by their trigrams, ["ABC", "DEF", "GHI", "JKL", "MNO"]
* @param problems The accepted problems in the same order than the teams, [1, 1, 2, 2, 3] * @param problems The accepted problems in the same order than the teams, [1, 1, 2, 2, 3]
*/ */
function reorderPoule(round, poule, teams, problems) { function reorderPoule(tid, round, poule, teams, problems) {
// Redraw the pool table // Redraw the pool table
let table = document.getElementById(`table-${tournament.id}-${round}-${poule}`) let table = document.getElementById(`table-${tid}-${round}-${poule}`)
table.parentElement.parentElement.remove() table.parentElement.parentElement.remove()
updatePouleTable(round, {'letter': poule, 'teams': teams}) updatePouleTable(tid, round, {'letter': poule, 'teams': teams})
// Put the problems in the table // Put the problems in the table
for (let i = 0; i < teams.length; ++i) { for (let i = 0; i < teams.length; ++i) {
let team = teams[i] let team = teams[i]
let problem = problems[i] let problem = problems[i]
setProblemAccepted(round, team, problem) setProblemAccepted(tid, round, team, problem)
} }
} }
// Listen on websockets and process messages from the server /**
socket.addEventListener('message', e => { * Process the received data from the server.
// Parse received data as JSON * @param tid The tournament id
const data = JSON.parse(e.data) * @param data The received message
*/
function processMessage(tid, data) {
switch (data.type) { switch (data.type) {
case 'alert': case 'alert':
// Add alert message // Add alert message
@ -676,61 +693,69 @@ document.addEventListener('DOMContentLoaded', () => {
break break
case 'set_info': case 'set_info':
// Update information banner // Update information banner
setInfo(data.information) setInfo(tid, data.information)
break break
case 'draw_start': case 'draw_start':
// Start the draw and update the interface // Start the draw and update the interface
drawStart(data.trigrams) drawStart(tid, data.trigrams)
break break
case 'abort': case 'abort':
// Abort the current draw // Abort the current draw
drawAbort() drawAbort(tid)
break break
case 'dice': case 'dice':
// Update the interface after a dice launch // Update the interface after a dice launch
updateDiceInfo(data.team, data.result) updateDiceInfo(tid, data.team, data.result)
break break
case 'dice_visibility': case 'dice_visibility':
// Update the dice button visibility // Update the dice button visibility
updateDiceVisibility(data.visible) updateDiceVisibility(tid, data.visible)
break break
case 'box_visibility': case 'box_visibility':
// Update the box button visibility // Update the box button visibility
updateBoxVisibility(data.visible) updateBoxVisibility(tid, data.visible)
break break
case 'buttons_visibility': case 'buttons_visibility':
// Update the accept/reject buttons visibility // Update the accept/reject buttons visibility
updateButtonsVisibility(data.visible) updateButtonsVisibility(tid, data.visible)
break break
case 'export_visibility': case 'export_visibility':
// Update the export button visibility // Update the export button visibility
updateExportVisibility(data.visible) updateExportVisibility(tid, data.visible)
break break
case 'continue_visibility': case 'continue_visibility':
// Update the continue button visibility for the final tournament // Update the continue button visibility for the final tournament
updateContinueVisibility(data.visible) updateContinueVisibility(tid, data.visible)
break break
case 'set_poules': case 'set_poules':
// Set teams order and pools and update the interface // Set teams order and pools and update the interface
updatePoules(data.round, data.poules) updatePoules(tid, data.round, data.poules)
break break
case 'set_active': case 'set_active':
// Highlight the team that is selecting a problem // Highlight the team that is selecting a problem
updateActiveRecap(data.round, data.poule, data.team) updateActiveRecap(tid, data.round, data.poule, data.team)
break break
case 'set_problem': case 'set_problem':
// Mark a problem as accepted and update the interface // Mark a problem as accepted and update the interface
setProblemAccepted(data.round, data.team, data.problem) setProblemAccepted(tid, data.round, data.team, data.problem)
break break
case 'reject_problem': case 'reject_problem':
// Mark a problem as rejected and update the interface // Mark a problem as rejected and update the interface
setProblemRejected(data.round, data.team, data.rejected) setProblemRejected(tid, data.round, data.team, data.rejected)
break break
case 'reorder_poule': case 'reorder_poule':
// Reorder a pool and redraw the associated table // Reorder a pool and redraw the associated table
reorderPoule(data.round, data.poule, data.teams, data.problems) reorderPoule(tid, data.round, data.poule, data.teams, data.problems)
break break
} }
}
// Listen on websockets and process messages from the server
socket.addEventListener('message', e => {
// Parse received data as JSON
const data = JSON.parse(e.data)
processMessage(data['tid'], data)
}) })
// Manage errors // Manage errors
@ -741,11 +766,13 @@ document.addEventListener('DOMContentLoaded', () => {
// When the socket is opened, set the language in order to receive alerts in the good language // When the socket is opened, set the language in order to receive alerts in the good language
socket.addEventListener('open', e => { socket.addEventListener('open', e => {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
'tid': tournaments[0].id,
'type': 'set_language', 'type': 'set_language',
'language': document.getElementsByName('language')[0].value, 'language': document.getElementsByName('language')[0].value,
})) }))
}) })
for (let tournament of tournaments) {
// Manage the start form // Manage the start form
let format_form = document.getElementById('format-form-' + tournament.id) let format_form = document.getElementById('format-form-' + tournament.id)
if (format_form !== null) { if (format_form !== null) {
@ -753,6 +780,7 @@ document.addEventListener('DOMContentLoaded', () => {
e.preventDefault() e.preventDefault()
socket.send(JSON.stringify({ socket.send(JSON.stringify({
'tid': tournament.id,
'type': 'start_draw', 'type': 'start_draw',
'fmt': document.getElementById('format-' + tournament.id).value 'fmt': document.getElementById('format-' + tournament.id).value
})) }))

View File

@ -1,6 +1,6 @@
# 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 asyncio
from random import shuffle from random import shuffle
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
@ -48,25 +48,26 @@ class TestDraw(TestCase):
""" """
await sync_to_async(self.async_client.force_login)(self.superuser) await sync_to_async(self.async_client.force_login)(self.superuser)
tid = self.tournament.id
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Connect to Websocket # Connect to Websocket
headers = [(b'cookie', self.async_client.cookies.output(header='', sep='; ').encode())] headers = [(b'cookie', self.async_client.cookies.output(header='', sep='; ').encode())]
communicator = WebsocketCommunicator(AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)), communicator = WebsocketCommunicator(AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)),
f"/ws/draw/{self.tournament.id}/", "/ws/draw/", headers)
headers)
connected, subprotocol = await communicator.connect() connected, subprotocol = await communicator.connect()
self.assertTrue(connected) self.assertTrue(connected)
# Define language # Define language
await communicator.send_json_to({'type': 'set_language', 'language': 'en'}) await communicator.send_json_to({'tid': tid, 'type': 'set_language', 'language': 'en'})
# Ensure that Draw has not started # Ensure that Draw has not started
self.assertFalse(await Draw.objects.filter(tournament=self.tournament).aexists()) self.assertFalse(await Draw.objects.filter(tournament=self.tournament).aexists())
# Must be an error since 1+1+1 != 12 # Must be an error since 1+1+1 != 12
await communicator.send_json_to({'type': 'start_draw', 'fmt': '1+1+1'}) await communicator.send_json_to({'tid': tid, 'type': 'start_draw', 'fmt': '1+1+1'})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], 'alert') self.assertEqual(resp['type'], 'alert')
self.assertEqual(resp['alert_type'], 'danger') self.assertEqual(resp['alert_type'], 'danger')
@ -74,29 +75,30 @@ class TestDraw(TestCase):
self.assertFalse(await Draw.objects.filter(tournament=self.tournament).aexists()) self.assertFalse(await Draw.objects.filter(tournament=self.tournament).aexists())
# Now start the draw # Now start the draw
await communicator.send_json_to({'type': 'start_draw', 'fmt': '3+4+5'}) await communicator.send_json_to({'tid': tid, 'type': 'start_draw', 'fmt': '3+4+5'})
# Receive data after the start # Receive data after the start
self.assertEqual((await communicator.receive_json_from())['type'], 'alert') self.assertEqual((await communicator.receive_json_from())['type'], 'alert')
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_poules', 'round': 1, {'tid': tid, 'type': 'set_poules', 'round': 1,
'poules': [{'letter': 'A', 'teams': []}, 'poules': [{'letter': 'A', 'teams': []},
{'letter': 'B', 'teams': []}, {'letter': 'B', 'teams': []},
{'letter': 'C', 'teams': []}]}) {'letter': 'C', 'teams': []}]})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_poules', 'round': 2, {'tid': tid, 'type': 'set_poules', 'round': 2,
'poules': [{'letter': 'A', 'teams': []}, 'poules': [{'letter': 'A', 'teams': []},
{'letter': 'B', 'teams': []}, {'letter': 'B', 'teams': []},
{'letter': 'C', 'teams': []}]}) {'letter': 'C', 'teams': []}]})
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'dice_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'alert') self.assertEqual((await communicator.receive_json_from())['type'], 'alert')
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'draw_start', 'fmt': [5, 4, 3], {'tid': tid, 'type': 'draw_start', 'fmt': [5, 4, 3],
'trigrams': ['AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF', 'trigrams': ['AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF',
'GGG', 'HHH', 'III', 'JJJ', 'KKK', 'LLL']}) 'GGG', 'HHH', 'III', 'JJJ', 'KKK', 'LLL']})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_active', 'round': 1, 'poule': None, 'team': None}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': None, 'team': None})
self.assertEqual((await communicator.receive_json_from())['type'], 'notification') self.assertEqual((await communicator.receive_json_from())['type'], 'notification')
# Ensure that now tournament has started # Ensure that now tournament has started
@ -107,7 +109,7 @@ class TestDraw(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Try to relaunch the draw # Try to relaunch the draw
await communicator.send_json_to({'type': 'start_draw', 'fmt': '3+4+5'}) await communicator.send_json_to({'tid': tid, 'type': 'start_draw', 'fmt': '3+4+5'})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], 'alert') self.assertEqual(resp['type'], 'alert')
self.assertEqual(resp['alert_type'], 'danger') self.assertEqual(resp['alert_type'], 'danger')
@ -119,7 +121,7 @@ class TestDraw(TestCase):
for i, team in enumerate(self.teams): for i, team in enumerate(self.teams):
# Launch a new dice # Launch a new dice
await communicator.send_json_to({'type': "dice", 'trigram': team.trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': team.trigram})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], "dice") self.assertEqual(resp['type'], "dice")
self.assertEqual(resp['team'], team.trigram) self.assertEqual(resp['team'], team.trigram)
@ -130,7 +132,7 @@ class TestDraw(TestCase):
self.assertEqual(resp['result'], td.passage_dice) self.assertEqual(resp['result'], td.passage_dice)
# Try to relaunch the dice # Try to relaunch the dice
await communicator.send_json_to({'type': "dice", 'trigram': team.trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': team.trigram})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], 'alert') self.assertEqual(resp['type'], 'alert')
self.assertEqual(resp['message'], "You've already launched the dice.") self.assertEqual(resp['message'], "You've already launched the dice.")
@ -151,7 +153,7 @@ class TestDraw(TestCase):
self.assertEqual((await communicator.receive_json_from())['type'], 'alert') self.assertEqual((await communicator.receive_json_from())['type'], 'alert')
for i in range(dup_count): for i in range(dup_count):
await communicator.send_json_to({'type': "dice", 'trigram': None}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': None})
await communicator.receive_json_from() await communicator.receive_json_from()
# Reset dices # Reset dices
@ -161,8 +163,10 @@ class TestDraw(TestCase):
self.assertIsNone(resp['result']) self.assertIsNone(resp['result'])
# Hide and re-display the dice # Hide and re-display the dice
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) {'tid': tid, 'type': 'dice_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'dice_visibility', 'visible': True})
# Set pools for the two rounds # Set pools for the two rounds
self.assertEqual((await communicator.receive_json_from())['type'], 'set_poules') self.assertEqual((await communicator.receive_json_from())['type'], 'set_poules')
@ -172,7 +176,7 @@ class TestDraw(TestCase):
# Manage the first pool # Manage the first pool
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_active', 'round': 1, 'poule': 'A', 'team': None}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'A', 'team': None})
r: Round = await Round.objects.prefetch_related('current_pool__current_team__participation__team')\ r: Round = await Round.objects.prefetch_related('current_pool__current_team__participation__team')\
.aget(number=1, draw=draw) .aget(number=1, draw=draw)
p = r.current_pool p = r.current_pool
@ -188,7 +192,7 @@ class TestDraw(TestCase):
i = 0 i = 0
async for td in p.teamdraw_set.prefetch_related('participation__team').all(): async for td in p.teamdraw_set.prefetch_related('participation__team').all():
# Launch a new dice # Launch a new dice
await communicator.send_json_to({'type': "dice", 'trigram': td.participation.team.trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], "dice") self.assertEqual(resp['type'], "dice")
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
@ -200,7 +204,7 @@ class TestDraw(TestCase):
self.assertEqual(resp['result'], td.choice_dice) self.assertEqual(resp['result'], td.choice_dice)
# Try to relaunch the dice # Try to relaunch the dice
await communicator.send_json_to({'type': "dice", 'trigram': trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': trigram})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], 'alert') self.assertEqual(resp['type'], 'alert')
self.assertEqual(resp['message'], "You've already launched the dice.") self.assertEqual(resp['message'], "You've already launched the dice.")
@ -222,7 +226,7 @@ class TestDraw(TestCase):
self.assertEqual((await communicator.receive_json_from())['type'], 'alert') self.assertEqual((await communicator.receive_json_from())['type'], 'alert')
for i in range(dup_count): for i in range(dup_count):
await communicator.send_json_to({'type': "dice", 'trigram': None}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': None})
await communicator.receive_json_from() await communicator.receive_json_from()
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
@ -232,27 +236,32 @@ class TestDraw(TestCase):
td = p.current_team td = p.current_team
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
self.assertEqual(td.choose_index, 0) self.assertEqual(td.choose_index, 0)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'A', self.assertEqual(await communicator.receive_json_from(),
'team': td.participation.team.trigram}) {'tid': tid, 'type': 'set_active', 'round': 1,
'poule': 'A', 'team': td.participation.team.trigram})
# Dice is hidden for everyone first # Dice is hidden for everyone first
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'dice_visibility', 'visible': False})
# The draw box is displayed for the current team and for volunteers # The draw box is displayed for the current team and for volunteers
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Try to launch a dice while it is not the time # Try to launch a dice while it is not the time
await communicator.send_json_to({'type': 'dice', 'trigram': None}) await communicator.send_json_to({'tid': tid, 'type': 'dice', 'trigram': None})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], 'alert') self.assertEqual(resp['type'], 'alert')
self.assertEqual(resp['message'], "This is not the time for this.") self.assertEqual(resp['message'], "This is not the time for this.")
# Draw a problem # Draw a problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -265,17 +274,19 @@ class TestDraw(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Try to redraw a problem while it is not the time # Try to redraw a problem while it is not the time
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
resp = await communicator.receive_json_from() resp = await communicator.receive_json_from()
self.assertEqual(resp['type'], 'alert') self.assertEqual(resp['type'], 'alert')
self.assertEqual(resp['message'], "This is not the time for this.") self.assertEqual(resp['message'], "This is not the time for this.")
# Reject the first problem # Reject the first problem
await communicator.send_json_to({'type': 'reject'}) await communicator.send_json_to({'tid': tid, 'type': 'reject'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'reject_problem', 'round': 1, 'team': trigram, 'rejected': [purposed]}) {'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'reject_problem', 'round': 1, 'team': trigram, 'rejected': [purposed]})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
@ -287,17 +298,19 @@ class TestDraw(TestCase):
td = p.current_team td = p.current_team
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
self.assertEqual(td.choose_index, i + 1) self.assertEqual(td.choose_index, i + 1)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'A', self.assertEqual(await communicator.receive_json_from(),
'team': trigram}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'A', 'team': trigram})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Draw a problem # Draw a problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -313,11 +326,13 @@ class TestDraw(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Accept the problem # Accept the problem
await communicator.send_json_to({'type': 'accept'}) await communicator.send_json_to({'tid': tid, 'type': 'accept'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': 1 + (i % 2)}) {'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': 1 + (i % 2)})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
@ -332,15 +347,17 @@ class TestDraw(TestCase):
td = p.current_team td = p.current_team
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
self.assertEqual(td.choose_index, 0) self.assertEqual(td.choose_index, 0)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'A', self.assertEqual(await communicator.receive_json_from(),
'team': trigram}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'A', 'team': trigram})
# Draw and reject 100 times a problem # Draw and reject 100 times a problem
for _i in range(100): for _i in range(100):
# Draw a problem # Draw a problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -349,10 +366,12 @@ class TestDraw(TestCase):
self.assertIn(td.purposed, range(3, len(settings.PROBLEMS) + 1)) self.assertIn(td.purposed, range(3, len(settings.PROBLEMS) + 1))
# Reject # Reject
await communicator.send_json_to({'type': 'reject'}) await communicator.send_json_to({'tid': tid, 'type': 'reject'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual((await communicator.receive_json_from())['type'], 'reject_problem') self.assertEqual((await communicator.receive_json_from())['type'], 'reject_problem')
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
@ -361,8 +380,8 @@ class TestDraw(TestCase):
# Ensures that this is still the first team # Ensures that this is still the first team
p: Pool = await Pool.objects.prefetch_related('current_team__participation__team').aget(round=r, letter=1) p: Pool = await Pool.objects.prefetch_related('current_team__participation__team').aget(round=r, letter=1)
self.assertEqual(p.current_team, td) self.assertEqual(p.current_team, td)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'A', self.assertEqual(await communicator.receive_json_from(),
'team': trigram}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'A', 'team': trigram})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
@ -372,9 +391,11 @@ class TestDraw(TestCase):
self.assertGreaterEqual(td.penalty, 1) self.assertGreaterEqual(td.penalty, 1)
# Draw a last problem # Draw a last problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -382,19 +403,21 @@ class TestDraw(TestCase):
self.assertIn(td.purposed, range(1, len(settings.PROBLEMS) + 1)) self.assertIn(td.purposed, range(1, len(settings.PROBLEMS) + 1))
# Accept the problem # Accept the problem
await communicator.send_json_to({'type': 'accept'}) await communicator.send_json_to({'tid': tid, 'type': 'accept'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': td.purposed}) {'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': td.purposed})
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
# Reorder the pool since there are 5 teams # Reorder the pool since there are 5 teams
self.assertEqual((await communicator.receive_json_from())['type'], 'reorder_poule') self.assertEqual((await communicator.receive_json_from())['type'], 'reorder_poule')
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'dice_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_active', 'round': 1, 'poule': 'B', 'team': None}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'B', 'team': None})
# Start pool 2 # Start pool 2
r: Round = await Round.objects.prefetch_related('current_pool__current_team__participation__team')\ r: Round = await Round.objects.prefetch_related('current_pool__current_team__participation__team')\
@ -412,7 +435,7 @@ class TestDraw(TestCase):
i = 0 i = 0
async for td in p.teamdraw_set.prefetch_related('participation__team').all(): async for td in p.teamdraw_set.prefetch_related('participation__team').all():
# Launch a new dice # Launch a new dice
await communicator.send_json_to({'type': "dice", 'trigram': td.participation.team.trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram})
await communicator.receive_json_from() await communicator.receive_json_from()
await td.arefresh_from_db() await td.arefresh_from_db()
td.choice_dice = 101 + i # Avoid duplicates td.choice_dice = 101 + i # Avoid duplicates
@ -427,20 +450,24 @@ class TestDraw(TestCase):
td = p.current_team td = p.current_team
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
self.assertEqual(td.choose_index, i) self.assertEqual(td.choose_index, i)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'B', self.assertEqual(await communicator.receive_json_from(),
'team': trigram}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'B', 'team': trigram})
if i == 0: if i == 0:
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) {'tid': tid, 'type': 'dice_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Draw a problem # Draw a problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -458,14 +485,17 @@ class TestDraw(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Accept the problem # Accept the problem
await communicator.send_json_to({'type': 'accept'}) await communicator.send_json_to({'tid': tid, 'type': 'accept'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1}) {'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1})
if i < 3: if i < 3:
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
else: else:
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'dice_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
@ -483,8 +513,8 @@ class TestDraw(TestCase):
self.assertEqual(p.size, 3) self.assertEqual(p.size, 3)
self.assertEqual(await p.teamdraw_set.acount(), 3) self.assertEqual(await p.teamdraw_set.acount(), 3)
self.assertEqual(p.current_team, None) self.assertEqual(p.current_team, None)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'C', self.assertEqual(await communicator.receive_json_from(),
'team': None}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'C', 'team': None})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
@ -493,7 +523,7 @@ class TestDraw(TestCase):
i = 0 i = 0
async for td in p.teamdraw_set.prefetch_related('participation__team').all(): async for td in p.teamdraw_set.prefetch_related('participation__team').all():
# Launch a new dice # Launch a new dice
await communicator.send_json_to({'type': "dice", 'trigram': td.participation.team.trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram})
await communicator.receive_json_from() await communicator.receive_json_from()
await td.arefresh_from_db() await td.arefresh_from_db()
td.choice_dice = 101 + i # Avoid duplicates td.choice_dice = 101 + i # Avoid duplicates
@ -508,20 +538,24 @@ class TestDraw(TestCase):
td = p.current_team td = p.current_team
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
self.assertEqual(td.choose_index, i) self.assertEqual(td.choose_index, i)
self.assertEqual(await communicator.receive_json_from(), {'type': 'set_active', 'round': 1, 'poule': 'C', self.assertEqual(await communicator.receive_json_from(),
'team': trigram}) {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'C', 'team': trigram})
if i == 0: if i == 0:
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) {'tid': tid, 'type': 'dice_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Draw a problem # Draw a problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -539,16 +573,18 @@ class TestDraw(TestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Accept the problem # Accept the problem
await communicator.send_json_to({'type': 'accept'}) await communicator.send_json_to({'tid': tid, 'type': 'accept'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1}) {'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1})
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
self.assertEqual(td.accepted, i + 1) self.assertEqual(td.accepted, i + 1)
if i == 2: if i == 2:
break break
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
# Render page # Render page
@ -572,8 +608,10 @@ class TestDraw(TestCase):
self.assertEqual(resp['type'], 'set_poules') self.assertEqual(resp['type'], 'set_poules')
self.assertEqual(resp['round'], 2) self.assertEqual(resp['round'], 2)
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
self.assertEqual(await communicator.receive_json_from(), {'type': 'export_visibility', 'visible': True}) {'tid': tid, 'type': 'dice_visibility', 'visible': True})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'export_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
# Render page # Render page
@ -589,12 +627,12 @@ class TestDraw(TestCase):
self.assertEqual(p.size, 5 - i) self.assertEqual(p.size, 5 - i)
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_active', 'round': 2, 'poule': chr(65 + i), 'team': None}) {'tid': tid, 'type': 'set_active', 'round': 2, 'poule': chr(65 + i), 'team': None})
j = 0 j = 0
async for td in p.teamdraw_set.prefetch_related('participation__team').all(): async for td in p.teamdraw_set.prefetch_related('participation__team').all():
# Launch a new dice # Launch a new dice
await communicator.send_json_to({'type': "dice", 'trigram': td.participation.team.trigram}) await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram})
await communicator.receive_json_from() await communicator.receive_json_from()
await td.arefresh_from_db() await td.arefresh_from_db()
td.choice_dice = 101 + j # Avoid duplicates td.choice_dice = 101 + j # Avoid duplicates
@ -612,23 +650,24 @@ class TestDraw(TestCase):
trigram = td.participation.team.trigram trigram = td.participation.team.trigram
self.assertEqual(td.choose_index, j) self.assertEqual(td.choose_index, j)
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'set_active', 'round': 2, 'poule': chr(65 + i), {'tid': tid, 'type': 'set_active', 'round': 2, 'poule': chr(65 + i),
'team': trigram}) 'team': trigram})
if j == 0: if j == 0:
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'dice_visibility', 'visible': False}) {'tid': tid, 'type': 'dice_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'box_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': True})
# Render page # Render page
resp = await self.async_client.get(reverse('draw:index')) resp = await self.async_client.get(reverse('draw:index'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Draw a problem # Draw a problem
await communicator.send_json_to({'type': 'draw_problem'}) await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'buttons_visibility', 'visible': True}) {'tid': tid, 'type': 'box_visibility', 'visible': False})
self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'buttons_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
@ -640,44 +679,55 @@ class TestDraw(TestCase):
self.assertNotEqual(td.purposed, old_td.accepted) self.assertNotEqual(td.purposed, old_td.accepted)
# Accept the problem # Accept the problem
await communicator.send_json_to({'type': 'accept'}) await communicator.send_json_to({'tid': tid, 'type': 'accept'})
self.assertEqual(await communicator.receive_json_from(), self.assertEqual(await communicator.receive_json_from(),
{'type': 'buttons_visibility', 'visible': False}) {'tid': tid, 'type': 'buttons_visibility', 'visible': False})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_problem') self.assertEqual((await communicator.receive_json_from())['type'], 'set_problem')
td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk)
self.assertIsNone(td.purposed) self.assertIsNone(td.purposed)
if j == 4 - i: if j == 4 - i:
break break
self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'box_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
if i == 0: if i == 0:
# Reorder the pool since there are 5 teams # Reorder the pool since there are 5 teams
self.assertEqual((await communicator.receive_json_from())['type'], 'reorder_poule') self.assertEqual((await communicator.receive_json_from())['type'], 'reorder_poule')
if i < 2: if i < 2:
self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'dice_visibility', 'visible': True})
else: else:
self.assertEqual(await communicator.receive_json_from(), {'type': 'export_visibility', 'visible': True}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'export_visibility', 'visible': True})
self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') self.assertEqual((await communicator.receive_json_from())['type'], 'set_info')
self.assertEqual((await communicator.receive_json_from())['type'], 'set_active') self.assertEqual((await communicator.receive_json_from())['type'], 'set_active')
# Export the draw # Export the draw
await communicator.send_json_to({'type': 'export'}) await communicator.send_json_to({'tid': tid, 'type': 'export'})
self.assertEqual(await communicator.receive_json_from(), {'type': 'export_visibility', 'visible': False}) self.assertEqual(await communicator.receive_json_from(),
{'tid': tid, 'type': 'export_visibility', 'visible': False})
# Cancel all steps and reset all # Cancel all steps and reset all
for i in range(1000): for i in range(1000):
await communicator.send_json_to({'type': 'cancel'}) await communicator.send_json_to({'tid': tid, 'type': 'cancel'})
if not await Draw.objects.filter(tournament=self.tournament).aexists():
# Purge receive queue
while True:
try:
await communicator.receive_json_from()
except asyncio.TimeoutError:
break break
else:
current_state = (await Draw.objects.filter(tournament=self.tournament).prefetch_related( if await Draw.objects.filter(tournament_id=tid).aexists():
print((await Draw.objects.filter(tournament_id=tid).aexists()))
current_state = (await Draw.objects.filter(tournament_id=tid).prefetch_related(
'current_round__current_pool__current_team__participation__team').aget()).get_state() 'current_round__current_pool__current_team__participation__team').aget()).get_state()
raise AssertionError("Draw wasn't aborted after 1000 steps, current state: " + current_state) raise AssertionError("Draw wasn't aborted after 1000 steps, current state: " + current_state)
# Abort while the tournament is already aborted # Abort while the tournament is already aborted
await communicator.send_json_to({'type': "abort"}) await communicator.send_json_to({'tid': tid, 'type': "abort"})
def test_admin_pages(self): def test_admin_pages(self):
""" """