2012-04-25 22:29:19 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
openslides.projector.api
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Useful functions for the projector app.
|
|
|
|
|
|
|
|
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
|
|
|
:license: GNU GPL, see LICENSE for more details.
|
|
|
|
"""
|
|
|
|
|
2013-10-07 08:52:18 +02:00
|
|
|
from time import time
|
2013-08-04 12:59:11 +02:00
|
|
|
|
2012-08-15 13:42:25 +02:00
|
|
|
from django.conf import settings
|
2013-09-25 10:01:01 +02:00
|
|
|
from django.template.loader import render_to_string
|
2012-08-15 13:42:25 +02:00
|
|
|
from django.utils.datastructures import SortedDict
|
|
|
|
from django.utils.importlib import import_module
|
2012-02-03 23:12:28 +01:00
|
|
|
|
2013-03-01 17:13:12 +01:00
|
|
|
from openslides.config.api import config
|
2013-08-04 12:59:11 +02:00
|
|
|
from openslides.utils.tornado_webserver import ProjectorSocketHandler
|
|
|
|
|
2013-09-25 10:01:01 +02:00
|
|
|
from .signals import projector_overlays
|
2012-06-11 13:43:48 +02:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
slide_callback = {}
|
|
|
|
"""
|
|
|
|
A dictonary where the key is the name of a slide, and the value is a
|
|
|
|
callable object which returns the html code for a slide.
|
|
|
|
"""
|
2012-02-03 23:12:28 +01:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
|
|
|
|
def update_projector():
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
Sends the data to the clients, who listen to the projector.
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
# TODO: only send necessary html
|
|
|
|
ProjectorSocketHandler.send_updates({'content': get_projector_content()})
|
2012-02-03 23:12:28 +01:00
|
|
|
|
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
def update_projector_overlay(overlay):
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-10-03 21:49:51 +02:00
|
|
|
Update one or all overlay on the projector.
|
2013-08-04 12:59:11 +02:00
|
|
|
|
|
|
|
Checks if the overlay is activated and updates it in this case.
|
|
|
|
|
2013-10-03 21:49:51 +02:00
|
|
|
The argument 'overlay' has to be an overlay object, the name of a
|
|
|
|
ovleray or None. If it is None, all overlays will be updated.
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
if overlay is None:
|
2013-09-25 10:01:01 +02:00
|
|
|
overlays = [item for item in get_overlays().values()]
|
2013-10-03 21:49:51 +02:00
|
|
|
elif isinstance(overlay, basestring):
|
|
|
|
overlays = [get_overlays()[overlay]]
|
2013-08-04 12:59:11 +02:00
|
|
|
else:
|
|
|
|
overlays = [overlay]
|
|
|
|
|
|
|
|
overlay_dict = {}
|
|
|
|
for overlay in overlays:
|
|
|
|
if overlay.is_active():
|
|
|
|
overlay_dict[overlay.name] = {
|
|
|
|
'html': overlay.get_projector_html(),
|
|
|
|
'javascript': overlay.get_javascript()}
|
|
|
|
else:
|
|
|
|
overlay_dict[overlay.name] = None
|
|
|
|
ProjectorSocketHandler.send_updates({'overlays': overlay_dict})
|
|
|
|
|
|
|
|
|
2013-10-17 15:32:54 +02:00
|
|
|
def call_on_projector(calls):
|
|
|
|
"""
|
|
|
|
Sends data to the projector.
|
2013-10-29 18:28:05 +01:00
|
|
|
|
|
|
|
The argument call has to be a dictionary with the javascript function name
|
|
|
|
as key and the argument for it as value.
|
2013-10-17 15:32:54 +02:00
|
|
|
"""
|
|
|
|
projector_js_cache = config['projector_js_cache']
|
|
|
|
projector_js_cache.update(calls)
|
|
|
|
config['projector_js_cache'] = projector_js_cache
|
2013-10-28 21:17:32 +01:00
|
|
|
ProjectorSocketHandler.send_updates({'calls': calls})
|
2013-10-17 15:32:54 +02:00
|
|
|
|
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
def get_projector_content(slide_dict=None):
|
|
|
|
"""
|
2013-10-29 18:28:05 +01:00
|
|
|
Returns the HTML-Content block for the projector.
|
|
|
|
|
|
|
|
Slide_dict has to be an dictonary with the key 'callback'.
|
|
|
|
|
|
|
|
If slide_dict is None, use the active slide from the database.
|
2013-08-04 12:59:11 +02:00
|
|
|
"""
|
|
|
|
if slide_dict is None:
|
|
|
|
slide_dict = config['projector_active_slide'].copy()
|
|
|
|
callback = slide_dict.pop('callback', None)
|
2012-04-17 12:07:32 +02:00
|
|
|
|
2012-04-16 16:35:30 +02:00
|
|
|
try:
|
2013-10-29 18:28:05 +01:00
|
|
|
slide_content = slide_callback[callback](**slide_dict)
|
2012-04-16 16:35:30 +02:00
|
|
|
except KeyError:
|
2013-10-29 18:28:05 +01:00
|
|
|
slide_content = default_slide()
|
|
|
|
return slide_content
|
2012-04-16 16:35:30 +02:00
|
|
|
|
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
def default_slide():
|
|
|
|
"""
|
|
|
|
Returns the HTML Code for the default slide.
|
2012-02-03 23:12:28 +01:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
return render_to_string('projector/default_slide.html')
|
2012-02-03 23:12:28 +01:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
|
|
|
|
def get_overlays():
|
2012-02-03 23:12:28 +01:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
Returns all overlay objects.
|
2012-02-03 23:12:28 +01:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
The returned value is a dictonary with the name of the overlay as key, and
|
|
|
|
the overlay object as value.
|
|
|
|
"""
|
|
|
|
overlays = {}
|
|
|
|
for receiver, overlay in projector_overlays.send(sender='get_overlays'):
|
|
|
|
overlays[overlay.name] = overlay
|
|
|
|
return overlays
|
2012-02-03 23:12:28 +01:00
|
|
|
|
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
def get_projector_overlays():
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
Returns the HTML code for all active overlays.
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
overlays = [{'name': key, 'html': overlay.get_projector_html()}
|
|
|
|
for key, overlay in get_overlays().items()
|
|
|
|
if overlay.is_active()]
|
|
|
|
return render_to_string('projector/all_overlays.html', {'overlays': overlays})
|
2012-10-29 23:26:10 +01:00
|
|
|
|
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
def get_projector_overlays_js():
|
|
|
|
"""
|
2013-10-29 18:28:05 +01:00
|
|
|
Returns JS-Code for the active overlays.
|
2012-02-09 02:29:38 +01:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
The retuned value is a list of json objects.
|
|
|
|
"""
|
2013-09-25 10:01:01 +02:00
|
|
|
javascript = []
|
|
|
|
for overlay in get_overlays().values():
|
2013-08-04 12:59:11 +02:00
|
|
|
if overlay.is_active():
|
|
|
|
overlay_js = overlay.get_javascript()
|
|
|
|
if overlay_js:
|
2013-10-28 21:17:32 +01:00
|
|
|
javascript.append(overlay_js)
|
2013-09-25 10:01:01 +02:00
|
|
|
return javascript
|
2012-02-09 02:29:38 +01:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
|
|
|
|
def register_slide(name, callback):
|
|
|
|
"""
|
|
|
|
Register a function as slide callback.
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
slide_callback[name] = callback
|
|
|
|
|
|
|
|
|
2013-10-03 21:49:51 +02:00
|
|
|
def register_slide_model(SlideModel, template):
|
|
|
|
"""
|
|
|
|
Shortcut for register_slide for a Model with the SlideMixin.
|
|
|
|
|
|
|
|
The Argument 'SlideModel' has to be a Django-Model-Class, which is a subclass
|
|
|
|
of SlideMixin. Template has to be a string to the path of a template.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def model_slide(**kwargs):
|
|
|
|
"""
|
|
|
|
Return the html code for the model slide.
|
|
|
|
"""
|
|
|
|
slide_pk = kwargs.get('pk', None)
|
|
|
|
|
|
|
|
try:
|
|
|
|
slide = SlideModel.objects.get(pk=slide_pk)
|
|
|
|
except SlideModel.DoesNotExist:
|
|
|
|
slide = None
|
|
|
|
context = {'slide': None}
|
|
|
|
else:
|
|
|
|
context = slide.get_slide_context()
|
|
|
|
|
|
|
|
return render_to_string(template, context)
|
|
|
|
|
|
|
|
register_slide(SlideModel.slide_callback_name, model_slide)
|
|
|
|
|
|
|
|
|
2013-09-25 10:01:01 +02:00
|
|
|
def set_active_slide(callback, kwargs=None):
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
Set the active Slide.
|
2012-04-14 11:18:47 +02:00
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
callback: The name of the slide callback.
|
|
|
|
kwargs: Keyword arguments for the slide callback.
|
|
|
|
"""
|
2013-09-25 10:01:01 +02:00
|
|
|
kwargs = kwargs or {}
|
2013-08-04 12:59:11 +02:00
|
|
|
kwargs.update(callback=callback)
|
|
|
|
config['projector_active_slide'] = kwargs
|
|
|
|
update_projector()
|
|
|
|
update_projector_overlay(None)
|
2012-02-03 23:12:28 +01:00
|
|
|
|
|
|
|
|
2013-08-04 12:59:11 +02:00
|
|
|
def get_active_slide():
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-10-03 21:49:51 +02:00
|
|
|
Returns the dictonary, which defines the active slide.
|
2012-07-07 14:01:40 +02:00
|
|
|
"""
|
2013-08-04 12:59:11 +02:00
|
|
|
return config['projector_active_slide']
|
2012-04-16 16:35:30 +02:00
|
|
|
|
|
|
|
|
2012-08-15 13:42:25 +02:00
|
|
|
def get_all_widgets(request, session=False):
|
2013-06-13 23:38:58 +02:00
|
|
|
"""
|
|
|
|
Collects the widgets from all apps and returns the Widget objects as sorted
|
|
|
|
dictionary.
|
|
|
|
|
|
|
|
The session flag decides whether to return only the widgets which are
|
|
|
|
active, that means that they are mentioned in the session.
|
|
|
|
"""
|
2013-09-07 15:09:37 +02:00
|
|
|
all_module_widgets = []
|
2012-08-15 13:42:25 +02:00
|
|
|
for app in settings.INSTALLED_APPS:
|
|
|
|
try:
|
|
|
|
mod = import_module(app + '.views')
|
|
|
|
except ImportError:
|
|
|
|
continue
|
|
|
|
try:
|
2013-08-04 12:59:11 +02:00
|
|
|
mod_get_widgets = mod.get_widgets
|
2012-08-15 13:42:25 +02:00
|
|
|
except AttributeError:
|
|
|
|
continue
|
2013-08-04 12:59:11 +02:00
|
|
|
else:
|
|
|
|
module_widgets = mod_get_widgets(request)
|
2013-09-07 15:09:37 +02:00
|
|
|
all_module_widgets.extend(module_widgets)
|
|
|
|
all_module_widgets.sort(key=lambda widget: widget.default_weight)
|
|
|
|
session_widgets = request.session.get('widgets', {})
|
|
|
|
widgets = SortedDict()
|
|
|
|
for widget in all_module_widgets:
|
|
|
|
if (widget.permission_required is None or
|
|
|
|
request.user.has_perm(widget.permission_required)):
|
|
|
|
if not session or session_widgets.get(widget.get_name(), True):
|
|
|
|
widgets[widget.get_name()] = widget
|
2012-08-15 13:42:25 +02:00
|
|
|
return widgets
|
2013-10-07 08:52:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def start_countdown():
|
|
|
|
"""
|
|
|
|
Starts the countdown
|
|
|
|
"""
|
|
|
|
# if we had stopped the countdown resume were we left of
|
|
|
|
if config['countdown_state'] == 'paused':
|
|
|
|
start_stamp = config['countdown_start_stamp']
|
|
|
|
pause_stamp = config['countdown_pause_stamp']
|
|
|
|
now = time()
|
|
|
|
config['countdown_start_stamp'] = now - \
|
|
|
|
(pause_stamp - start_stamp)
|
|
|
|
else:
|
|
|
|
config['countdown_start_stamp'] = time()
|
|
|
|
|
|
|
|
config['countdown_state'] = 'active'
|
|
|
|
config['countdown_pause_stamp'] = 0
|
|
|
|
|
|
|
|
|
|
|
|
def stop_countdown():
|
|
|
|
"""
|
|
|
|
Stops the countdown
|
|
|
|
"""
|
|
|
|
if config['countdown_state'] == 'active':
|
|
|
|
config['countdown_state'] = 'paused'
|
|
|
|
config['countdown_pause_stamp'] = time()
|
|
|
|
|
|
|
|
|
|
|
|
def reset_countdown():
|
|
|
|
"""
|
|
|
|
Resets the countdown
|
|
|
|
"""
|
|
|
|
config['countdown_start_stamp'] = time()
|
|
|
|
config['countdown_pause_stamp'] = 0
|
|
|
|
config['countdown_state'] = 'inactive'
|