1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-27 07:46:28 +00:00

Compare commits

..

No commits in common. "b38302449ce24a2d8285a7bed3821b84815d3afa" and "8c41684993882322813890d8053ae938290b541f" have entirely different histories.

5 changed files with 868 additions and 1031 deletions

View File

@ -42,61 +42,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
We accept only if this is a user of a team of the associated tournament, or a volunteer
of the tournament.
"""
# 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) \
# Get the tournament from the URL
self.tournament_id = self.scope['url_route']['kwargs']['tournament_id']
self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\
.prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget()
# Fetch participations from the tournament
@ -104,6 +52,51 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'):
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
self.tournament = await Tournament.objects.filter(pk=self.tournament_id)\
.prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget()
@ -183,9 +176,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await TeamDraw.objects.acreate(participation=participation, round=r)
# Send to clients the different pools
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{
'tid': self.tournament_id,
'type': 'draw.send_poules',
{'type': 'draw.send_poules',
'round': r.number,
'poules': [
{
@ -193,32 +184,29 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'teams': await pool.atrigrams(),
}
async for pool in r.pool_set.order_by('letter').all()
]
})
]})
draw.current_round = r1
await draw.asave()
# Make dice box visible
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
await self.alert(_("Draw started!"), 'success')
# Update user interface
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.start', 'fmt': fmt, 'draw': draw})
{'type': 'draw.start', 'fmt': fmt, 'draw': draw})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active', 'round': 1})
{'type': 'draw.set_active', 'round': 1})
# Send notification to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': 'Tirage au sort du TFJM²',
{'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²',
'body': "Le tirage au sort du tournoi de "
f"{self.tournament.name} a commencé !"})
@ -228,7 +216,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
await self.alert(_("The draw for the tournament {tournament} will start.")
.format(tournament=self.tournament.name), 'warning')
await self.send_json({'tid': content['tid'], 'type': 'draw_start', 'fmt': content['fmt'],
await self.send_json({'type': 'draw_start', 'fmt': content['fmt'],
'trigrams': [p.team.trigram for p in self.participations]})
@ensure_orga
@ -243,8 +231,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# All associated data will be deleted by cascade
await self.tournament.draw.adelete()
# Send information to all users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw_abort'})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {'type': 'draw_abort'})
async def draw_abort(self, content) -> None:
"""
@ -252,7 +239,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
await self.alert(_("The draw for the tournament {tournament} is aborted.")
.format(tournament=self.tournament.name), 'danger')
await self.send_json({'tid': content['tid'], 'type': 'abort'})
await self.send_json({'type': 'abort'})
async def process_dice(self, trigram: str | None = None, **kwargs):
"""
@ -323,8 +310,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send the dice result to all users
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
'team': trigram, 'result': res})
f"tournament-{self.tournament.id}", {'type': 'draw.dice', 'team': trigram, 'result': res})
if state == 'DICE_SELECT_POULES' and \
not await TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id,
@ -378,20 +364,19 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await dup.asave()
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice',
'team': dup.participation.team.trigram, 'result': None})
{'type': 'draw.dice', 'team': dup.participation.team.trigram, 'result': None})
# Send notification to concerned teams
await self.channel_layer.group_send(
f"team-{dup.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²',
{'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²',
'body': 'Votre score de dé est identique à celui de une ou plusieurs équipes. '
'Veuillez le relancer.'}
)
# Alert the tournament
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.alert',
{'type': 'draw.alert',
'message': _('Dices from teams {teams} are identical. Please relaunch your dices.').format(
teams=', '.join(td.participation.team.trigram for td in dups)),
'alert_type': 'warning'})
@ -430,9 +415,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# This also determines the passage order, in the natural order this time.
# If there is a 5-teams pool, we force the last team to be in the first pool,
# which is this specific pool since they are ordered by decreasing size.
# This is not true for the final tournament, which considers the scores of the
# first round.
if not self.tournament.final:
tds_copy = tds.copy()
round2 = await self.tournament.draw.round_set.filter(number=2).aget()
round2_pools = [p async for p in Pool.objects.filter(round__draw__tournament=self.tournament, round=round2)
@ -470,26 +452,23 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
for td in tds:
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None})
{'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None})
# Hide dice interface
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
{'type': 'draw.dice_visibility', 'visible': False})
# 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():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
# First send the second pool to have the good team order
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.send_poules',
{'type': 'draw.send_poules',
'round': r2.number,
'poules': [
{
@ -499,7 +478,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for pool in r2.pool_set.order_by('letter').all()
]})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.send_poules',
{'type': 'draw.send_poules',
'round': r.number,
'poules': [
{
@ -511,10 +490,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update information header and the active team on the recap menu
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display()})
@ -542,30 +521,28 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update information header
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram})
# Hide dice button to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
{'type': 'draw.dice_visibility', 'visible': False})
# Display the box button to the first team and to volunteers
trigram = pool.current_team.participation.team.trigram
await self.channel_layer.group_send(f"team-{trigram}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{tds[0].participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
async def select_problem(self, **kwargs):
@ -608,25 +585,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update interface
trigram = td.participation.team.trigram
await self.channel_layer.group_send(f"team-{trigram}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': False})
{'type': 'draw.box_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': False})
{'type': 'draw.box_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"team-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.draw_problem', 'team': trigram,
'problem': problem})
{'type': 'draw.draw_problem', 'team': trigram, 'problem': problem})
self.tournament.draw.last_message = ""
await self.tournament.draw.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
async def accept_problem(self, **kwargs):
@ -669,13 +641,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send the accepted problem to the users
await self.channel_layer.group_send(f"team-{trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_problem',
{'type': 'draw.set_problem',
'round': r.number,
'team': trigram,
'problem': td.accepted})
@ -689,16 +659,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
new_trigram = next_td.participation.team.trigram
await self.channel_layer.group_send(f"team-{new_trigram}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
else:
# Pool is ended
@ -707,10 +674,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
pool = r.current_pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram
@ -748,7 +715,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send the reordered pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {
'tid': self.tournament_id,
'type': 'draw.reorder_pool',
'round': r.number,
'pool': pool.get_letter_display(),
@ -770,17 +736,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
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}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
else:
# Round is ended
await self.end_round(r)
@ -803,18 +766,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
for participation in self.participations:
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice',
'team': participation.team.trigram, 'result': None})
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
# Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.send_poules',
{'type': 'draw.send_poules',
'round': r2.number,
'poules': [
{
@ -832,11 +793,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in p1.teamdraw_set.prefetch_related('participation__team').all():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
elif r.number == 1 and self.tournament.final:
# For the final tournament, we wait for a manual update between the two rounds.
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
@ -844,8 +803,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.tournament.draw.asave()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.export_visibility',
'visible': True})
{'type': 'draw.export_visibility', 'visible': True})
async def reject_problem(self, **kwargs):
"""
@ -896,13 +854,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update interface
await self.channel_layer.group_send(f"team-{trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.reject_problem',
{'type': 'draw.reject_problem',
'round': r.number, 'team': trigram, 'rejected': td.rejected})
if already_refused:
@ -917,23 +873,22 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
new_trigram = next_td.participation.team.trigram
await self.channel_layer.group_send(f"team-{new_trigram}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': new_trigram})
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
@ensure_orga
@ -951,8 +906,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await pool.export()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.export_visibility',
'visible': False})
{'type': 'draw.export_visibility', 'visible': False})
@ensure_orga
async def continue_final(self, **kwargs):
@ -972,12 +926,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
# Send notification to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': 'Tirage au sort du TFJM²',
'body': "Le tirage au sort pour le second tour de la finale a commencé !"})
# Set the first pool of the second round as the active pool
pool = await Pool.objects.filter(round=self.tournament.draw.current_round, letter=1).aget()
r2.current_pool = pool
@ -1003,7 +951,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send pools to users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.send_poules',
{'type': 'draw.send_poules',
'round': r2.number,
'poules': [
{
@ -1017,31 +965,27 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
for participation in self.participations:
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
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}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.notify',
'title': "À votre tour !",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.continue_visibility',
'visible': False})
{'type': 'draw.continue_visibility', 'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
{'type': 'draw.set_active',
'round': r2.number,
'pool': r2.current_pool.get_letter_display()})
@ -1070,12 +1014,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.undo_order_dice()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_info',
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
r = self.tournament.draw.current_round
p = r.current_pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_active',
{'type': 'draw.set_active',
'round': r.number,
'pool': p.get_letter_display() if p else None,
'team': p.current_team.participation.team.trigram
@ -1093,18 +1037,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.continue_visibility',
'visible': False})
{'type': 'draw.continue_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_problem',
{'type': 'draw.set_problem',
'round': r.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
@ -1120,15 +1061,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave()
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': False})
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True})
{'type': 'draw.box_visibility', 'visible': True})
async def undo_process_problem(self):
"""
@ -1176,7 +1115,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await last_td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_problem',
{'type': 'draw.set_problem',
'round': r.number,
'team': last_td.participation.team.trigram,
'problem': last_td.accepted})
@ -1194,7 +1133,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await last_td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.reject_problem',
{'type': 'draw.reject_problem',
'round': r.number,
'team': last_td.participation.team.trigram,
'rejected': last_td.rejected})
@ -1204,11 +1143,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await r.current_pool.asave()
await self.channel_layer.group_send(f"team-{last_td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
else:
# Return to the dice choice
pool_tds = {td.id: td async for td in p.team_draws.prefetch_related('participation__team')}
@ -1229,7 +1166,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
@ -1240,15 +1177,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Make dice box visible
for td in pool_tds.values():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.box_visibility',
'visible': False})
{'type': 'draw.box_visibility', 'visible': False})
async def undo_pool_dice(self):
"""
@ -1283,7 +1217,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
@ -1302,18 +1236,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
{'type': 'draw.dice_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_problem',
{'type': 'draw.set_problem',
'round': r.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
@ -1327,12 +1258,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in r1.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.send_poules',
{'type': 'draw.send_poules',
'round': r1.number,
'poules': [
{
@ -1350,18 +1281,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
{'type': 'draw.dice_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.buttons_visibility',
'visible': True})
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.set_problem',
{'type': 'draw.set_problem',
'round': r1.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
@ -1380,16 +1308,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
async for td in r1.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': False})
{'type': 'draw.dice_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.continue_visibility',
'visible': True})
{'type': 'draw.continue_visibility', 'visible': True})
else:
# Go to the dice order
async for r0 in self.tournament.draw.round_set.all():
@ -1423,20 +1349,19 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
async for td in r.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.passage_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'tid': self.tournament_id, 'type': 'draw.dice_visibility',
'visible': True})
{'type': 'draw.dice_visibility', 'visible': True})
async def undo_order_dice(self):
"""
@ -1468,7 +1393,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice',
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
@ -1485,57 +1410,55 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
Send a notification (with title and body) to the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'notification',
'title': content['title'], 'body': content['body']})
await self.send_json({'type': 'notification', 'title': content['title'], 'body': content['body']})
async def draw_set_info(self, content):
"""
Set the information banner to the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'set_info', 'information': content['info']})
await self.send_json({'type': 'set_info', 'information': content['info']})
async def draw_dice(self, content):
"""
Update the dice of a given team for the current user interface.
"""
await self.send_json({'tid': content['tid'], 'type': 'dice',
'team': content['team'], 'result': content['result']})
await self.send_json({'type': 'dice', 'team': content['team'], 'result': content['result']})
async def draw_dice_visibility(self, content):
"""
Update the visibility of the dice button for the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'dice_visibility', 'visible': content['visible']})
await self.send_json({'type': 'dice_visibility', 'visible': content['visible']})
async def draw_box_visibility(self, content):
"""
Update the visibility of the box button for the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'box_visibility', 'visible': content['visible']})
await self.send_json({'type': 'box_visibility', 'visible': content['visible']})
async def draw_buttons_visibility(self, content):
"""
Update the visibility of the accept/reject buttons for the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'buttons_visibility', 'visible': content['visible']})
await self.send_json({'type': 'buttons_visibility', 'visible': content['visible']})
async def draw_export_visibility(self, content):
"""
Update the visibility of the export button for the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'export_visibility', 'visible': content['visible']})
await self.send_json({'type': 'export_visibility', 'visible': content['visible']})
async def draw_continue_visibility(self, content):
"""
Update the visibility of the continue button for the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'continue_visibility', 'visible': content['visible']})
await self.send_json({'type': 'continue_visibility', 'visible': content['visible']})
async def draw_send_poules(self, content):
"""
Send the pools and the teams to the current user to update the interface.
"""
await self.send_json({'tid': content['tid'], 'type': 'set_poules', 'round': content['round'],
await self.send_json({'type': 'set_poules', 'round': content['round'],
'poules': content['poules']})
async def draw_set_active(self, content):
@ -1543,7 +1466,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
Update the user interface to highlight the current team.
"""
await self.send_json({
'tid': content['tid'],
'type': 'set_active',
'round': content.get('round', None),
'poule': content.get('pool', None),
@ -1554,20 +1476,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
Send the accepted problem of a team to the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'set_problem', 'round': content['round'],
await self.send_json({'type': 'set_problem', 'round': content['round'],
'team': content['team'], 'problem': content['problem']})
async def draw_reject_problem(self, content):
"""
Send the rejected problems of a team to the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'reject_problem', 'round': content['round'],
await self.send_json({'type': 'reject_problem', 'round': content['round'],
'team': content['team'], 'rejected': content['rejected']})
async def draw_reorder_pool(self, content):
"""
Send the new order of a pool to the current user.
"""
await self.send_json({'tid': content['tid'], 'type': 'reorder_poule', 'round': content['round'],
await self.send_json({'type': 'reorder_poule', 'round': content['round'],
'poule': content['pool'], 'teams': content['teams'],
'problems': content['problems']})

View File

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

View File

@ -7,7 +7,7 @@
const problems_count = JSON.parse(document.getElementById('problems_count').textContent)
const tournaments = JSON.parse(document.getElementById('tournaments_list').textContent)
let socket = null
const sockets = {}
const messages = document.getElementById('messages')
@ -17,7 +17,7 @@ const messages = document.getElementById('messages')
* @param tid The tournament id
*/
function abortDraw(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'abort'}))
sockets[tid].send(JSON.stringify({'type': 'abort'}))
}
/**
@ -26,7 +26,7 @@ function abortDraw(tid) {
* @param tid The tournament id
*/
function cancelLastStep(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'cancel'}))
sockets[tid].send(JSON.stringify({'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)
*/
function drawDice(tid, trigram = null) {
socket.send(JSON.stringify({'tid': tid, 'type': 'dice', 'trigram': trigram}))
sockets[tid].send(JSON.stringify({'type': 'dice', 'trigram': trigram}))
}
/**
@ -44,7 +44,7 @@ function drawDice(tid, trigram = null) {
* @param tid The tournament id
*/
function drawProblem(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'draw_problem'}))
sockets[tid].send(JSON.stringify({'type': 'draw_problem'}))
}
/**
@ -52,7 +52,7 @@ function drawProblem(tid) {
* @param tid The tournament id
*/
function acceptProblem(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'accept'}))
sockets[tid].send(JSON.stringify({'type': 'accept'}))
}
/**
@ -60,7 +60,7 @@ function acceptProblem(tid) {
* @param tid The tournament id
*/
function rejectProblem(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'reject'}))
sockets[tid].send(JSON.stringify({'type': 'reject'}))
}
/**
@ -68,7 +68,7 @@ function rejectProblem(tid) {
* @param tid The tournament id
*/
function exportDraw(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'export'}))
sockets[tid].send(JSON.stringify({'type': 'export'}))
}
/**
@ -76,7 +76,7 @@ function exportDraw(tid) {
* @param tid The tournament id
*/
function continueFinal(tid) {
socket.send(JSON.stringify({'tid': tid, 'type': 'continue_final'}))
sockets[tid].send(JSON.stringify({'type': 'continue_final'}))
}
/**
@ -108,6 +108,14 @@ document.addEventListener('DOMContentLoaded', () => {
elem => elem.addEventListener(
'click', () => document.location.hash = '#' + elem.innerText.toLowerCase()))
for (let tournament of tournaments) {
// Open a websocket per tournament
let socket = new WebSocket(
(document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host
+ '/ws/draw/' + tournament.id + '/'
)
sockets[tournament.id] = socket
/**
* Add alert message on the top on the interface.
* @param message The content of the alert.
@ -129,25 +137,23 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Update the information banner.
* @param tid The tournament id
* @param info The content to updated
*/
function setInfo(tid, info) {
document.getElementById(`messages-${tid}`).innerHTML = info
function setInfo(info) {
document.getElementById(`messages-${tournament.id}`).innerHTML = info
}
/**
* 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.
*/
function drawStart(tid, teams) {
function drawStart(teams) {
// Hide the not-started-banner
document.getElementById(`banner-not-started-${tid}`).classList.add('d-none')
document.getElementById(`banner-not-started-${tournament.id}`).classList.add('d-none')
// Display the full draw interface
document.getElementById(`draw-content-${tid}`).classList.remove('d-none')
document.getElementById(`draw-content-${tournament.id}`).classList.remove('d-none')
let dicesDiv = document.getElementById(`dices-${tid}`)
let dicesDiv = document.getElementById(`dices-${tournament.id}`)
for (let team of teams) {
// Add empty dice score badge for each team
let col = document.createElement('div')
@ -155,11 +161,11 @@ document.addEventListener('DOMContentLoaded', () => {
dicesDiv.append(col)
let diceDiv = document.createElement('div')
diceDiv.id = `dice-${tid}-${team}`
diceDiv.id = `dice-${tournament.id}-${team}`
diceDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning')
if (document.getElementById(`abort-${tid}`) !== null) {
// Check if this is a volunteer, who can launch a die for a specific team
diceDiv.onclick = (_) => drawDice(tid, team)
if (document.getElementById(`abort-${tournament.id}`) !== null) {
// Check if this is a volunteer, who can launch a dice for a specific team
diceDiv.onclick = (e) => drawDice(tournament.id, team)
}
diceDiv.textContent = `${team} 🎲 ??`
col.append(diceDiv)
@ -168,30 +174,28 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Abort the current draw, and make all invisible, except the not-started-banner.
* @param tid The tournament id
*/
function drawAbort(tid) {
document.getElementById(`banner-not-started-${tid}`).classList.remove('d-none')
document.getElementById(`draw-content-${tid}`).classList.add('d-none')
document.getElementById(`dices-${tid}`).innerHTML = ""
document.getElementById(`recap-${tid}-round-list`).innerHTML = ""
document.getElementById(`tables-${tid}`).innerHTML = ""
updateDiceVisibility(tid, false)
updateBoxVisibility(tid, false)
updateButtonsVisibility(tid, false)
updateExportVisibility(tid, false)
updateContinueVisibility(tid, false)
function drawAbort() {
document.getElementById(`banner-not-started-${tournament.id}`).classList.remove('d-none')
document.getElementById(`draw-content-${tournament.id}`).classList.add('d-none')
document.getElementById(`dices-${tournament.id}`).innerHTML = ""
document.getElementById(`recap-${tournament.id}-round-list`).innerHTML = ""
document.getElementById(`tables-${tournament.id}`).innerHTML = ""
updateDiceVisibility(false)
updateBoxVisibility(false)
updateButtonsVisibility(false)
updateExportVisibility(false)
updateContinueVisibility(false)
}
/**
* 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.
* @param tid The tournament id
* @param trigram The trigram of the team that launched its dice
* @param result The result of the dice. null if it is a reset.
*/
function updateDiceInfo(tid, trigram, result) {
let elem = document.getElementById(`dice-${tid}-${trigram}`)
function updateDiceInfo(trigram, result) {
let elem = document.getElementById(`dice-${tournament.id}-${trigram}`)
if (result === null) {
elem.classList.remove('text-bg-success')
elem.classList.add('text-bg-warning')
@ -206,11 +210,10 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Display or hide the dice button.
* @param tid The tournament id
* @param visible The visibility status
*/
function updateDiceVisibility(tid, visible) {
let div = document.getElementById(`launch-dice-${tid}`)
function updateDiceVisibility(visible) {
let div = document.getElementById(`launch-dice-${tournament.id}`)
if (visible)
div.classList.remove('d-none')
else
@ -219,11 +222,10 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Display or hide the box button.
* @param tid The tournament id
* @param visible The visibility status
*/
function updateBoxVisibility(tid, visible) {
let div = document.getElementById(`draw-problem-${tid}`)
function updateBoxVisibility(visible) {
let div = document.getElementById(`draw-problem-${tournament.id}`)
if (visible)
div.classList.remove('d-none')
else
@ -232,11 +234,10 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Display or hide the accept and reject buttons.
* @param tid The tournament id
* @param visible The visibility status
*/
function updateButtonsVisibility(tid, visible) {
let div = document.getElementById(`buttons-${tid}`)
function updateButtonsVisibility(visible) {
let div = document.getElementById(`buttons-${tournament.id}`)
if (visible)
div.classList.remove('d-none')
else
@ -245,11 +246,10 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Display or hide the export button.
* @param tid The tournament id
* @param visible The visibility status
*/
function updateExportVisibility(tid, visible) {
let div = document.getElementById(`export-${tid}`)
function updateExportVisibility(visible) {
let div = document.getElementById(`export-${tournament.id}`)
if (visible)
div.classList.remove('d-none')
else
@ -258,37 +258,32 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Display or hide the continuation button.
* @param tid The tournament id
* @param visible The visibility status
*/
function updateContinueVisibility(tid, visible) {
let div = document.getElementById(`continue-${tid}`)
if (div !== null) {
// Only present during the final
function updateContinueVisibility(visible) {
let div = document.getElementById(`continue-${tournament.id}`)
if (visible)
div.classList.remove('d-none')
else
div.classList.add('d-none')
}
}
/**
* 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 poules The list of poules, which are represented with their letters and trigrams,
* [{'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}]
*/
function updatePoules(tid, round, poules) {
let roundList = document.getElementById(`recap-${tid}-round-list`)
let poolListId = `recap-${tid}-round-${round}-pool-list`
function updatePoules(round, poules) {
let roundList = document.getElementById(`recap-${tournament.id}-round-list`)
let poolListId = `recap-${tournament.id}-round-${round}-pool-list`
let poolList = document.getElementById(poolListId)
if (poolList === null) {
// Add a div for the round in the recap div
let div = document.createElement('div')
div.id = `recap-${tid}-round-${round}`
div.id = `recap-${tournament.id}-round-${round}`
div.classList.add('col-md-6', 'px-3', 'py-3')
div.setAttribute('data-tournament', tid)
div.setAttribute('data-tournament', tournament.id)
let title = document.createElement('strong')
title.textContent = 'Tour ' + round
@ -304,14 +299,14 @@ document.addEventListener('DOMContentLoaded', () => {
let c = 1
for (let poule of poules) {
let teamListId = `recap-${tid}-round-${round}-pool-${poule.letter}-team-list`
let teamListId = `recap-${tournament.id}-round-${round}-pool-${poule.letter}-team-list`
let teamList = document.getElementById(teamListId)
if (teamList === null) {
// Add a div for the pool in the recap div
let li = document.createElement('li')
li.id = `recap-${tid}-round-${round}-pool-${poule.letter}`
li.id = `recap-${tournament.id}-round-${round}-pool-${poule.letter}`
li.classList.add('list-group-item', 'px-3', 'py-3')
li.setAttribute('data-tournament', tid)
li.setAttribute('data-tournament', tournament.id)
let title = document.createElement('strong')
title.textContent = 'Poule ' + poule.letter + round
@ -328,11 +323,11 @@ document.addEventListener('DOMContentLoaded', () => {
// The pool is initialized
for (let team of poule.teams) {
// Reorder dices
let diceDiv = document.getElementById(`dice-${tid}-${team}`)
let diceDiv = document.getElementById(`dice-${tournament.id}-${team}`)
diceDiv.parentElement.style.order = c.toString()
c += 1
let teamLiId = `recap-${tid}-round-${round}-team-${team}`
let teamLiId = `recap-${tournament.id}-round-${round}-team-${team}`
let teamLi = document.getElementById(teamLiId)
if (teamLi === null) {
@ -340,13 +335,13 @@ document.addEventListener('DOMContentLoaded', () => {
teamLi = document.createElement('li')
teamLi.id = teamLiId
teamLi.classList.add('list-group-item')
teamLi.setAttribute('data-tournament', tid)
teamLi.setAttribute('data-tournament', tournament.id)
teamList.append(teamLi)
}
// Add the accepted problem div (empty for now)
let acceptedDivId = `recap-${tid}-round-${round}-team-${team}-accepted`
let acceptedDivId = `recap-${tournament.id}-round-${round}-team-${team}-accepted`
let acceptedDiv = document.getElementById(acceptedDivId)
if (acceptedDiv === null) {
acceptedDiv = document.createElement('div')
@ -357,7 +352,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
// Add the rejected problems div (empty for now)
let rejectedDivId = `recap-${tid}-round-${round}-team-${team}-rejected`
let rejectedDivId = `recap-${tournament.id}-round-${round}-team-${team}-rejected`
let rejectedDiv = document.getElementById(rejectedDivId)
if (rejectedDiv === null) {
rejectedDiv = document.createElement('div')
@ -370,8 +365,8 @@ document.addEventListener('DOMContentLoaded', () => {
}
// Draw tables
let tablesDiv = document.getElementById(`tables-${tid}`)
let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`)
let tablesDiv = document.getElementById(`tables-${tournament.id}`)
let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`)
if (tablesRoundDiv === null) {
// Add the tables div for the current round if necessary
let card = document.createElement('div')
@ -384,7 +379,7 @@ document.addEventListener('DOMContentLoaded', () => {
card.append(cardHeader)
tablesRoundDiv = document.createElement('div')
tablesRoundDiv.id = `tables-${tid}-round-${round}`
tablesRoundDiv.id = `tables-${tournament.id}-round-${round}`
tablesRoundDiv.classList.add('card-body', 'd-flex', 'flex-wrap')
card.append(tablesRoundDiv)
}
@ -394,21 +389,20 @@ document.addEventListener('DOMContentLoaded', () => {
continue
// Display the table for the pool
updatePouleTable(tid, round, poule)
updatePouleTable(round, poule)
}
}
}
/**
* 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 poule The current pool, which id represented with its letter and trigrams,
* {'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}
*/
function updatePouleTable(tid, round, poule) {
let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`)
let pouleTable = document.getElementById(`table-${tid}-${round}-${poule.letter}`)
function updatePouleTable(round, poule) {
let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`)
let pouleTable = document.getElementById(`table-${tournament.id}-${round}-${poule.letter}`)
if (pouleTable === null) {
// Create table
let card = document.createElement('div')
@ -425,7 +419,7 @@ document.addEventListener('DOMContentLoaded', () => {
card.append(cardBody)
pouleTable = document.createElement('table')
pouleTable.id = `table-${tid}-${round}-${poule.letter}`
pouleTable.id = `table-${tournament.id}-${round}-${poule.letter}`
pouleTable.classList.add('table', 'table-stripped')
cardBody.append(pouleTable)
@ -470,7 +464,7 @@ document.addEventListener('DOMContentLoaded', () => {
let problemTh = document.createElement('th')
problemTh.classList.add('text-center')
// Problem is unknown for now
problemTh.innerHTML = `Pb. <span id="table-${tid}-round-${round}-problem-${team}">?</span>`
problemTh.innerHTML = `Pb. <span id="table-${tournament.id}-round-${round}-problem-${team}">?</span>`
problemTr.append(problemTh)
}
@ -559,47 +553,45 @@ document.addEventListener('DOMContentLoaded', () => {
}
/**
* Highlight the team that is currently choosing its problem.
* @param tid The tournament id
* Highligh the team that is currently choosing its problem.
* @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 team The current team trigram (null if non-relevant)
*/
function updateActiveRecap(tid, round, pool, team) {
function updateActiveRecap(round, pool, team) {
// Remove the previous highlights
document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tid}"]`)
document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tournament.id}"]`)
.forEach(elem => elem.classList.remove('text-bg-secondary'))
document.querySelectorAll(`li.list-group-item-success[data-tournament="${tid}"]`)
document.querySelectorAll(`li.list-group-item-success[data-tournament="${tournament.id}"]`)
.forEach(elem => elem.classList.remove('list-group-item-success'))
document.querySelectorAll(`li.list-group-item-info[data-tournament="${tid}"]`)
document.querySelectorAll(`li.list-group-item-info[data-tournament="${tournament.id}"]`)
.forEach(elem => elem.classList.remove('list-group-item-info'))
// Highlight current round, if existing
let roundDiv = document.getElementById(`recap-${tid}-round-${round}`)
let roundDiv = document.getElementById(`recap-${tournament.id}-round-${round}`)
if (roundDiv !== null)
roundDiv.classList.add('text-bg-secondary')
// Highlight current pool, if existing
let poolLi = document.getElementById(`recap-${tid}-round-${round}-pool-${pool}`)
let poolLi = document.getElementById(`recap-${tournament.id}-round-${round}-pool-${pool}`)
if (poolLi !== null)
poolLi.classList.add('list-group-item-success')
// Highlight current team, if existing
let teamLi = document.getElementById(`recap-${tid}-round-${round}-team-${team}`)
let teamLi = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}`)
if (teamLi !== null)
teamLi.classList.add('list-group-item-info')
}
/**
* 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 team The current team trigram
* @param problem The accepted problem, as integer
*/
function setProblemAccepted(tid, round, team, problem) {
function setProblemAccepted(round, team, problem) {
// Update recap
let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-accepted`)
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-accepted`)
if (problem !== null) {
recapDiv.classList.remove('text-bg-warning')
recapDiv.classList.add('text-bg-success')
@ -611,28 +603,27 @@ document.addEventListener('DOMContentLoaded', () => {
recapDiv.textContent = `${team} 📃 ${problem ? problem : '?'}`
// Update table
let tableSpan = document.getElementById(`table-${tid}-round-${round}-problem-${team}`)
let tableSpan = document.getElementById(`table-${tournament.id}-round-${round}-problem-${team}`)
tableSpan.textContent = problem ? 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 team The current team trigram
* @param rejected The full list of rejected problems
*/
function setProblemRejected(tid, round, team, rejected) {
function setProblemRejected(round, team, rejected) {
// Update recap
let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-rejected`)
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-rejected`)
recapDiv.textContent = `🗑️ ${rejected.join(', ')}`
let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`)
let penaltyDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-penalty`)
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 (penaltyDiv === null) {
penaltyDiv = document.createElement('div')
penaltyDiv.id = `recap-${tid}-round-${round}-team-${team}-penalty`
penaltyDiv.id = `recap-${tournament.id}-round-${round}-team-${team}-penalty`
penaltyDiv.classList.add('badge', 'rounded-pill', 'text-bg-info')
recapDiv.parentNode.append(penaltyDiv)
}
@ -648,34 +639,32 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* 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.
* @param tid The tournament id
* @param round The current round, as integer (1 or 2)
* @param poule The pool represented by its letter
* @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]
*/
function reorderPoule(tid, round, poule, teams, problems) {
function reorderPoule(round, poule, teams, problems) {
// Redraw the pool table
let table = document.getElementById(`table-${tid}-${round}-${poule}`)
let table = document.getElementById(`table-${tournament.id}-${round}-${poule}`)
table.parentElement.parentElement.remove()
updatePouleTable(tid, round, {'letter': poule, 'teams': teams})
updatePouleTable(round, {'letter': poule, 'teams': teams})
// Put the problems in the table
for (let i = 0; i < teams.length; ++i) {
let team = teams[i]
let problem = problems[i]
setProblemAccepted(tid, round, team, problem)
setProblemAccepted(round, team, problem)
}
}
/**
* Process the received data from the server.
* @param tid The tournament id
* @param data The received message
*/
function processMessage(tid, data) {
// Listen on websockets and process messages from the server
socket.addEventListener('message', e => {
// Parse received data as JSON
const data = JSON.parse(e.data)
switch (data.type) {
case 'alert':
// Add alert message
@ -687,93 +676,76 @@ document.addEventListener('DOMContentLoaded', () => {
break
case 'set_info':
// Update information banner
setInfo(tid, data.information)
setInfo(data.information)
break
case 'draw_start':
// Start the draw and update the interface
drawStart(tid, data.trigrams)
drawStart(data.trigrams)
break
case 'abort':
// Abort the current draw
drawAbort(tid)
drawAbort()
break
case 'dice':
// Update the interface after a dice launch
updateDiceInfo(tid, data.team, data.result)
updateDiceInfo(data.team, data.result)
break
case 'dice_visibility':
// Update the dice button visibility
updateDiceVisibility(tid, data.visible)
updateDiceVisibility(data.visible)
break
case 'box_visibility':
// Update the box button visibility
updateBoxVisibility(tid, data.visible)
updateBoxVisibility(data.visible)
break
case 'buttons_visibility':
// Update the accept/reject buttons visibility
updateButtonsVisibility(tid, data.visible)
updateButtonsVisibility(data.visible)
break
case 'export_visibility':
// Update the export button visibility
updateExportVisibility(tid, data.visible)
updateExportVisibility(data.visible)
break
case 'continue_visibility':
// Update the continue button visibility for the final tournament
updateContinueVisibility(tid, data.visible)
updateContinueVisibility(data.visible)
break
case 'set_poules':
// Set teams order and pools and update the interface
updatePoules(tid, data.round, data.poules)
updatePoules(data.round, data.poules)
break
case 'set_active':
// Highlight the team that is selecting a problem
updateActiveRecap(tid, data.round, data.poule, data.team)
updateActiveRecap(data.round, data.poule, data.team)
break
case 'set_problem':
// Mark a problem as accepted and update the interface
setProblemAccepted(tid, data.round, data.team, data.problem)
setProblemAccepted(data.round, data.team, data.problem)
break
case 'reject_problem':
// Mark a problem as rejected and update the interface
setProblemRejected(tid, data.round, data.team, data.rejected)
setProblemRejected(data.round, data.team, data.rejected)
break
case 'reorder_poule':
// Reorder a pool and redraw the associated table
reorderPoule(tid, data.round, data.poule, data.teams, data.problems)
reorderPoule(data.round, data.poule, data.teams, data.problems)
break
}
}
function setupSocket() {
// Open a global websocket
socket = new WebSocket(
(document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host + '/ws/draw/'
)
// 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
socket.addEventListener('close', e => {
console.error('Chat socket closed unexpectedly, restarting…')
setupSocket()
console.error('Chat socket closed unexpectedly')
})
// When the socket is opened, set the language in order to receive alerts in the good language
socket.addEventListener('open', e => {
socket.send(JSON.stringify({
'tid': tournaments[0].id,
'type': 'set_language',
'language': document.getElementsByName('language')[0].value,
}))
})
for (let tournament of tournaments) {
// Manage the start form
let format_form = document.getElementById('format-form-' + tournament.id)
if (format_form !== null) {
@ -781,14 +753,10 @@ document.addEventListener('DOMContentLoaded', () => {
e.preventDefault()
socket.send(JSON.stringify({
'tid': tournament.id,
'type': 'start_draw',
'fmt': document.getElementById('format-' + tournament.id).value
}))
})
}
}
}
setupSocket()
})

View File

@ -351,7 +351,7 @@
{% trans "Are you sure you want to abort this draw?" %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger" data-bs-dismiss="modal" onclick="abortDraw({{ tournament.id }})">{% trans "Abort" %}</button>
<button type="submit" class="btn btn-danger" onclick="abortDraw({{ tournament.id }})">{% trans "Abort" %}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
</div>
</div>

View File

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