mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-10-31 15:40:01 +01:00 
			
		
		
		
	Use a unique socket for the drawing system
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
		| @@ -42,8 +42,60 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         We accept only if this is a user of a team of the associated tournament, or a volunteer | ||||
|         of the tournament. | ||||
|         """ | ||||
|         # Get the tournament from the URL | ||||
|         self.tournament_id = self.scope['url_route']['kwargs']['tournament_id'] | ||||
|  | ||||
|         # Fetch the registration of the current user | ||||
|         user = self.scope['user'] | ||||
|         reg = await Registration.objects.aget(user=user) | ||||
|         self.registration = reg | ||||
|  | ||||
|         # Accept the connection | ||||
|         await self.accept() | ||||
|  | ||||
|         # Register to channel layers to get updates | ||||
|         if self.registration.participates: | ||||
|             await self.channel_layer.group_add(f"team-{self.registration.team.trigram}", self.channel_name) | ||||
|             participation = reg.team.participation | ||||
|             if participation.valid: | ||||
|                 await self.channel_layer.group_add(f"tournament-{participation.tournament.id}", self.channel_name) | ||||
|         else: | ||||
|             tids = [t.id async for t in Tournament.objects.all()] \ | ||||
|                 if reg.is_admin else [t.id for t in reg.interesting_tournaments] | ||||
|             for tid in tids: | ||||
|                 await self.channel_layer.group_add(f"tournament-{tid}", self.channel_name) | ||||
|                 await self.channel_layer.group_add(f"volunteer-{tid}", self.channel_name) | ||||
|  | ||||
|     async def disconnect(self, close_code) -> None: | ||||
|         """ | ||||
|         Called when the websocket got disconnected, for any reason. | ||||
|         :param close_code: The error code. | ||||
|         """ | ||||
|         # Unregister from channel layers | ||||
|         if not self.registration.is_volunteer: | ||||
|             await self.channel_layer.group_discard(f"team-{self.registration.team.trigram}", self.channel_name) | ||||
|             participation = self.registration.team.participation | ||||
|             await self.channel_layer.group_discard(f"tournament-{participation.tournament.id}", self.channel_name) | ||||
|         else: | ||||
|             async for tournament in Tournament.objects.all(): | ||||
|                 await self.channel_layer.group_discard(f"tournament-{tournament.id}", self.channel_name) | ||||
|                 await self.channel_layer.group_discard(f"volunteer-{tournament.id}", self.channel_name) | ||||
|  | ||||
|     async def alert(self, message: str, alert_type: str = 'info', tid: int = -1, **kwargs): | ||||
|         """ | ||||
|         Send an alert message to the current user. | ||||
|         :param message: The body of the alert. | ||||
|         :param alert_type: The type of the alert, which is a bootstrap color (success, warning, info, danger,…) | ||||
|         :param tid: The tournament id. Default to -1, the current tournament. | ||||
|         """ | ||||
|         tid = tid if tid > 0 else self.tournament_id | ||||
|         return await self.send_json({'tid': tid, 'type': 'alert', 'alert_type': alert_type, 'message': str(message)}) | ||||
|  | ||||
|     async def receive_json(self, content, **kwargs): | ||||
|         """ | ||||
|         Called when the client sends us some data, parsed as JSON. | ||||
|         :param content: The sent data, decoded from JSON text. Must content a `type` field. | ||||
|         """ | ||||
|         # Get the tournament from the message | ||||
|         self.tournament_id = content['tid'] | ||||
|         self.tournament = await Tournament.objects.filter(pk=self.tournament_id) \ | ||||
|             .prefetch_related('draw__current_round__current_pool__current_team__participation__team').aget() | ||||
|  | ||||
| @@ -52,51 +104,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team'): | ||||
|             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() | ||||
| @@ -176,7 +183,9 @@ 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}", | ||||
|                                                 {'type': 'draw.send_poules', | ||||
|                                                 { | ||||
|                                                     'tid': self.tournament_id, | ||||
|                                                     'type': 'draw.send_poules', | ||||
|                                                     'round': r.number, | ||||
|                                                     'poules': [ | ||||
|                                                         { | ||||
| @@ -184,29 +193,32 @@ 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}", | ||||
|                                             {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, '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}", | ||||
|                                             {'type': 'draw.start', 'fmt': fmt, 'draw': draw}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.start', 'fmt': fmt, 'draw': draw}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', 'round': 1}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', 'round': 1}) | ||||
|  | ||||
|         # Send notification to everyone | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.notify', | ||||
|                                              'title': 'Tirage au sort du TFJM²', | ||||
|                                              'body': "Le tirage au sort du tournoi de " | ||||
|                                                      f"{self.tournament.name} a commencé !"}) | ||||
|  | ||||
| @@ -216,7 +228,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({'type': 'draw_start', 'fmt': content['fmt'], | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'draw_start', 'fmt': content['fmt'], | ||||
|                               'trigrams': [p.team.trigram for p in self.participations]}) | ||||
|  | ||||
|     @ensure_orga | ||||
| @@ -231,7 +243,8 @@ 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}", {'type': 'draw_abort'}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw_abort'}) | ||||
|  | ||||
|     async def draw_abort(self, content) -> None: | ||||
|         """ | ||||
| @@ -239,7 +252,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({'type': 'abort'}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'abort'}) | ||||
|  | ||||
|     async def process_dice(self, trigram: str | None = None, **kwargs): | ||||
|         """ | ||||
| @@ -310,7 +323,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|         # Send the dice result to all users | ||||
|         await self.channel_layer.group_send( | ||||
|             f"tournament-{self.tournament.id}", {'type': 'draw.dice', 'team': trigram, 'result': res}) | ||||
|             f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice', | ||||
|                                                  'team': trigram, 'result': res}) | ||||
|  | ||||
|         if state == 'DICE_SELECT_POULES' and \ | ||||
|                 not await TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id, | ||||
| @@ -364,19 +378,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|                     await dup.asave() | ||||
|                     await self.channel_layer.group_send( | ||||
|                         f"tournament-{self.tournament.id}", | ||||
|                         {'type': 'draw.dice', 'team': dup.participation.team.trigram, 'result': None}) | ||||
|                         {'tid': self.tournament_id, 'type': 'draw.dice', | ||||
|                          'team': dup.participation.team.trigram, 'result': None}) | ||||
|  | ||||
|                     # Send notification to concerned teams | ||||
|                     await self.channel_layer.group_send( | ||||
|                         f"team-{dup.participation.team.trigram}", | ||||
|                         {'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²', | ||||
|                         {'tid': self.tournament_id, 'type': 'draw.notify', 'title': 'Tirage au sort du TFJM²', | ||||
|                          'body': 'Votre score de dé est identique à celui de une ou plusieurs équipes. ' | ||||
|                                  'Veuillez le relancer.'} | ||||
|                     ) | ||||
|                 # Alert the tournament | ||||
|                 await self.channel_layer.group_send( | ||||
|                     f"tournament-{self.tournament.id}", | ||||
|                     {'type': 'draw.alert', | ||||
|                     {'tid': self.tournament_id, 'type': 'draw.alert', | ||||
|                      'message': _('Dices from teams {teams} are identical. Please relaunch your dices.').format( | ||||
|                          teams=', '.join(td.participation.team.trigram for td in dups)), | ||||
|                      'alert_type': 'warning'}) | ||||
| @@ -452,23 +467,26 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         for td in tds: | ||||
|             await self.channel_layer.group_send( | ||||
|                 f"tournament-{self.tournament.id}", | ||||
|                 {'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None}) | ||||
|                 {'tid': self.tournament_id, 'type': 'draw.dice', 'team': td.participation.team.trigram, 'result': None}) | ||||
|  | ||||
|         # Hide dice interface | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.dice_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                              'visible': False}) | ||||
|  | ||||
|         # Display dice interface only for the teams in the first pool, and for volunteers | ||||
|         async for td in pool.teamdraw_set.prefetch_related('participation__team').all(): | ||||
|             await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                                 {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                  'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                              'visible': True}) | ||||
|  | ||||
|         # First send the second pool to have the good team order | ||||
|         r2 = await self.tournament.draw.round_set.filter(number=2).aget() | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.send_poules', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.send_poules', | ||||
|                                              'round': r2.number, | ||||
|                                              'poules': [ | ||||
|                                                  { | ||||
| @@ -478,7 +496,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}", | ||||
|                                             {'type': 'draw.send_poules', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.send_poules', | ||||
|                                              'round': r.number, | ||||
|                                              'poules': [ | ||||
|                                                  { | ||||
| @@ -490,10 +508,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}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', | ||||
|                                              'round': r.number, | ||||
|                                              'pool': pool.get_letter_display()}) | ||||
|  | ||||
| @@ -521,28 +539,30 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|         # Update information header | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', | ||||
|                                              'round': r.number, | ||||
|                                              '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}", | ||||
|                                             {'type': 'draw.dice_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                              'visible': False}) | ||||
|  | ||||
|         # Display the box button to the first team and to volunteers | ||||
|         trigram = pool.current_team.participation.team.trigram | ||||
|         await self.channel_layer.group_send(f"team-{trigram}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True}) | ||||
|  | ||||
|         # Notify the team that it can draw a problem | ||||
|         await self.channel_layer.group_send(f"team-{tds[0].participation.team.trigram}", | ||||
|                                             {'type': 'draw.notify', 'title': "À votre tour !", | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.notify', | ||||
|                                              'title': "À votre tour !", | ||||
|                                              'body': "C'est à vous de tirer un nouveau problème !"}) | ||||
|  | ||||
|     async def select_problem(self, **kwargs): | ||||
| @@ -585,20 +605,25 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         # Update interface | ||||
|         trigram = td.participation.team.trigram | ||||
|         await self.channel_layer.group_send(f"team-{trigram}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"team-{trigram}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': True}) | ||||
|         await self.channel_layer.group_send(f"team-{self.tournament.id}", | ||||
|                                             {'type': 'draw.draw_problem', 'team': trigram, 'problem': problem}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.draw_problem', 'team': trigram, | ||||
|                                              'problem': problem}) | ||||
|  | ||||
|         self.tournament.draw.last_message = "" | ||||
|         await self.tournament.draw.asave() | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|  | ||||
|     async def accept_problem(self, **kwargs): | ||||
| @@ -641,11 +666,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|         # Send the accepted problem to the users | ||||
|         await self.channel_layer.group_send(f"team-{trigram}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_problem', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_problem', | ||||
|                                              'round': r.number, | ||||
|                                              'team': trigram, | ||||
|                                              'problem': td.accepted}) | ||||
| @@ -659,13 +686,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|             new_trigram = next_td.participation.team.trigram | ||||
|             await self.channel_layer.group_send(f"team-{new_trigram}", | ||||
|                                                 {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.box_visibility', | ||||
|                                                  'visible': True}) | ||||
|             await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                 {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.box_visibility', | ||||
|                                                  'visible': True}) | ||||
|  | ||||
|             # Notify the team that it can draw a problem | ||||
|             await self.channel_layer.group_send(f"team-{new_trigram}", | ||||
|                                                 {'type': 'draw.notify', 'title': "À votre tour !", | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.notify', | ||||
|                                                  'title': "À votre tour !", | ||||
|                                                  'body': "C'est à vous de tirer un nouveau problème !"}) | ||||
|         else: | ||||
|             # Pool is ended | ||||
| @@ -674,10 +704,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|             pool = r.current_pool | ||||
|  | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', | ||||
|                                              'round': r.number, | ||||
|                                              'pool': pool.get_letter_display(), | ||||
|                                              'team': pool.current_team.participation.team.trigram | ||||
| @@ -715,6 +745,7 @@ 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(), | ||||
| @@ -736,14 +767,17 @@ 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}", | ||||
|                                                     {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                      'visible': True}) | ||||
|                 # Notify the team that it can draw a dice | ||||
|                 await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                                     {'type': 'draw.notify', 'title': "À votre tour !", | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.notify', | ||||
|                                                      'title': "À votre tour !", | ||||
|                                                      'body': "C'est à vous de lancer le dé !"}) | ||||
|  | ||||
|             await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                 {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                  'visible': True}) | ||||
|         else: | ||||
|             # Round is ended | ||||
|             await self.end_round(r) | ||||
| @@ -766,16 +800,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|             for participation in self.participations: | ||||
|                 await self.channel_layer.group_send( | ||||
|                     f"tournament-{self.tournament.id}", | ||||
|                     {'type': 'draw.dice', 'team': participation.team.trigram, 'result': None}) | ||||
|                     {'tid': self.tournament_id, '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}", | ||||
|                                                     {'type': 'draw.notify', 'title': "À votre tour !", | ||||
|                                                     {'tid': self.tournament_id, '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}", | ||||
|                                                 {'type': 'draw.send_poules', | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.send_poules', | ||||
|                                                  'round': r2.number, | ||||
|                                                  'poules': [ | ||||
|                                                      { | ||||
| @@ -793,9 +829,11 @@ 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}", | ||||
|                                                     {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                      'visible': True}) | ||||
|             await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                 {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                  'visible': True}) | ||||
|         elif r.number == 1 and self.tournament.final: | ||||
|             # For the final tournament, we wait for a manual update between the two rounds. | ||||
|             msg += "<br><br>Le tirage au sort du tour 1 est terminé." | ||||
| @@ -803,7 +841,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|             await self.tournament.draw.asave() | ||||
|  | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.export_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.export_visibility', | ||||
|                                              'visible': True}) | ||||
|  | ||||
|     async def reject_problem(self, **kwargs): | ||||
|         """ | ||||
| @@ -854,11 +893,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|         # Update interface | ||||
|         await self.channel_layer.group_send(f"team-{trigram}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.reject_problem', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.reject_problem', | ||||
|                                              'round': r.number, 'team': trigram, 'rejected': td.rejected}) | ||||
|  | ||||
|         if already_refused: | ||||
| @@ -873,22 +914,23 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|         new_trigram = next_td.participation.team.trigram | ||||
|         await self.channel_layer.group_send(f"team-{new_trigram}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True}) | ||||
|  | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', | ||||
|                                              'round': r.number, | ||||
|                                              '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}", | ||||
|                                             {'type': 'draw.notify', 'title': "À votre tour !", | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.notify', | ||||
|                                              'title': "À votre tour !", | ||||
|                                              'body': "C'est à vous de tirer un nouveau problème !"}) | ||||
|  | ||||
|     @ensure_orga | ||||
| @@ -906,7 +948,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|                     await pool.export() | ||||
|  | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.export_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.export_visibility', | ||||
|                                              'visible': False}) | ||||
|  | ||||
|     @ensure_orga | ||||
|     async def continue_final(self, **kwargs): | ||||
| @@ -951,7 +994,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|         # Send pools to users | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.send_poules', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.send_poules', | ||||
|                                              'round': r2.number, | ||||
|                                              'poules': [ | ||||
|                                                  { | ||||
| @@ -965,27 +1008,31 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         for participation in self.participations: | ||||
|             await self.channel_layer.group_send( | ||||
|                 f"tournament-{self.tournament.id}", | ||||
|                 {'type': 'draw.dice', 'team': participation.team.trigram, 'result': None}) | ||||
|                 {'tid': self.tournament_id, 'type': 'draw.dice', 'team': participation.team.trigram, 'result': None}) | ||||
|  | ||||
|         async for td in r2.current_pool.team_draws.prefetch_related('participation__team'): | ||||
|             await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                                 {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                  'visible': True}) | ||||
|  | ||||
|             # Notify the team that it can draw a problem | ||||
|             await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                                 {'type': 'draw.notify', 'title': "À votre tour !", | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.notify', | ||||
|                                                  'title': "À votre tour !", | ||||
|                                                  'body': "C'est à vous de tirer un nouveau problème !"}) | ||||
|  | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                              'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.continue_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.continue_visibility', | ||||
|                                              'visible': False}) | ||||
|  | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', | ||||
|                                              'round': r2.number, | ||||
|                                              'pool': r2.current_pool.get_letter_display()}) | ||||
|  | ||||
| @@ -1014,12 +1061,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|             await self.undo_order_dice() | ||||
|  | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_info', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_info', | ||||
|                                              'info': await self.tournament.draw.ainformation()}) | ||||
|         r = self.tournament.draw.current_round | ||||
|         p = r.current_pool | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_active', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_active', | ||||
|                                              'round': r.number, | ||||
|                                              'pool': p.get_letter_display() if p else None, | ||||
|                                              'team': p.current_team.participation.team.trigram | ||||
| @@ -1037,15 +1084,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         await td.asave() | ||||
|  | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.continue_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.continue_visibility', | ||||
|                                              'visible': False}) | ||||
|  | ||||
|         await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': True}) | ||||
|  | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.set_problem', | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.set_problem', | ||||
|                                              'round': r.number, | ||||
|                                              'team': td.participation.team.trigram, | ||||
|                                              'problem': td.accepted}) | ||||
| @@ -1061,13 +1111,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         await td.asave() | ||||
|  | ||||
|         await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.buttons_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                              'visible': False}) | ||||
|         await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True}) | ||||
|         await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': True}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', 'visible': True}) | ||||
|  | ||||
|     async def undo_process_problem(self): | ||||
|         """ | ||||
| @@ -1115,7 +1167,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|                     await last_td.asave() | ||||
|  | ||||
|                     await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                         {'type': 'draw.set_problem', | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.set_problem', | ||||
|                                                          'round': r.number, | ||||
|                                                          'team': last_td.participation.team.trigram, | ||||
|                                                          'problem': last_td.accepted}) | ||||
| @@ -1133,7 +1185,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|                     await last_td.asave() | ||||
|  | ||||
|                     await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                         {'type': 'draw.reject_problem', | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.reject_problem', | ||||
|                                                          'round': r.number, | ||||
|                                                          'team': last_td.participation.team.trigram, | ||||
|                                                          'rejected': last_td.rejected}) | ||||
| @@ -1143,9 +1195,11 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|             await r.current_pool.asave() | ||||
|  | ||||
|             await self.channel_layer.group_send(f"team-{last_td.participation.team.trigram}", | ||||
|                                                 {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                                  'visible': True}) | ||||
|             await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                 {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, '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')} | ||||
| @@ -1166,7 +1220,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|                     # Reset the dice on the interface | ||||
|                     await self.channel_layer.group_send( | ||||
|                         f"tournament-{self.tournament.id}", {'type': 'draw.dice', | ||||
|                         f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice', | ||||
|                                                              'team': last_td.participation.team.trigram, | ||||
|                                                              'result': None}) | ||||
|                     break | ||||
| @@ -1177,12 +1231,15 @@ 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}", | ||||
|                                                     {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                      'visible': True}) | ||||
|             await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                 {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                 {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                  'visible': True}) | ||||
|  | ||||
|         await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                             {'type': 'draw.box_visibility', 'visible': False}) | ||||
|                                             {'tid': self.tournament_id, 'type': 'draw.box_visibility', | ||||
|                                              'visible': False}) | ||||
|  | ||||
|     async def undo_pool_dice(self): | ||||
|         """ | ||||
| @@ -1217,7 +1274,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|                     # Reset the dice on the interface | ||||
|                     await self.channel_layer.group_send( | ||||
|                         f"tournament-{self.tournament.id}", {'type': 'draw.dice', | ||||
|                         f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice', | ||||
|                                                              'team': last_td.participation.team.trigram, | ||||
|                                                              'result': None}) | ||||
|                     break | ||||
| @@ -1236,15 +1293,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|                 await td.asave() | ||||
|  | ||||
|                 await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                     {'type': 'draw.dice_visibility', 'visible': False}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                      'visible': False}) | ||||
|  | ||||
|                 await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                                     {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                                      'visible': True}) | ||||
|                 await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                     {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                                      'visible': True}) | ||||
|  | ||||
|                 await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                     {'type': 'draw.set_problem', | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.set_problem', | ||||
|                                                      'round': r.number, | ||||
|                                                      'team': td.participation.team.trigram, | ||||
|                                                      'problem': td.accepted}) | ||||
| @@ -1258,12 +1318,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}", {'type': 'draw.dice', | ||||
|                             f"tournament-{self.tournament.id}", {'tid': 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}", | ||||
|                                                         {'type': 'draw.send_poules', | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.send_poules', | ||||
|                                                          'round': r1.number, | ||||
|                                                          'poules': [ | ||||
|                                                              { | ||||
| @@ -1281,15 +1341,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|                     await td.asave() | ||||
|  | ||||
|                     await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                         {'type': 'draw.dice_visibility', 'visible': False}) | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                          'visible': False}) | ||||
|  | ||||
|                     await self.channel_layer.group_send(f"team-{td.participation.team.trigram}", | ||||
|                                                         {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                                          'visible': True}) | ||||
|                     await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                         {'type': 'draw.buttons_visibility', 'visible': True}) | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.buttons_visibility', | ||||
|                                                          'visible': True}) | ||||
|  | ||||
|                     await self.channel_layer.group_send(f"tournament-{self.tournament.id}", | ||||
|                                                         {'type': 'draw.set_problem', | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.set_problem', | ||||
|                                                          'round': r1.number, | ||||
|                                                          'team': td.participation.team.trigram, | ||||
|                                                          'problem': td.accepted}) | ||||
| @@ -1308,14 +1371,16 @@ 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}", {'type': 'draw.dice', | ||||
|                             f"tournament-{self.tournament.id}", {'tid': 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}", | ||||
|                                                         {'type': 'draw.dice_visibility', 'visible': False}) | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                          'visible': False}) | ||||
|                     await self.channel_layer.group_send(f"volunteer-{self.tournament.id}", | ||||
|                                                         {'type': 'draw.continue_visibility', 'visible': True}) | ||||
|                                                         {'tid': self.tournament_id, 'type': 'draw.continue_visibility', | ||||
|                                                          'visible': True}) | ||||
|             else: | ||||
|                 # Go to the dice order | ||||
|                 async for r0 in self.tournament.draw.round_set.all(): | ||||
| @@ -1349,19 +1414,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|                         # Reset the dice on the interface | ||||
|                         await self.channel_layer.group_send( | ||||
|                             f"tournament-{self.tournament.id}", {'type': 'draw.dice', | ||||
|                             f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice', | ||||
|                                                                  'team': last_td.participation.team.trigram, | ||||
|                                                                  '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}", {'type': 'draw.dice', | ||||
|                         f"tournament-{self.tournament.id}", {'tid': 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}", | ||||
|                                                     {'type': 'draw.dice_visibility', 'visible': True}) | ||||
|                                                     {'tid': self.tournament_id, 'type': 'draw.dice_visibility', | ||||
|                                                      'visible': True}) | ||||
|  | ||||
|     async def undo_order_dice(self): | ||||
|         """ | ||||
| @@ -1393,7 +1459,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|  | ||||
|                     # Reset the dice on the interface | ||||
|                     await self.channel_layer.group_send( | ||||
|                         f"tournament-{self.tournament.id}", {'type': 'draw.dice', | ||||
|                         f"tournament-{self.tournament.id}", {'tid': self.tournament_id, 'type': 'draw.dice', | ||||
|                                                              'team': last_td.participation.team.trigram, | ||||
|                                                              'result': None}) | ||||
|                     break | ||||
| @@ -1410,55 +1476,57 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         """ | ||||
|         Send a notification (with title and body) to the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'notification', 'title': content['title'], 'body': content['body']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'notification', | ||||
|                               'title': content['title'], 'body': content['body']}) | ||||
|  | ||||
|     async def draw_set_info(self, content): | ||||
|         """ | ||||
|         Set the information banner to the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'set_info', 'information': content['info']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'set_info', 'information': content['info']}) | ||||
|  | ||||
|     async def draw_dice(self, content): | ||||
|         """ | ||||
|         Update the dice of a given team for the current user interface. | ||||
|         """ | ||||
|         await self.send_json({'type': 'dice', 'team': content['team'], 'result': content['result']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'dice', | ||||
|                               'team': content['team'], 'result': content['result']}) | ||||
|  | ||||
|     async def draw_dice_visibility(self, content): | ||||
|         """ | ||||
|         Update the visibility of the dice button for the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'dice_visibility', 'visible': content['visible']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'dice_visibility', 'visible': content['visible']}) | ||||
|  | ||||
|     async def draw_box_visibility(self, content): | ||||
|         """ | ||||
|         Update the visibility of the box button for the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'box_visibility', 'visible': content['visible']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'box_visibility', 'visible': content['visible']}) | ||||
|  | ||||
|     async def draw_buttons_visibility(self, content): | ||||
|         """ | ||||
|         Update the visibility of the accept/reject buttons for the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'buttons_visibility', 'visible': content['visible']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'buttons_visibility', 'visible': content['visible']}) | ||||
|  | ||||
|     async def draw_export_visibility(self, content): | ||||
|         """ | ||||
|         Update the visibility of the export button for the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'export_visibility', 'visible': content['visible']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'export_visibility', 'visible': content['visible']}) | ||||
|  | ||||
|     async def draw_continue_visibility(self, content): | ||||
|         """ | ||||
|         Update the visibility of the continue button for the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'continue_visibility', 'visible': content['visible']}) | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'continue_visibility', 'visible': content['visible']}) | ||||
|  | ||||
|     async def draw_send_poules(self, content): | ||||
|         """ | ||||
|         Send the pools and the teams to the current user to update the interface. | ||||
|         """ | ||||
|         await self.send_json({'type': 'set_poules', 'round': content['round'], | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'set_poules', 'round': content['round'], | ||||
|                               'poules': content['poules']}) | ||||
|  | ||||
|     async def draw_set_active(self, content): | ||||
| @@ -1466,6 +1534,7 @@ 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), | ||||
| @@ -1476,20 +1545,20 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): | ||||
|         """ | ||||
|         Send the accepted problem of a team to the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'set_problem', 'round': content['round'], | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'set_problem', 'round': content['round'], | ||||
|                               'team': content['team'], 'problem': content['problem']}) | ||||
|  | ||||
|     async def draw_reject_problem(self, content): | ||||
|         """ | ||||
|         Send the rejected problems of a team to the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'reject_problem', 'round': content['round'], | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'reject_problem', 'round': content['round'], | ||||
|                               'team': content['team'], 'rejected': content['rejected']}) | ||||
|  | ||||
|     async def draw_reorder_pool(self, content): | ||||
|         """ | ||||
|         Send the new order of a pool to the current user. | ||||
|         """ | ||||
|         await self.send_json({'type': 'reorder_poule', 'round': content['round'], | ||||
|         await self.send_json({'tid': content['tid'], 'type': 'reorder_poule', 'round': content['round'], | ||||
|                               'poule': content['pool'], 'teams': content['teams'], | ||||
|                               'problems': content['problems']}) | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| # 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/<int:tournament_id>/", consumers.DrawConsumer.as_asgi()), | ||||
|     path("ws/draw/", consumers.DrawConsumer.as_asgi()), | ||||
| ] | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| const problems_count = JSON.parse(document.getElementById('problems_count').textContent) | ||||
|  | ||||
| const tournaments = JSON.parse(document.getElementById('tournaments_list').textContent) | ||||
| const sockets = {} | ||||
| let socket = null | ||||
|  | ||||
| const messages = document.getElementById('messages') | ||||
|  | ||||
| @@ -17,7 +17,7 @@ const messages = document.getElementById('messages') | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function abortDraw(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'abort'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'abort'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -26,7 +26,7 @@ function abortDraw(tid) { | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function cancelLastStep(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'cancel'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'cancel'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -36,7 +36,7 @@ function cancelLastStep(tid) { | ||||
|  * @param trigram The trigram of the team that a volunteer wants to force the dice launch (default: null) | ||||
|  */ | ||||
| function drawDice(tid, trigram = null) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'dice', 'trigram': trigram})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'dice', 'trigram': trigram})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -44,7 +44,7 @@ function drawDice(tid, trigram = null) { | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function drawProblem(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'draw_problem'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'draw_problem'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -52,7 +52,7 @@ function drawProblem(tid) { | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function acceptProblem(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'accept'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'accept'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -60,7 +60,7 @@ function acceptProblem(tid) { | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function rejectProblem(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'reject'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'reject'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -68,7 +68,7 @@ function rejectProblem(tid) { | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function exportDraw(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'export'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'export'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -76,7 +76,7 @@ function exportDraw(tid) { | ||||
|  * @param tid The tournament id | ||||
|  */ | ||||
| function continueFinal(tid) { | ||||
|     sockets[tid].send(JSON.stringify({'type': 'continue_final'})) | ||||
|     socket.send(JSON.stringify({'tid': tid, 'type': 'continue_final'})) | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -108,13 +108,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|         elem => elem.addEventListener( | ||||
|             'click', () => document.location.hash = '#' + elem.innerText.toLowerCase())) | ||||
|  | ||||
|     for (let tournament of tournaments) { | ||||
|         // Open a websocket per tournament | ||||
|         let socket = new WebSocket( | ||||
|     // Open a global websocket | ||||
|     socket = new WebSocket( | ||||
|         (document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host | ||||
|             + '/ws/draw/' + tournament.id + '/' | ||||
|         + '/ws/draw/' | ||||
|     ) | ||||
|         sockets[tournament.id] = socket | ||||
|  | ||||
|     /** | ||||
|      * Add alert message on the top on the interface. | ||||
| @@ -137,23 +135,25 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Update the information banner. | ||||
|      * @param tid The tournament id | ||||
|      * @param info The content to updated | ||||
|      */ | ||||
|         function setInfo(info) { | ||||
|             document.getElementById(`messages-${tournament.id}`).innerHTML = info | ||||
|     function setInfo(tid, info) { | ||||
|         document.getElementById(`messages-${tid}`).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(teams) { | ||||
|     function drawStart(tid, teams) { | ||||
|         // Hide the not-started-banner | ||||
|             document.getElementById(`banner-not-started-${tournament.id}`).classList.add('d-none') | ||||
|         document.getElementById(`banner-not-started-${tid}`).classList.add('d-none') | ||||
|         // Display the full draw interface | ||||
|             document.getElementById(`draw-content-${tournament.id}`).classList.remove('d-none') | ||||
|         document.getElementById(`draw-content-${tid}`).classList.remove('d-none') | ||||
|  | ||||
|             let dicesDiv = document.getElementById(`dices-${tournament.id}`) | ||||
|         let dicesDiv = document.getElementById(`dices-${tid}`) | ||||
|         for (let team of teams) { | ||||
|             // Add empty dice score badge for each team | ||||
|             let col = document.createElement('div') | ||||
| @@ -161,11 +161,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|             dicesDiv.append(col) | ||||
|  | ||||
|             let diceDiv = document.createElement('div') | ||||
|                 diceDiv.id = `dice-${tournament.id}-${team}` | ||||
|             diceDiv.id = `dice-${tid}-${team}` | ||||
|             diceDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning') | ||||
|                 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) | ||||
|             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) | ||||
|             } | ||||
|             diceDiv.textContent = `${team} 🎲 ??` | ||||
|             col.append(diceDiv) | ||||
| @@ -174,28 +174,30 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Abort the current draw, and make all invisible, except the not-started-banner. | ||||
|      * @param tid The tournament id | ||||
|      */ | ||||
|         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) | ||||
|     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) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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(trigram, result) { | ||||
|             let elem = document.getElementById(`dice-${tournament.id}-${trigram}`) | ||||
|     function updateDiceInfo(tid, trigram, result) { | ||||
|         let elem = document.getElementById(`dice-${tid}-${trigram}`) | ||||
|         if (result === null) { | ||||
|             elem.classList.remove('text-bg-success') | ||||
|             elem.classList.add('text-bg-warning') | ||||
| @@ -210,10 +212,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Display or hide the dice button. | ||||
|      * @param tid The tournament id | ||||
|      * @param visible The visibility status | ||||
|      */ | ||||
|         function updateDiceVisibility(visible) { | ||||
|             let div = document.getElementById(`launch-dice-${tournament.id}`) | ||||
|     function updateDiceVisibility(tid, visible) { | ||||
|         let div = document.getElementById(`launch-dice-${tid}`) | ||||
|         if (visible) | ||||
|             div.classList.remove('d-none') | ||||
|         else | ||||
| @@ -222,10 +225,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Display or hide the box button. | ||||
|      * @param tid The tournament id | ||||
|      * @param visible The visibility status | ||||
|      */ | ||||
|         function updateBoxVisibility(visible) { | ||||
|             let div = document.getElementById(`draw-problem-${tournament.id}`) | ||||
|     function updateBoxVisibility(tid, visible) { | ||||
|         let div = document.getElementById(`draw-problem-${tid}`) | ||||
|         if (visible) | ||||
|             div.classList.remove('d-none') | ||||
|         else | ||||
| @@ -234,10 +238,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Display or hide the accept and reject buttons. | ||||
|      * @param tid The tournament id | ||||
|      * @param visible The visibility status | ||||
|      */ | ||||
|         function updateButtonsVisibility(visible) { | ||||
|             let div = document.getElementById(`buttons-${tournament.id}`) | ||||
|     function updateButtonsVisibility(tid, visible) { | ||||
|         let div = document.getElementById(`buttons-${tid}`) | ||||
|         if (visible) | ||||
|             div.classList.remove('d-none') | ||||
|         else | ||||
| @@ -246,10 +251,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Display or hide the export button. | ||||
|      * @param tid The tournament id | ||||
|      * @param visible The visibility status | ||||
|      */ | ||||
|         function updateExportVisibility(visible) { | ||||
|             let div = document.getElementById(`export-${tournament.id}`) | ||||
|     function updateExportVisibility(tid, visible) { | ||||
|         let div = document.getElementById(`export-${tid}`) | ||||
|         if (visible) | ||||
|             div.classList.remove('d-none') | ||||
|         else | ||||
| @@ -258,32 +264,37 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|  | ||||
|     /** | ||||
|      * Display or hide the continuation button. | ||||
|      * @param tid The tournament id | ||||
|      * @param visible The visibility status | ||||
|      */ | ||||
|         function updateContinueVisibility(visible) { | ||||
|             let div = document.getElementById(`continue-${tournament.id}`) | ||||
|     function updateContinueVisibility(tid, visible) { | ||||
|         let div = document.getElementById(`continue-${tid}`) | ||||
|         if (div !== null) { | ||||
|             // Only present during the final | ||||
|             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(round, poules) { | ||||
|             let roundList = document.getElementById(`recap-${tournament.id}-round-list`) | ||||
|             let poolListId = `recap-${tournament.id}-round-${round}-pool-list` | ||||
|     function updatePoules(tid, round, poules) { | ||||
|         let roundList = document.getElementById(`recap-${tid}-round-list`) | ||||
|         let poolListId = `recap-${tid}-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-${tournament.id}-round-${round}` | ||||
|             div.id = `recap-${tid}-round-${round}` | ||||
|             div.classList.add('col-md-6', 'px-3', 'py-3') | ||||
|                 div.setAttribute('data-tournament', tournament.id) | ||||
|             div.setAttribute('data-tournament', tid) | ||||
|  | ||||
|             let title = document.createElement('strong') | ||||
|             title.textContent = 'Tour ' + round | ||||
| @@ -299,14 +310,14 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|         let c = 1 | ||||
|  | ||||
|         for (let poule of poules) { | ||||
|                 let teamListId = `recap-${tournament.id}-round-${round}-pool-${poule.letter}-team-list` | ||||
|             let teamListId = `recap-${tid}-round-${round}-pool-${poule.letter}-team-list` | ||||
|             let teamList = document.getElementById(teamListId) | ||||
|             if (teamList === null) { | ||||
|                 // Add a div for the pool in the recap div | ||||
|                 let li = document.createElement('li') | ||||
|                     li.id = `recap-${tournament.id}-round-${round}-pool-${poule.letter}` | ||||
|                 li.id = `recap-${tid}-round-${round}-pool-${poule.letter}` | ||||
|                 li.classList.add('list-group-item', 'px-3', 'py-3') | ||||
|                     li.setAttribute('data-tournament', tournament.id) | ||||
|                 li.setAttribute('data-tournament', tid) | ||||
|  | ||||
|                 let title = document.createElement('strong') | ||||
|                 title.textContent = 'Poule ' + poule.letter + round | ||||
| @@ -323,11 +334,11 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                 // The pool is initialized | ||||
|                 for (let team of poule.teams) { | ||||
|                     // Reorder dices | ||||
|                         let diceDiv = document.getElementById(`dice-${tournament.id}-${team}`) | ||||
|                     let diceDiv = document.getElementById(`dice-${tid}-${team}`) | ||||
|                     diceDiv.parentElement.style.order = c.toString() | ||||
|                     c += 1 | ||||
|  | ||||
|                         let teamLiId = `recap-${tournament.id}-round-${round}-team-${team}` | ||||
|                     let teamLiId = `recap-${tid}-round-${round}-team-${team}` | ||||
|                     let teamLi = document.getElementById(teamLiId) | ||||
|  | ||||
|                     if (teamLi === null) { | ||||
| @@ -335,13 +346,13 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                         teamLi = document.createElement('li') | ||||
|                         teamLi.id = teamLiId | ||||
|                         teamLi.classList.add('list-group-item') | ||||
|                             teamLi.setAttribute('data-tournament', tournament.id) | ||||
|                         teamLi.setAttribute('data-tournament', tid) | ||||
|  | ||||
|                         teamList.append(teamLi) | ||||
|                     } | ||||
|  | ||||
|                     // Add the accepted problem div (empty for now) | ||||
|                         let acceptedDivId = `recap-${tournament.id}-round-${round}-team-${team}-accepted` | ||||
|                     let acceptedDivId = `recap-${tid}-round-${round}-team-${team}-accepted` | ||||
|                     let acceptedDiv = document.getElementById(acceptedDivId) | ||||
|                     if (acceptedDiv === null) { | ||||
|                         acceptedDiv = document.createElement('div') | ||||
| @@ -352,7 +363,7 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                     } | ||||
|  | ||||
|                     // Add the rejected problems div (empty for now) | ||||
|                         let rejectedDivId = `recap-${tournament.id}-round-${round}-team-${team}-rejected` | ||||
|                     let rejectedDivId = `recap-${tid}-round-${round}-team-${team}-rejected` | ||||
|                     let rejectedDiv = document.getElementById(rejectedDivId) | ||||
|                     if (rejectedDiv === null) { | ||||
|                         rejectedDiv = document.createElement('div') | ||||
| @@ -365,8 +376,8 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|             } | ||||
|  | ||||
|             // Draw tables | ||||
|                 let tablesDiv = document.getElementById(`tables-${tournament.id}`) | ||||
|                 let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`) | ||||
|             let tablesDiv = document.getElementById(`tables-${tid}`) | ||||
|             let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`) | ||||
|             if (tablesRoundDiv === null) { | ||||
|                 // Add the tables div for the current round if necessary | ||||
|                 let card = document.createElement('div') | ||||
| @@ -379,7 +390,7 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                 card.append(cardHeader) | ||||
|  | ||||
|                 tablesRoundDiv = document.createElement('div') | ||||
|                     tablesRoundDiv.id = `tables-${tournament.id}-round-${round}` | ||||
|                 tablesRoundDiv.id = `tables-${tid}-round-${round}` | ||||
|                 tablesRoundDiv.classList.add('card-body', 'd-flex', 'flex-wrap') | ||||
|                 card.append(tablesRoundDiv) | ||||
|             } | ||||
| @@ -389,20 +400,21 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                     continue | ||||
|  | ||||
|                 // Display the table for the pool | ||||
|                     updatePouleTable(round, poule) | ||||
|                 updatePouleTable(tid, round, poule) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the table for the given round and the given pool, where there will be the chosen problems. | ||||
|      * @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(round, poule) { | ||||
|             let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`) | ||||
|             let pouleTable = document.getElementById(`table-${tournament.id}-${round}-${poule.letter}`) | ||||
|     function updatePouleTable(tid, round, poule) { | ||||
|         let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`) | ||||
|         let pouleTable = document.getElementById(`table-${tid}-${round}-${poule.letter}`) | ||||
|         if (pouleTable === null) { | ||||
|             // Create table | ||||
|             let card = document.createElement('div') | ||||
| @@ -419,7 +431,7 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|             card.append(cardBody) | ||||
|  | ||||
|             pouleTable = document.createElement('table') | ||||
|                 pouleTable.id = `table-${tournament.id}-${round}-${poule.letter}` | ||||
|             pouleTable.id = `table-${tid}-${round}-${poule.letter}` | ||||
|             pouleTable.classList.add('table', 'table-stripped') | ||||
|             cardBody.append(pouleTable) | ||||
|  | ||||
| @@ -464,7 +476,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-${tournament.id}-round-${round}-problem-${team}">?</span>` | ||||
|                 problemTh.innerHTML = `Pb. <span id="table-${tid}-round-${round}-problem-${team}">?</span>` | ||||
|                 problemTr.append(problemTh) | ||||
|             } | ||||
|  | ||||
| @@ -553,45 +565,47 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|          * Highligh the team that is currently choosing its problem. | ||||
|      * Highlight the team that is currently choosing its problem. | ||||
|      * @param tid The tournament id | ||||
|      * @param round The current round number, as integer (1 or 2) | ||||
|      * @param 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(round, pool, team) { | ||||
|     function updateActiveRecap(tid, round, pool, team) { | ||||
|         // Remove the previous highlights | ||||
|             document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tournament.id}"]`) | ||||
|         document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tid}"]`) | ||||
|             .forEach(elem => elem.classList.remove('text-bg-secondary')) | ||||
|             document.querySelectorAll(`li.list-group-item-success[data-tournament="${tournament.id}"]`) | ||||
|         document.querySelectorAll(`li.list-group-item-success[data-tournament="${tid}"]`) | ||||
|             .forEach(elem => elem.classList.remove('list-group-item-success')) | ||||
|             document.querySelectorAll(`li.list-group-item-info[data-tournament="${tournament.id}"]`) | ||||
|         document.querySelectorAll(`li.list-group-item-info[data-tournament="${tid}"]`) | ||||
|             .forEach(elem => elem.classList.remove('list-group-item-info')) | ||||
|  | ||||
|         // Highlight current round, if existing | ||||
|             let roundDiv = document.getElementById(`recap-${tournament.id}-round-${round}`) | ||||
|         let roundDiv = document.getElementById(`recap-${tid}-round-${round}`) | ||||
|         if (roundDiv !== null) | ||||
|             roundDiv.classList.add('text-bg-secondary') | ||||
|  | ||||
|         // Highlight current pool, if existing | ||||
|             let poolLi = document.getElementById(`recap-${tournament.id}-round-${round}-pool-${pool}`) | ||||
|         let poolLi = document.getElementById(`recap-${tid}-round-${round}-pool-${pool}`) | ||||
|         if (poolLi !== null) | ||||
|             poolLi.classList.add('list-group-item-success') | ||||
|  | ||||
|         // Highlight current team, if existing | ||||
|             let teamLi = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}`) | ||||
|         let teamLi = document.getElementById(`recap-${tid}-round-${round}-team-${team}`) | ||||
|         if (teamLi !== null) | ||||
|             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(round, team, problem) { | ||||
|     function setProblemAccepted(tid, round, team, problem) { | ||||
|         // Update recap | ||||
|             let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-accepted`) | ||||
|         let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-accepted`) | ||||
|         if (problem !== null) { | ||||
|             recapDiv.classList.remove('text-bg-warning') | ||||
|             recapDiv.classList.add('text-bg-success') | ||||
| @@ -603,27 +617,28 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|         recapDiv.textContent = `${team} 📃 ${problem ? problem : '?'}` | ||||
|  | ||||
|         // Update table | ||||
|             let tableSpan = document.getElementById(`table-${tournament.id}-round-${round}-problem-${team}`) | ||||
|         let tableSpan = document.getElementById(`table-${tid}-round-${round}-problem-${team}`) | ||||
|         tableSpan.textContent = problem ? problem : '?' | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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(round, team, rejected) { | ||||
|     function setProblemRejected(tid, round, team, rejected) { | ||||
|         // Update recap | ||||
|             let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-rejected`) | ||||
|         let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-rejected`) | ||||
|         recapDiv.textContent = `🗑️ ${rejected.join(', ')}` | ||||
|  | ||||
|             let penaltyDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-penalty`) | ||||
|         let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`) | ||||
|         if (rejected.length > problems_count - 5) { | ||||
|             // If 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-${tournament.id}-round-${round}-team-${team}-penalty` | ||||
|                 penaltyDiv.id = `recap-${tid}-round-${round}-team-${team}-penalty` | ||||
|                 penaltyDiv.classList.add('badge', 'rounded-pill', 'text-bg-info') | ||||
|                 recapDiv.parentNode.append(penaltyDiv) | ||||
|             } | ||||
| @@ -639,32 +654,34 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|     /** | ||||
|      * For a 5-teams pool, we may reorder the pool if two teams select the same problem. | ||||
|      * 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(round, poule, teams, problems) { | ||||
|     function reorderPoule(tid, round, poule, teams, problems) { | ||||
|         // Redraw the pool table | ||||
|             let table = document.getElementById(`table-${tournament.id}-${round}-${poule}`) | ||||
|         let table = document.getElementById(`table-${tid}-${round}-${poule}`) | ||||
|         table.parentElement.parentElement.remove() | ||||
|  | ||||
|             updatePouleTable(round, {'letter': poule, 'teams': teams}) | ||||
|         updatePouleTable(tid, 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(round, team, problem) | ||||
|             setProblemAccepted(tid, round, team, problem) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         // Listen on websockets and process messages from the server | ||||
|         socket.addEventListener('message', e => { | ||||
|             // Parse received data as JSON | ||||
|             const data = JSON.parse(e.data) | ||||
|  | ||||
|     /** | ||||
|      * Process the received data from the server. | ||||
|      * @param tid The tournament id | ||||
|      * @param data The received message | ||||
|      */ | ||||
|     function processMessage(tid, data) { | ||||
|         switch (data.type) { | ||||
|             case 'alert': | ||||
|                 // Add alert message | ||||
| @@ -676,61 +693,69 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                 break | ||||
|             case 'set_info': | ||||
|                 // Update information banner | ||||
|                     setInfo(data.information) | ||||
|                 setInfo(tid, data.information) | ||||
|                 break | ||||
|             case 'draw_start': | ||||
|                 // Start the draw and update the interface | ||||
|                     drawStart(data.trigrams) | ||||
|                 drawStart(tid, data.trigrams) | ||||
|                 break | ||||
|             case 'abort': | ||||
|                 // Abort the current draw | ||||
|                     drawAbort() | ||||
|                 drawAbort(tid) | ||||
|                 break | ||||
|             case 'dice': | ||||
|                 // Update the interface after a dice launch | ||||
|                     updateDiceInfo(data.team, data.result) | ||||
|                 updateDiceInfo(tid, data.team, data.result) | ||||
|                 break | ||||
|             case 'dice_visibility': | ||||
|                 // Update the dice button visibility | ||||
|                     updateDiceVisibility(data.visible) | ||||
|                 updateDiceVisibility(tid, data.visible) | ||||
|                 break | ||||
|             case 'box_visibility': | ||||
|                 // Update the box button visibility | ||||
|                     updateBoxVisibility(data.visible) | ||||
|                 updateBoxVisibility(tid, data.visible) | ||||
|                 break | ||||
|             case 'buttons_visibility': | ||||
|                 // Update the accept/reject buttons visibility | ||||
|                     updateButtonsVisibility(data.visible) | ||||
|                 updateButtonsVisibility(tid, data.visible) | ||||
|                 break | ||||
|             case 'export_visibility': | ||||
|                 // Update the export button visibility | ||||
|                     updateExportVisibility(data.visible) | ||||
|                 updateExportVisibility(tid, data.visible) | ||||
|                 break | ||||
|             case 'continue_visibility': | ||||
|                 // Update the continue button visibility for the final tournament | ||||
|                     updateContinueVisibility(data.visible) | ||||
|                 updateContinueVisibility(tid, data.visible) | ||||
|                 break | ||||
|             case 'set_poules': | ||||
|                 // Set teams order and pools and update the interface | ||||
|                     updatePoules(data.round, data.poules) | ||||
|                 updatePoules(tid, data.round, data.poules) | ||||
|                 break | ||||
|             case 'set_active': | ||||
|                 // Highlight the team that is selecting a problem | ||||
|                     updateActiveRecap(data.round, data.poule, data.team) | ||||
|                 updateActiveRecap(tid, data.round, data.poule, data.team) | ||||
|                 break | ||||
|             case 'set_problem': | ||||
|                 // Mark a problem as accepted and update the interface | ||||
|                     setProblemAccepted(data.round, data.team, data.problem) | ||||
|                 setProblemAccepted(tid, data.round, data.team, data.problem) | ||||
|                 break | ||||
|             case 'reject_problem': | ||||
|                 // Mark a problem as rejected and update the interface | ||||
|                     setProblemRejected(data.round, data.team, data.rejected) | ||||
|                 setProblemRejected(tid, data.round, data.team, data.rejected) | ||||
|                 break | ||||
|             case 'reorder_poule': | ||||
|                 // Reorder a pool and redraw the associated table | ||||
|                     reorderPoule(data.round, data.poule, data.teams, data.problems) | ||||
|                 reorderPoule(tid, data.round, data.poule, data.teams, data.problems) | ||||
|                 break | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 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 | ||||
| @@ -741,11 +766,13 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|     // 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) { | ||||
| @@ -753,6 +780,7 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
|                 e.preventDefault() | ||||
|  | ||||
|                 socket.send(JSON.stringify({ | ||||
|                     'tid': tournament.id, | ||||
|                     'type': 'start_draw', | ||||
|                     'fmt': document.getElementById('format-' + tournament.id).value | ||||
|                 })) | ||||
|   | ||||
							
								
								
									
										270
									
								
								draw/tests.py
									
									
									
									
									
								
							
							
						
						
									
										270
									
								
								draw/tests.py
									
									
									
									
									
								
							| @@ -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,25 +48,26 @@ 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)), | ||||
|                                              f"/ws/draw/{self.tournament.id}/", | ||||
|                                              headers) | ||||
|                                              "/ws/draw/", headers) | ||||
|         connected, subprotocol = await communicator.connect() | ||||
|         self.assertTrue(connected) | ||||
|  | ||||
|         # Define language | ||||
|         await communicator.send_json_to({'type': 'set_language', 'language': 'en'}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'set_language', 'language': 'en'}) | ||||
|  | ||||
|         # Ensure that Draw has not started | ||||
|         self.assertFalse(await Draw.objects.filter(tournament=self.tournament).aexists()) | ||||
|  | ||||
|         # Must be an error since 1+1+1 != 12 | ||||
|         await communicator.send_json_to({'type': 'start_draw', 'fmt': '1+1+1'}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'start_draw', 'fmt': '1+1+1'}) | ||||
|         resp = await communicator.receive_json_from() | ||||
|         self.assertEqual(resp['type'], 'alert') | ||||
|         self.assertEqual(resp['alert_type'], 'danger') | ||||
| @@ -74,29 +75,30 @@ class TestDraw(TestCase): | ||||
|         self.assertFalse(await Draw.objects.filter(tournament=self.tournament).aexists()) | ||||
|  | ||||
|         # Now start the draw | ||||
|         await communicator.send_json_to({'type': 'start_draw', 'fmt': '3+4+5'}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'start_draw', 'fmt': '3+4+5'}) | ||||
|  | ||||
|         # Receive data after the start | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'alert') | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'set_poules', 'round': 1, | ||||
|                          {'tid': tid, 'type': 'set_poules', 'round': 1, | ||||
|                           'poules': [{'letter': 'A', 'teams': []}, | ||||
|                                      {'letter': 'B', 'teams': []}, | ||||
|                                      {'letter': 'C', 'teams': []}]}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'set_poules', 'round': 2, | ||||
|                          {'tid': tid, 'type': 'set_poules', 'round': 2, | ||||
|                           'poules': [{'letter': 'A', 'teams': []}, | ||||
|                                      {'letter': 'B', 'teams': []}, | ||||
|                                      {'letter': 'C', 'teams': []}]}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'dice_visibility', 'visible': True}) | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'alert') | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'draw_start', 'fmt': [5, 4, 3], | ||||
|                          {'tid': tid, '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(), | ||||
|                          {'type': 'set_active', 'round': 1, 'poule': None, 'team': None}) | ||||
|                          {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': None, 'team': None}) | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'notification') | ||||
|  | ||||
|         # Ensure that now tournament has started | ||||
| @@ -107,7 +109,7 @@ class TestDraw(TestCase): | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|  | ||||
|         # Try to relaunch the draw | ||||
|         await communicator.send_json_to({'type': 'start_draw', 'fmt': '3+4+5'}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'start_draw', 'fmt': '3+4+5'}) | ||||
|         resp = await communicator.receive_json_from() | ||||
|         self.assertEqual(resp['type'], 'alert') | ||||
|         self.assertEqual(resp['alert_type'], 'danger') | ||||
| @@ -119,7 +121,7 @@ class TestDraw(TestCase): | ||||
|  | ||||
|         for i, team in enumerate(self.teams): | ||||
|             # Launch a new dice | ||||
|             await communicator.send_json_to({'type': "dice", 'trigram': team.trigram}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': team.trigram}) | ||||
|             resp = await communicator.receive_json_from() | ||||
|             self.assertEqual(resp['type'], "dice") | ||||
|             self.assertEqual(resp['team'], team.trigram) | ||||
| @@ -130,7 +132,7 @@ class TestDraw(TestCase): | ||||
|                 self.assertEqual(resp['result'], td.passage_dice) | ||||
|  | ||||
|                 # Try to relaunch the dice | ||||
|                 await communicator.send_json_to({'type': "dice", 'trigram': team.trigram}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': team.trigram}) | ||||
|                 resp = await communicator.receive_json_from() | ||||
|                 self.assertEqual(resp['type'], 'alert') | ||||
|                 self.assertEqual(resp['message'], "You've already launched the dice.") | ||||
| @@ -151,7 +153,7 @@ class TestDraw(TestCase): | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'alert') | ||||
|  | ||||
|             for i in range(dup_count): | ||||
|                 await communicator.send_json_to({'type': "dice", 'trigram': None}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': None}) | ||||
|                 await communicator.receive_json_from() | ||||
|  | ||||
|         # Reset dices | ||||
| @@ -161,8 +163,10 @@ class TestDraw(TestCase): | ||||
|             self.assertIsNone(resp['result']) | ||||
|  | ||||
|         # Hide and re-display the dice | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) | ||||
|         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}) | ||||
|  | ||||
|         # Set pools for the two rounds | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_poules') | ||||
| @@ -172,7 +176,7 @@ class TestDraw(TestCase): | ||||
|  | ||||
|         # Manage the first pool | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'set_active', 'round': 1, 'poule': 'A', 'team': None}) | ||||
|                          {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'A', 'team': None}) | ||||
|         r: Round = await Round.objects.prefetch_related('current_pool__current_team__participation__team')\ | ||||
|             .aget(number=1, draw=draw) | ||||
|         p = r.current_pool | ||||
| @@ -188,7 +192,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({'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|             resp = await communicator.receive_json_from() | ||||
|             self.assertEqual(resp['type'], "dice") | ||||
|             trigram = td.participation.team.trigram | ||||
| @@ -200,7 +204,7 @@ class TestDraw(TestCase): | ||||
|                 self.assertEqual(resp['result'], td.choice_dice) | ||||
|  | ||||
|                 # Try to relaunch the dice | ||||
|                 await communicator.send_json_to({'type': "dice", 'trigram': trigram}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': trigram}) | ||||
|                 resp = await communicator.receive_json_from() | ||||
|                 self.assertEqual(resp['type'], 'alert') | ||||
|                 self.assertEqual(resp['message'], "You've already launched the dice.") | ||||
| @@ -222,7 +226,7 @@ class TestDraw(TestCase): | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'alert') | ||||
|  | ||||
|             for i in range(dup_count): | ||||
|                 await communicator.send_json_to({'type': "dice", 'trigram': None}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': None}) | ||||
|                 await communicator.receive_json_from() | ||||
|  | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
| @@ -232,27 +236,32 @@ 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(), {'type': 'set_active', 'round': 1, 'poule': 'A', | ||||
|                                                                   'team': td.participation.team.trigram}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, '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(), {'type': 'dice_visibility', 'visible': False}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'dice_visibility', 'visible': False}) | ||||
|         # The draw box is displayed for the current team and for volunteers | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'box_visibility', 'visible': True}) | ||||
|  | ||||
|         # Render page | ||||
|         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({'type': 'dice', 'trigram': None}) | ||||
|         await communicator.send_json_to({'tid': tid, '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({'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}) | ||||
|         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}) | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|         td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -265,17 +274,19 @@ class TestDraw(TestCase): | ||||
|         self.assertEqual(resp.status_code, 200) | ||||
|  | ||||
|         # Try to redraw a problem while it is not the time | ||||
|         await communicator.send_json_to({'type': 'draw_problem'}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'}) | ||||
|         resp = await communicator.receive_json_from() | ||||
|         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({'type': 'reject'}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'reject'}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'reject_problem', 'round': 1, 'team': trigram, 'rejected': [purposed]}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|                          {'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}) | ||||
|         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) | ||||
| @@ -287,17 +298,19 @@ 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(), {'type': 'set_active', 'round': 1, 'poule': 'A', | ||||
|                                                                       'team': trigram}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, '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({'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}) | ||||
|             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}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -313,11 +326,13 @@ class TestDraw(TestCase): | ||||
|             self.assertEqual(resp.status_code, 200) | ||||
|  | ||||
|             # Accept the problem | ||||
|             await communicator.send_json_to({'type': 'accept'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': 'accept'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': 1 + (i % 2)}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|                              {'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}) | ||||
|             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) | ||||
| @@ -332,15 +347,17 @@ 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(), {'type': 'set_active', 'round': 1, 'poule': 'A', | ||||
|                                                                   'team': trigram}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, '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({'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}) | ||||
|             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}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -349,10 +366,12 @@ class TestDraw(TestCase): | ||||
|             self.assertIn(td.purposed, range(3, len(settings.PROBLEMS) + 1)) | ||||
|  | ||||
|             # Reject | ||||
|             await communicator.send_json_to({'type': 'reject'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': 'reject'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'buttons_visibility', 'visible': False}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'reject_problem') | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'box_visibility', 'visible': True}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
|             self.assertIsNone(td.purposed) | ||||
| @@ -361,8 +380,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(), {'type': 'set_active', 'round': 1, 'poule': 'A', | ||||
|                                                                       'team': trigram}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'A', 'team': trigram}) | ||||
|  | ||||
|         # Render page | ||||
|         resp = await self.async_client.get(reverse('draw:index')) | ||||
| @@ -372,9 +391,11 @@ class TestDraw(TestCase): | ||||
|         self.assertGreaterEqual(td.penalty, 1) | ||||
|  | ||||
|         # Draw a last 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(), {'type': 'buttons_visibility', 'visible': True}) | ||||
|         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}) | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|         td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -382,19 +403,21 @@ class TestDraw(TestCase): | ||||
|         self.assertIn(td.purposed, range(1, len(settings.PROBLEMS) + 1)) | ||||
|  | ||||
|         # Accept the problem | ||||
|         await communicator.send_json_to({'type': 'accept'}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'accept'}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': td.purposed}) | ||||
|                          {'tid': tid, 'type': 'buttons_visibility', 'visible': False}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': td.purposed}) | ||||
|         td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
|         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(), {'type': 'dice_visibility', 'visible': True}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'dice_visibility', 'visible': True}) | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'type': 'set_active', 'round': 1, 'poule': 'B', 'team': None}) | ||||
|                          {'tid': tid, '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')\ | ||||
| @@ -412,7 +435,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({'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|             await communicator.receive_json_from() | ||||
|             await td.arefresh_from_db() | ||||
|             td.choice_dice = 101 + i  # Avoid duplicates | ||||
| @@ -427,20 +450,24 @@ 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(), {'type': 'set_active', 'round': 1, 'poule': 'B', | ||||
|                                                                       'team': trigram}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'B', 'team': trigram}) | ||||
|             if i == 0: | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'dice_visibility', 'visible': False}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, '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({'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}) | ||||
|             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}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -458,14 +485,17 @@ class TestDraw(TestCase): | ||||
|             self.assertEqual(resp.status_code, 200) | ||||
|  | ||||
|             # Accept the problem | ||||
|             await communicator.send_json_to({'type': 'accept'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': 'accept'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1}) | ||||
|                              {'tid': tid, 'type': 'buttons_visibility', 'visible': False}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1}) | ||||
|             if i < 3: | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'box_visibility', 'visible': True}) | ||||
|             else: | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'dice_visibility', 'visible': True}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
|             self.assertIsNone(td.purposed) | ||||
| @@ -483,8 +513,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(), {'type': 'set_active', 'round': 1, 'poule': 'C', | ||||
|                                                                   'team': None}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'C', 'team': None}) | ||||
|  | ||||
|         # Render page | ||||
|         resp = await self.async_client.get(reverse('draw:index')) | ||||
| @@ -493,7 +523,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({'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|             await communicator.receive_json_from() | ||||
|             await td.arefresh_from_db() | ||||
|             td.choice_dice = 101 + i  # Avoid duplicates | ||||
| @@ -508,20 +538,24 @@ 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(), {'type': 'set_active', 'round': 1, 'poule': 'C', | ||||
|                                                                       'team': trigram}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'set_active', 'round': 1, 'poule': 'C', 'team': trigram}) | ||||
|             if i == 0: | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'dice_visibility', 'visible': False}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'dice_visibility', 'visible': False}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, '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({'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}) | ||||
|             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}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -539,16 +573,18 @@ class TestDraw(TestCase): | ||||
|             self.assertEqual(resp.status_code, 200) | ||||
|  | ||||
|             # Accept the problem | ||||
|             await communicator.send_json_to({'type': 'accept'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'buttons_visibility', 'visible': False}) | ||||
|             await communicator.send_json_to({'tid': tid, 'type': 'accept'}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1}) | ||||
|                              {'tid': tid, 'type': 'buttons_visibility', 'visible': False}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'set_problem', 'round': 1, 'team': trigram, 'problem': i + 1}) | ||||
|             td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
|             self.assertIsNone(td.purposed) | ||||
|             self.assertEqual(td.accepted, i + 1) | ||||
|             if i == 2: | ||||
|                 break | ||||
|             self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': True}) | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'tid': tid, 'type': 'box_visibility', 'visible': True}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|             # Render page | ||||
| @@ -572,8 +608,10 @@ class TestDraw(TestCase): | ||||
|         self.assertEqual(resp['type'], 'set_poules') | ||||
|         self.assertEqual(resp['round'], 2) | ||||
|  | ||||
|         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(), | ||||
|                          {'tid': tid, 'type': 'dice_visibility', 'visible': True}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'export_visibility', 'visible': True}) | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|         # Render page | ||||
| @@ -589,12 +627,12 @@ class TestDraw(TestCase): | ||||
|             self.assertEqual(p.size, 5 - i) | ||||
|  | ||||
|             self.assertEqual(await communicator.receive_json_from(), | ||||
|                              {'type': 'set_active', 'round': 2, 'poule': chr(65 + i), 'team': None}) | ||||
|                              {'tid': tid, 'type': 'set_active', 'round': 2, 'poule': chr(65 + i), 'team': None}) | ||||
|  | ||||
|             j = 0 | ||||
|             async for td in p.teamdraw_set.prefetch_related('participation__team').all(): | ||||
|                 # Launch a new dice | ||||
|                 await communicator.send_json_to({'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': "dice", 'trigram': td.participation.team.trigram}) | ||||
|                 await communicator.receive_json_from() | ||||
|                 await td.arefresh_from_db() | ||||
|                 td.choice_dice = 101 + j  # Avoid duplicates | ||||
| @@ -612,23 +650,24 @@ class TestDraw(TestCase): | ||||
|                 trigram = td.participation.team.trigram | ||||
|                 self.assertEqual(td.choose_index, j) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'type': 'set_active', 'round': 2, 'poule': chr(65 + i), | ||||
|                                  {'tid': tid, 'type': 'set_active', 'round': 2, 'poule': chr(65 + i), | ||||
|                                   'team': trigram}) | ||||
|                 if j == 0: | ||||
|                     self.assertEqual(await communicator.receive_json_from(), | ||||
|                                      {'type': 'dice_visibility', 'visible': False}) | ||||
|                                      {'tid': tid, 'type': 'dice_visibility', 'visible': False}) | ||||
|                     self.assertEqual(await communicator.receive_json_from(), | ||||
|                                      {'type': 'box_visibility', 'visible': True}) | ||||
|                                      {'tid': tid, '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({'type': 'draw_problem'}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'box_visibility', 'visible': False}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': 'draw_problem'}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'type': 'buttons_visibility', 'visible': True}) | ||||
|                                  {'tid': tid, 'type': 'box_visibility', 'visible': False}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'buttons_visibility', 'visible': True}) | ||||
|                 self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|                 td: TeamDraw = await TeamDraw.objects.prefetch_related('participation__team').aget(pk=td.pk) | ||||
| @@ -640,44 +679,55 @@ class TestDraw(TestCase): | ||||
|                 self.assertNotEqual(td.purposed, old_td.accepted) | ||||
|  | ||||
|                 # Accept the problem | ||||
|                 await communicator.send_json_to({'type': 'accept'}) | ||||
|                 await communicator.send_json_to({'tid': tid, 'type': 'accept'}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'type': 'buttons_visibility', 'visible': False}) | ||||
|                                  {'tid': tid, '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(), {'type': 'box_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'box_visibility', 'visible': True}) | ||||
|                 self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|             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(), {'type': 'dice_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'dice_visibility', 'visible': True}) | ||||
|             else: | ||||
|                 self.assertEqual(await communicator.receive_json_from(), {'type': 'export_visibility', 'visible': True}) | ||||
|                 self.assertEqual(await communicator.receive_json_from(), | ||||
|                                  {'tid': tid, 'type': 'export_visibility', 'visible': True}) | ||||
|             self.assertEqual((await communicator.receive_json_from())['type'], 'set_info') | ||||
|  | ||||
|         self.assertEqual((await communicator.receive_json_from())['type'], 'set_active') | ||||
|  | ||||
|         # Export the draw | ||||
|         await communicator.send_json_to({'type': 'export'}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), {'type': 'export_visibility', 'visible': False}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': 'export'}) | ||||
|         self.assertEqual(await communicator.receive_json_from(), | ||||
|                          {'tid': tid, 'type': 'export_visibility', 'visible': False}) | ||||
|  | ||||
|         # Cancel all steps and reset all | ||||
|         for i in range(1000): | ||||
|             await communicator.send_json_to({'type': 'cancel'}) | ||||
|             if not await Draw.objects.filter(tournament=self.tournament).aexists(): | ||||
|             await communicator.send_json_to({'tid': tid, 'type': 'cancel'}) | ||||
|  | ||||
|         # Purge receive queue | ||||
|         while True: | ||||
|             try: | ||||
|                 await communicator.receive_json_from() | ||||
|             except asyncio.TimeoutError: | ||||
|                 break | ||||
|         else: | ||||
|             current_state = (await Draw.objects.filter(tournament=self.tournament).prefetch_related( | ||||
|  | ||||
|         if await Draw.objects.filter(tournament_id=tid).aexists(): | ||||
|             print((await Draw.objects.filter(tournament_id=tid).aexists())) | ||||
|             current_state = (await Draw.objects.filter(tournament_id=tid).prefetch_related( | ||||
|                 'current_round__current_pool__current_team__participation__team').aget()).get_state() | ||||
|             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({'type': "abort"}) | ||||
|         await communicator.send_json_to({'tid': tid, 'type': "abort"}) | ||||
|  | ||||
|     def test_admin_pages(self): | ||||
|         """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user