From 68df020da017ab99b5add61280eb44c5aae51c17 Mon Sep 17 00:00:00 2001 From: MarcZierle Date: Tue, 21 Jun 2022 21:54:17 +0200 Subject: [PATCH] add websockets and connect celery worker to consumer --- api/views.py | 3 --- config/asgi.py | 10 ++++++- config/settings.py | 17 ++++++++++++ .../{ => supervisor}/celery_supervisor.conf | 0 config/supervisor/daphne_supervisor.conf | 26 +++++++++++++++++++ manage.py | 0 photo_log/tasks.py | 20 ++++++++++++++ websocket/__init__.py | 0 websocket/admin.py | 3 +++ websocket/apps.py | 6 +++++ websocket/consumers.py | 23 ++++++++++++++++ websocket/routing.py | 8 ++++++ 12 files changed, 112 insertions(+), 4 deletions(-) rename config/{ => supervisor}/celery_supervisor.conf (100%) create mode 100644 config/supervisor/daphne_supervisor.conf mode change 100755 => 100644 manage.py create mode 100644 websocket/__init__.py create mode 100644 websocket/admin.py create mode 100644 websocket/apps.py create mode 100644 websocket/consumers.py create mode 100644 websocket/routing.py diff --git a/api/views.py b/api/views.py index 34eb866..67c18a9 100755 --- a/api/views.py +++ b/api/views.py @@ -20,9 +20,6 @@ from .serializers import ( PhotoLogTemplateSerializer, ) -from .photolog_generator import generate_photolog_from_latex -from .autocrop.autocrop import autocrop - from photo_log import tasks from django.db.models import FileField diff --git a/config/asgi.py b/config/asgi.py index 8aee0cb..fbe8570 100755 --- a/config/asgi.py +++ b/config/asgi.py @@ -9,13 +9,21 @@ https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ import os -from channels.routing import ProtocolTypeRouter +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter from django.core.asgi import get_asgi_application +import websocket.routing + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') django_asgi_app = get_asgi_application() #application = get_asgi_application() application = ProtocolTypeRouter({ "http": django_asgi_app, + "websocket": AuthMiddlewareStack( + URLRouter( + websocket.routing.websocket_urlpatterns + ) + ), }) diff --git a/config/settings.py b/config/settings.py index 15c9a92..2893830 100755 --- a/config/settings.py +++ b/config/settings.py @@ -51,6 +51,7 @@ INSTALLED_APPS = [ 'django_celery_results', # local + 'websocket', 'accounts', 'photo_log', 'api', @@ -226,3 +227,19 @@ CACHES = { "KEY_PREFIX": "zierletraining", } } + + +# Django Channels - Channel Layers Backend +# See https://channels.readthedocs.io/en/stable/topics/channel_layers.html +# See https://pypi.org/project/channels-redis/ for settings configuration + +CHANNEL_LAYERS = { + "default": { + "BACKEND": "channels_redis.core.RedisChannelLayer", + "CONFIG": { + "hosts": [("127.0.0.1", 6378)], + "prefix": "asgi_zierle_training_staging:", + "group_expiry": 7200, + }, + }, +} diff --git a/config/celery_supervisor.conf b/config/supervisor/celery_supervisor.conf similarity index 100% rename from config/celery_supervisor.conf rename to config/supervisor/celery_supervisor.conf diff --git a/config/supervisor/daphne_supervisor.conf b/config/supervisor/daphne_supervisor.conf new file mode 100644 index 0000000..eee5ca7 --- /dev/null +++ b/config/supervisor/daphne_supervisor.conf @@ -0,0 +1,26 @@ +[fcgi-program:ws_zierle_training_staging] +# TCP socket used by Nginx backend upstream +socket=tcp://localhost:8001 + +user=www-data + +# Directory where your site's project files are located +directory=/home/marc/www-staging/backend + +# Each process needs to have a separate socket file, so we use process_num +# Make sure to update "mysite.asgi" to match your project name +command=/home/marc/www-staging/backend/env/bin/python3 -m daphne -u /run/daphne/daphne%(process_num)d.sock --fd 0 --access-log - --proxy-headers config.asgi:application + +# Number of processes to startup, roughly the number of CPUs you have +numprocs=2 + +# Give each process a unique name so they can be told apart +process_name=ws_zierle_training_staging%(process_num)d + +# Automatically start and recover processes +autostart=true +autorestart=true + +# Choose where you want your log to go +stdout_logfile=/home/marc/www-staging/logs/daphne.log +redirect_stderr=true diff --git a/manage.py b/manage.py old mode 100755 new mode 100644 diff --git a/photo_log/tasks.py b/photo_log/tasks.py index 3051da5..7ba8032 100644 --- a/photo_log/tasks.py +++ b/photo_log/tasks.py @@ -10,6 +10,9 @@ from django.conf import settings from django.core.files import File from django.apps import apps +from channels.layers import get_channel_layer +from asgiref.sync import async_to_sync + from django_tex.shortcuts import compile_template_to_pdf from .photolog_layout import generate_tex @@ -25,6 +28,16 @@ def chordfinisher(*args, **kwargs): return 'OK' +@shared_task +def notify_client(description, content): + channel_layer = get_channel_layer() + async_to_sync(channel_layer.group_send)("notifications", { + "type": "task.finished", + "description": description, + "content": content + }) + + @shared_task def download_s3_file(folder_path, s3_file_path, bucket): """ @@ -383,6 +396,13 @@ def generate_photo_log_chain(photo_log_id, work_folder_name=uuid4()): 'photo_log', 'PhotoLog', photo_log.id, 'pdf', pdf_s3_path ), + notify_client.si( + description='update_photolog_pdf', + content={ + 'pdf': pdf_s3_path, + 'id': photo_log.id + } + ), delete_folder.si( folder ) diff --git a/websocket/__init__.py b/websocket/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/websocket/admin.py b/websocket/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/websocket/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/websocket/apps.py b/websocket/apps.py new file mode 100644 index 0000000..a962b95 --- /dev/null +++ b/websocket/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class WebsocketConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'websocket' diff --git a/websocket/consumers.py b/websocket/consumers.py new file mode 100644 index 0000000..95d3be4 --- /dev/null +++ b/websocket/consumers.py @@ -0,0 +1,23 @@ +from channels.generic.websocket import JsonWebsocketConsumer + + +class NotificationsConsumer(JsonWebsocketConsumer): + groups = ['notifications'] + + def connect(self): + self.accept() + + + def disconnect(self, close_code): + pass + + + def receive_json(self, content): + pass + + + def task_finished(self, event): + self.send_json({ + 'type': event['description'], + 'content': event['content'] + }) diff --git a/websocket/routing.py b/websocket/routing.py new file mode 100644 index 0000000..59dc979 --- /dev/null +++ b/websocket/routing.py @@ -0,0 +1,8 @@ +from django.urls import re_path + +from . import consumers + +websocket_urlpatterns = [ + # change this for handling multiple users + re_path(r'ws/notifications/', consumers.NotificationsConsumer.as_asgi()), +]