Update to django channels 2

This commit is contained in:
Tom Hubrecht 2022-06-27 15:34:24 +02:00
parent 0065269af5
commit 0cc035d903
14 changed files with 129 additions and 73 deletions

View file

@ -1,8 +1,15 @@
"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""
import os
from channels.asgi import get_channel_layer
import django
from channels.routing import get_default_application
if "DJANGO_SETTINGS_MODULE" not in os.environ:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.local")
channel_layer = get_channel_layer()
django.setup()
application = get_default_application()

View file

@ -1,3 +1,18 @@
from channels.routing import include
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path
routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")]
from kfet.routing import KFRouter
application = ProtocolTypeRouter(
{
# WebSocket chat handler
"websocket": AuthMiddlewareStack(
URLRouter(
[
path("ws/k-fet", KFRouter),
]
)
),
}
)

View file

@ -109,6 +109,8 @@ MEDIA_URL = "/gestion/media/"
CORS_ORIGIN_WHITELIST = ("bda.ens.fr", "www.bda.ens.fr" "cof.ens.fr", "www.cof.ens.fr")
ASGI_APPLICATION = "gestioasso.routing.application"
# ---
# Auth-related stuff
# ---
@ -147,7 +149,7 @@ CACHES = {
CHANNEL_LAYERS = {
"default": {
"BACKEND": "asgi_redis.RedisChannelLayer",
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [
(
@ -160,11 +162,9 @@ CHANNEL_LAYERS = {
)
]
},
"ROUTING": "gestioasso.routing.routing",
}
}
# ---
# reCAPTCHA settings
# https://github.com/praekelt/django-recaptcha

View file

@ -47,8 +47,7 @@ CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"
# Use the default in memory asgi backend for local development
CHANNEL_LAYERS = {
"default": {
"BACKEND": "asgiref.inmemory.ChannelLayer",
"ROUTING": "gestioasso.routing.routing",
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}

View file

@ -4,3 +4,6 @@ from .utils import DjangoJsonWebsocketConsumer, PermConsumerMixin
class KPsul(PermConsumerMixin, DjangoJsonWebsocketConsumer):
groups = ["kfet.kpsul"]
perms_connect = ["kfet.is_team"]
async def kpsul(self, event):
await self.send_json(event)

View file

@ -12,13 +12,15 @@ class OpenKfetConsumer(PermConsumerMixin, DjangoJsonWebsocketConsumer):
"""
def connection_groups(self, user, **kwargs):
"""Select which group the user should be connected."""
if kfet_is_team(user):
return ["kfet.open.team"]
return ["kfet.open.base"]
async def open_status(self, event):
await self.send_json(event)
def connect(self, message, *args, **kwargs):
async def connect(self):
"""Send current status on connect."""
super().connect(message, *args, **kwargs)
self.send(kfet_open.export(message.user))
await super().connect()
group = "team" if kfet_is_team(self.user) else "base"
await self.channel_layer.group_add(f"kfet.open.{group}", self.channel_name)
await self.send_json(kfet_open.export(self.user))

View file

@ -1,5 +1,6 @@
from datetime import timedelta
from channels.layers import get_channel_layer
from django.utils import timezone
from ..decorators import kfet_is_team
@ -77,7 +78,7 @@ class OpenKfet(CachedMixin, object):
"""
status = self.status()
base = {"status": status}
base = {"status": status, "type": "open.status"}
restrict = {
"admin_status": self.admin_status(status),
"force_close": self.force_close,
@ -95,13 +96,14 @@ class OpenKfet(CachedMixin, object):
base, team = self._export()
return team if kfet_is_team(user) else base
def send_ws(self):
async def send_ws(self):
"""Send internal state to websocket channels."""
from .consumers import OpenKfetConsumer
base, team = self._export()
OpenKfetConsumer.group_send("kfet.open.base", base)
OpenKfetConsumer.group_send("kfet.open.team", team)
channel_layer = get_channel_layer()
await channel_layer.group_send("kfet.open.base", base)
await channel_layer.group_send("kfet.open.team", team)
kfet_open = OpenKfet()

View file

@ -1,5 +1,10 @@
from channels.routing import route_class
from channels.routing import URLRouter
from django.urls import path
from . import consumers
from .consumers import OpenKfetConsumer
routing = [route_class(consumers.OpenKfetConsumer)]
OpenRouter = URLRouter(
[
path(r"", OpenKfetConsumer),
]
)

View file

@ -1,3 +1,4 @@
from asgiref.sync import async_to_sync
from django.conf import settings
from django.contrib.auth.decorators import permission_required
from django.core.exceptions import PermissionDenied
@ -18,7 +19,7 @@ def raw_open(request):
raise PermissionDenied
raw_open = request.POST.get("raw_open") in TRUE_STR
kfet_open.raw_open = raw_open
kfet_open.send_ws()
async_to_sync(kfet_open.send_ws)()
return HttpResponse()
@ -27,5 +28,5 @@ def raw_open(request):
def force_close(request):
force_close = request.POST.get("force_close") in TRUE_STR
kfet_open.force_close = force_close
kfet_open.send_ws()
async_to_sync(kfet_open.send_ws)()
return HttpResponse()

View file

@ -1,8 +1,13 @@
from channels.routing import include, route_class
from channels.routing import URLRouter
from django.urls import path
from . import consumers
from kfet.open.routing import OpenRouter
routing = [
route_class(consumers.KPsul, path=r"^/k-psul/$"),
include("kfet.open.routing.routing", path=r"^/open"),
]
from .consumers import KPsul
KFRouter = URLRouter(
[
path("k-psul/", KPsul),
path("open", OpenRouter),
]
)

View file

@ -1,8 +1,7 @@
import json
import math
from channels.channel import Group
from channels.generic.websockets import JsonWebsocketConsumer
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from django.core.cache import cache
from django.core.serializers.json import DjangoJSONEncoder
@ -63,7 +62,7 @@ class CachedMixin:
# Consumers
class DjangoJsonWebsocketConsumer(JsonWebsocketConsumer):
class DjangoJsonWebsocketConsumer(AsyncJsonWebsocketConsumer):
"""Custom Json Websocket Consumer.
Encode to JSON with DjangoJSONEncoder.
@ -71,7 +70,7 @@ class DjangoJsonWebsocketConsumer(JsonWebsocketConsumer):
"""
@classmethod
def encode_json(cls, content):
async def encode_json(cls, content):
return json.dumps(content, cls=DjangoJSONEncoder)
@ -89,31 +88,35 @@ class PermConsumerMixin:
http_user = True # Enable message.user
perms_connect = []
def connect(self, message, **kwargs):
async def connect(self):
"""Check permissions on connection."""
if message.user.has_perms(self.perms_connect):
super().connect(message, **kwargs)
self.user = self.scope["user"]
if self.user.has_perms(self.perms_connect):
await super().connect()
else:
self.close()
await self.close()
def raw_connect(self, message, **kwargs):
# Same as original raw_connect method of JsonWebsocketConsumer
# We add user to connection_groups call.
groups = self.connection_groups(user=message.user, **kwargs)
for group in groups:
Group(group, channel_layer=message.channel_layer).add(message.reply_channel)
self.connect(message, **kwargs)
def raw_disconnect(self, message, **kwargs):
# Same as original raw_connect method of JsonWebsocketConsumer
# We add user to connection_groups call.
groups = self.connection_groups(user=message.user, **kwargs)
for group in groups:
Group(group, channel_layer=message.channel_layer).discard(
message.reply_channel
)
self.disconnect(message, **kwargs)
def connection_groups(self, user, **kwargs):
"""`message.user` is available as `user` arg. Original behavior."""
return super().connection_groups(user=user, **kwargs)
# async def raw_connect(self, message, **kwargs):
# # Same as original raw_connect method of JsonWebsocketConsumer
# # We add user to connection_groups call.
# groups = self.connection_groups(user=message.user, **kwargs)
# for group in groups:
# await self.channel_layer.group_add(group, message.reply_channel)
# # Group(group, channel_layer=message.channel_layer).add(message.reply_channel)
# self.connect(message, **kwargs)
#
# async def raw_disconnect(self, message, **kwargs):
# # Same as original raw_connect method of JsonWebsocketConsumer
# # We add user to connection_groups call.
# groups = self.connection_groups(user=message.user, **kwargs)
# for group in groups:
# await self.channel_layer.group_discard(group, message.reply_channel)
# # Group(group, channel_layer=message.channel_layer).discard(
# # message.reply_channel
# # )
# self.disconnect(message, **kwargs)
#
# def connection_groups(self, user, **kwargs):
# """`message.user` is available as `user` arg. Original behavior."""
# return super().connection_groups(user=user, **kwargs)

View file

@ -6,6 +6,8 @@ from decimal import Decimal
from typing import List, Tuple
from urllib.parse import urlencode
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
@ -42,7 +44,7 @@ from django.views.generic.detail import BaseDetailView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from gestioncof.models import CofProfile
from kfet import KFET_DELETED_TRIGRAMME, consumers
from kfet import KFET_DELETED_TRIGRAMME
from kfet.auth.decorators import kfet_password_auth
from kfet.autocomplete import kfet_account_only_autocomplete, kfet_autocomplete
from kfet.config import kfet_config
@ -994,8 +996,14 @@ def kpsul_update_addcost(request):
kfet_config.set(addcost_for=account, addcost_amount=amount)
data = {"addcost": {"for": account and account.trigramme or None, "amount": amount}}
consumers.KPsul.group_send("kfet.kpsul", data)
data = {
"addcost": {"for": account and account.trigramme or None, "amount": amount},
"type": "kpsul",
}
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("kfet.kpsul", data)
return JsonResponse(data)
@ -1179,7 +1187,7 @@ def kpsul_perform_operations(request):
)
# Websocket data
websocket_data = {}
websocket_data = {"type": "kpsul"}
websocket_data["groups"] = [
{
"add": True,
@ -1226,7 +1234,10 @@ def kpsul_perform_operations(request):
websocket_data["articles"].append(
{"id": article["id"], "stock": article["stock"]}
)
consumers.KPsul.group_send("kfet.kpsul", websocket_data)
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("kfet.kpsul", websocket_data)
return JsonResponse(data)
@ -1421,7 +1432,7 @@ def cancel_operations(request):
articles = Article.objects.values("id", "stock").filter(pk__in=articles_pk)
# Websocket data
websocket_data = {"checkouts": [], "articles": []}
websocket_data = {"checkouts": [], "articles": [], "type": "kpsul"}
for checkout in checkouts:
websocket_data["checkouts"].append(
{"id": checkout["id"], "balance": checkout["balance"]}
@ -1430,7 +1441,10 @@ def cancel_operations(request):
websocket_data["articles"].append(
{"id": article["id"], "stock": article["stock"]}
)
consumers.KPsul.group_send("kfet.kpsul", websocket_data)
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("kfet.kpsul", websocket_data)
data["canceled"] = list(opes)
data["opegroups_to_update"] = list(opegroups)

View file

@ -6,7 +6,7 @@ psycopg2<2.8
# Redis
django-redis-cache==2.1.*
redis~=2.10.6
asgi-redis==1.4.*
channels-redis==2.4.*
# ASGI protocol and HTTP server
asgiref~=1.1.2

View file

@ -1,7 +1,7 @@
Django==2.2.*
Pillow==7.2.0
authens==0.1b0
channels==1.1.*
channels==2.4.*
configparser==3.5.0
django-autocomplete-light==3.3.*
django-bootstrap-form==3.3