Add Redis Channel Layer for the drawing system

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2023-04-11 23:05:58 +02:00
parent 0e7a275a28
commit 8245ba0063
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
5 changed files with 121 additions and 33 deletions

View File

@ -176,7 +176,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await TeamDraw.objects.acreate(participation=participation, round=r) await TeamDraw.objects.acreate(participation=participation, round=r)
# Send to clients the different pools # Send to clients the different pools
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r}) {'type': 'draw.send_poules',
'round': r.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r.pool_set.order_by('letter').all()
]})
draw.current_round = r1 draw.current_round = r1
await draw.asave() await draw.asave()
@ -191,9 +199,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.start', 'fmt': fmt, 'draw': draw}) {'type': 'draw.start', 'fmt': fmt, 'draw': draw})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active', 'round': 1})
# Send notification to everyone # Send notification to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -457,25 +466,44 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.dice_visibility', 'visible': True}) {'type': 'draw.dice_visibility', 'visible': True})
# First send the second pool to have the good team order # First send the second pool to have the good team order
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'type': 'draw.send_poules',
'round': await self.tournament.draw.round_set.filter(number=2).aget()}) 'round': r2.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r2.pool_set.order_by('letter').all()
]})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'type': 'draw.send_poules',
'round': self.tournament.draw.current_round}) 'round': r.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r.pool_set.order_by('letter').all()
]})
# Update information header and the active team on the recap menu # Update information header and the active team on the recap menu
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display()})
async def process_dice_order_poule(self): async def process_dice_order_poule(self):
""" """
Called when all teams of the current launched their dice to determine the choice order. Called when all teams of the current launched their dice to determine the choice order.
Place teams into pools and order their passage. Place teams into pools and order their passage.
""" """
pool = self.tournament.draw.current_round.current_pool r = self.tournament.draw.current_round
pool = r.current_pool
tds = [td async for td in TeamDraw.objects.filter(pool=pool).prefetch_related('participation__team')] tds = [td async for td in TeamDraw.objects.filter(pool=pool).prefetch_related('participation__team')]
# Order teams by decreasing dice score # Order teams by decreasing dice score
@ -493,9 +521,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update information header # Update information header
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram})
# Hide dice button to everyone # Hide dice button to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -566,7 +598,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
self.tournament.draw.last_message = "" self.tournament.draw.last_message = ""
await self.tournament.draw.asave() await self.tournament.draw.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
async def accept_problem(self, **kwargs): async def accept_problem(self, **kwargs):
""" """
@ -637,11 +670,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
else: else:
# Pool is ended # Pool is ended
await self.end_pool(pool) await self.end_pool(pool)
r = self.tournament.draw.current_round
pool = r.current_pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram
if pool.current_team else None})
async def end_pool(self, pool: Pool) -> None: async def end_pool(self, pool: Pool) -> None:
""" """
@ -736,7 +776,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reorder dices # Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', {'type': 'draw.send_poules',
'round': r2}) 'round': r2.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r2.pool_set.order_by('letter').all()
]})
# The passage order for the second round is already determined by the first round # The passage order for the second round is already determined by the first round
# Start the first pool of the second round # Start the first pool of the second round
@ -831,9 +878,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.box_visibility', 'visible': True}) {'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': new_trigram})
# Notify the team that it can draw a problem # Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}", await self.channel_layer.group_send(f"team-{new_trigram}",
@ -900,7 +951,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send pools to users # Send pools to users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r2}) {'type': 'draw.send_poules',
'round': r2.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r2.pool_set.order_by('letter').all()
]})
# Reset dices and update interface # Reset dices and update interface
for participation in self.participations: for participation in self.participations:
@ -923,9 +982,12 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.continue_visibility', 'visible': False}) {'type': 'draw.continue_visibility', 'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active',
'round': r2.number,
'pool': r2.current_pool.get_letter_display()})
@ensure_orga @ensure_orga
async def cancel_last_step(self, **kwargs): async def cancel_last_step(self, **kwargs):
@ -952,9 +1014,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.undo_order_dice() await self.undo_order_dice()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw}) {'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}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw}) {'type': 'draw.set_active',
'round': r.number,
'pool': p.get_letter_display() if p else None,
'team': p.current_team.participation.team.trigram
if p and p.current_team else None})
async def undo_end_draw(self) -> None: async def undo_end_draw(self) -> None:
""" """
@ -1194,7 +1263,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'result': td.choice_dice}) 'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}", await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r1}) {'type': 'draw.send_poules',
'round': r1.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r1.pool_set.order_by('letter').all()
]})
previous_pool = r1.current_pool previous_pool = r1.current_pool
@ -1339,7 +1416,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
""" """
Set the information banner to the current user. Set the information banner to the current user.
""" """
await self.send_json({'type': 'set_info', 'information': await content['draw'].ainformation()}) await self.send_json({'type': 'set_info', 'information': content['info']})
async def draw_dice(self, content): async def draw_dice(self, content):
""" """
@ -1381,21 +1458,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
""" """
Send the pools and the teams to the current user to update the interface. Send the pools and the teams to the current user to update the interface.
""" """
await self.send_json({'type': 'set_poules', 'round': content['round'].number, await self.send_json({'type': 'set_poules', 'round': content['round'],
'poules': [{'letter': pool.get_letter_display(), 'teams': await pool.atrigrams()} 'poules': content['poules']})
async for pool in content['round'].pool_set.order_by('letter').all()]})
async def draw_set_active(self, content): async def draw_set_active(self, content):
""" """
Update the user interface to highlight the current team. Update the user interface to highlight the current team.
""" """
r = content['draw'].current_round
await self.send_json({ await self.send_json({
'type': 'set_active', 'type': 'set_active',
'round': r.number, 'round': content.get('round', None),
'poule': r.current_pool.get_letter_display() if r.current_pool else None, 'poule': content.get('pool', None),
'team': r.current_pool.current_team.participation.team.trigram 'team': content.get('team', None),
if r.current_pool and r.current_pool.current_team else None,
}) })
async def draw_set_problem(self, content): async def draw_set_problem(self, content):

View File

@ -24,7 +24,7 @@ from django.utils.crypto import get_random_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, FormView, RedirectView, TemplateView, UpdateView, View from django.views.generic import CreateView, DetailView, FormView, RedirectView, TemplateView, UpdateView, View
from django.views.generic.edit import FormMixin, ProcessFormView from django.views.generic.edit import FormMixin, ProcessFormView
from django_tables2 import SingleTableView, MultiTableMixin from django_tables2 import MultiTableMixin, SingleTableView
from magic import Magic from magic import Magic
from odf.opendocument import OpenDocumentSpreadsheet from odf.opendocument import OpenDocumentSpreadsheet
from odf.style import Style, TableCellProperties, TableColumnProperties, TextProperties from odf.style import Style, TableCellProperties, TableColumnProperties, TextProperties

View File

@ -1,4 +1,5 @@
channels[daphne]~=4.0.0 channels[daphne]~=4.0.0
channels-redis~=4.0.0
crispy-bootstrap5~=0.7 crispy-bootstrap5~=0.7
Django>=4.1,<5.0 Django>=4.1,<5.0
django-cas-server~=2.0 django-cas-server~=2.0

View File

@ -79,6 +79,11 @@ if "test" not in sys.argv: # pragma: no cover
'mailer', 'mailer',
] ]
if os.getenv("TFJM_STAGE", "dev") == "prod": # pragma: no cover
INSTALLED_APPS += [
'channels_redis',
]
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
@ -268,7 +273,6 @@ FORBIDDEN_TRIGRAMS = [
"SEX", "SEX",
] ]
# TODO: Use a redis server in production
CHANNEL_LAYERS = { CHANNEL_LAYERS = {
"default": { "default": {
"BACKEND": "channels.layers.InMemoryChannelLayer" "BACKEND": "channels.layers.InMemoryChannelLayer"

View File

@ -30,3 +30,12 @@ CSRF_COOKIE_SECURE = False
CSRF_COOKIE_HTTPONLY = False CSRF_COOKIE_HTTPONLY = False
X_FRAME_OPTIONS = 'DENY' X_FRAME_OPTIONS = 'DENY'
SESSION_COOKIE_AGE = 60 * 60 * 3 SESSION_COOKIE_AGE = 60 * 60 * 3
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [(os.getenv('REDIS_SERVER_HOST', 'localhost'), os.getenv('REDIS_SERVER_PORT', 6379))],
},
},
}