From ebf686ef34d6a67a628a8f66e97df88b48bd59c6 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 14 Jan 2017 12:29:42 +0100 Subject: [PATCH] Send all data to the client at startup --- CHANGELOG | 2 ++ openslides/agenda/apps.py | 4 ++++ openslides/assignments/apps.py | 4 ++++ openslides/core/apps.py | 15 +++++++++++---- openslides/mediafiles/apps.py | 4 ++++ openslides/motions/apps.py | 5 +++++ openslides/topics/apps.py | 4 ++++ openslides/utils/autoupdate.py | 24 +++++++++++++++++++++++- openslides/utils/collection.py | 11 +++++++++++ 9 files changed, 68 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a03480ed2..5683c42d4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -95,6 +95,8 @@ Other: - Added new caching system with support for Redis. - Support https as websocket protocol (wss). - Added migration path from 2.0. +- Add an api for apps to send data when the websocket connection is established. +- Used this api for all models expect of users to send all data to the client. Version 2.0 (2016-04-18) diff --git a/openslides/agenda/apps.py b/openslides/agenda/apps.py index 81e71f295..8c94160b0 100644 --- a/openslides/agenda/apps.py +++ b/openslides/agenda/apps.py @@ -35,3 +35,7 @@ class AgendaAppConfig(AppConfig): # Register viewsets. router.register(self.get_model('Item').get_collection_string(), ItemViewSet) + + def get_startup_elements(self): + from ..utils.collection import Collection + return [Collection(self.get_model('Item').get_collection_string())] diff --git a/openslides/assignments/apps.py b/openslides/assignments/apps.py index 6ce05de3a..6a796d687 100644 --- a/openslides/assignments/apps.py +++ b/openslides/assignments/apps.py @@ -24,3 +24,7 @@ class AssignmentsAppConfig(AppConfig): # Register viewsets. router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet) router.register('assignments/poll', AssignmentPollViewSet) + + def get_startup_elements(self): + from ..utils.collection import Collection + return [Collection(self.get_model('Assignment').get_collection_string())] diff --git a/openslides/core/apps.py b/openslides/core/apps.py index c7dc9ceb7..3abd4e47a 100644 --- a/openslides/core/apps.py +++ b/openslides/core/apps.py @@ -14,10 +14,10 @@ class CoreAppConfig(AppConfig): # Import all required stuff. from django.db.models import signals - from openslides.core.config import config - from openslides.core.signals import post_permission_creation - from openslides.utils.rest_api import router - from openslides.utils.search import index_add_instance, index_del_instance + from .config import config + from .signals import post_permission_creation + from ..utils.rest_api import router + from ..utils.search import index_add_instance, index_del_instance from .config_variables import get_config_variables from .signals import delete_django_app_permissions from .views import ( @@ -55,3 +55,10 @@ class CoreAppConfig(AppConfig): signals.m2m_changed.connect( index_add_instance, dispatch_uid='m2m_index_add_instance') + + def get_startup_elements(self): + from .config import config + from ..utils.collection import Collection + for model in ('Projector', 'ChatMessage', 'Tag', 'ProjectorMessage', 'Countdown'): + yield Collection(self.get_model(model).get_collection_string()) + yield Collection(config.get_collection_string()) diff --git a/openslides/mediafiles/apps.py b/openslides/mediafiles/apps.py index 1732f1299..31d18c7e0 100644 --- a/openslides/mediafiles/apps.py +++ b/openslides/mediafiles/apps.py @@ -18,3 +18,7 @@ class MediafilesAppConfig(AppConfig): # Register viewsets. router.register(self.get_model('Mediafile').get_collection_string(), MediafileViewSet) + + def get_startup_elements(self): + from ..utils.collection import Collection + return [Collection(self.get_model('Mediafile').get_collection_string())] diff --git a/openslides/motions/apps.py b/openslides/motions/apps.py index e82732e31..da7487943 100644 --- a/openslides/motions/apps.py +++ b/openslides/motions/apps.py @@ -34,3 +34,8 @@ class MotionsAppConfig(AppConfig): router.register(self.get_model('MotionChangeRecommendation').get_collection_string(), MotionChangeRecommendationViewSet) router.register('motions/motionpoll', MotionPollViewSet) + + def get_startup_elements(self): + from ..utils.collection import Collection + for model in ('Category', 'Motion', 'MotionBlock', 'Workflow', 'MotionChangeRecommendation'): + yield Collection(self.get_model(model).get_collection_string()) diff --git a/openslides/topics/apps.py b/openslides/topics/apps.py index 15f37e93e..49eba7af8 100644 --- a/openslides/topics/apps.py +++ b/openslides/topics/apps.py @@ -18,3 +18,7 @@ class TopicsAppConfig(AppConfig): # Register viewsets. router.register(self.get_model('Topic').get_collection_string(), TopicViewSet) + + def get_startup_elements(self): + from ..utils.collection import Collection + return [Collection(self.get_model('Topic').get_collection_string())] diff --git a/openslides/utils/autoupdate.py b/openslides/utils/autoupdate.py index cf4dfebf0..33ae81f70 100644 --- a/openslides/utils/autoupdate.py +++ b/openslides/utils/autoupdate.py @@ -4,6 +4,7 @@ from collections import Iterable from asgiref.inmemory import ChannelLayer from channels import Channel, Group from channels.auth import channel_session_user, channel_session_user_from_http +from django.apps import apps from django.db import transaction from ..core.config import config @@ -19,12 +20,33 @@ def ws_add_site(message): Adds the websocket connection to a group specific to the connecting user. The group with the name 'user-None' stands for all anonymous users. + + Send all "starup-data" through the connection. """ Group('site').add(message.reply_channel) message.channel_session['user_id'] = message.user.id # Saves the reply channel to the user. Uses 0 for anonymous users. websocket_user_cache.add(message.user.id or 0, message.reply_channel.name) - message.reply_channel.send({"accept": True}) + + # Collect all elements that shoud be send to the client when the websocket + # connection is established + output = [] + for app in apps.get_app_configs(): + try: + # Get the method get_startup_elements() from an app. + # This method has to return an iterable of Collection objects. + get_startup_elements = app.get_startup_elements + except AttributeError: + # Skip apps that do not implement get_startup_elements + continue + for collection in get_startup_elements(): + output.extend(collection.as_autoupdate_for_user(message.user)) + + # Send all data. If there is no data, then onyl accept the connection + if output: + message.reply_channel.send({'text': json.dumps(output)}) + else: + message.reply_channel.send({"accept": True}) @channel_session_user diff --git a/openslides/utils/collection.py b/openslides/utils/collection.py index e56d4e788..697fa2f27 100644 --- a/openslides/utils/collection.py +++ b/openslides/utils/collection.py @@ -352,6 +352,17 @@ class Collection: output.append(content) return output + def as_autoupdate_for_user(self, user): + """ + Returns a list of dicts, that can be send though the websocket to a user. + """ + output = [] + for collection_element in self.element_generator(): + content = collection_element.as_autoupdate_for_user(user) + if content is not None: + output.append(content) + return output + def as_list_for_user(self, user): """ Returns a list of dictonaries to send them to a user, for example over