Merge pull request #1123 from ostcar/overlay_error
dont throw errors in overlay.get_projector_html
This commit is contained in:
commit
e1e5ffd14c
@ -112,10 +112,7 @@ class Item(SlideMixin, MPTTModel):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
super(Item, self).save(*args, **kwargs)
|
super(Item, self).save(*args, **kwargs)
|
||||||
active_slide = get_active_slide()
|
if self.parent and self.parent.is_active_slide():
|
||||||
active_slide_pk = active_slide.get('pk', None)
|
|
||||||
if (active_slide['callback'] == 'agenda' and
|
|
||||||
unicode(self.parent_id) == unicode(active_slide_pk)):
|
|
||||||
update_projector()
|
update_projector()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
@ -359,14 +356,10 @@ class Speaker(models.Model):
|
|||||||
Checks, if the agenda item, or parts of it, is on the projector.
|
Checks, if the agenda item, or parts of it, is on the projector.
|
||||||
If yes, it updates the projector.
|
If yes, it updates the projector.
|
||||||
"""
|
"""
|
||||||
active_slide = get_active_slide()
|
if self.item.is_active_slide():
|
||||||
active_slide_pk = active_slide.get('pk', None)
|
if get_active_slide().get('type', None) == 'list_of_speakers':
|
||||||
slide_type = active_slide.get('type', None)
|
|
||||||
if (active_slide['callback'] == 'agenda' and
|
|
||||||
unicode(self.item_id) == unicode(active_slide_pk)):
|
|
||||||
if slide_type == 'list_of_speakers':
|
|
||||||
update_projector()
|
update_projector()
|
||||||
elif slide_type is None:
|
else:
|
||||||
update_projector_overlay('agenda_speaker')
|
update_projector_overlay('agenda_speaker')
|
||||||
|
|
||||||
def begin_speach(self):
|
def begin_speach(self):
|
||||||
|
@ -13,7 +13,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
|
|
||||||
from openslides.config.api import config, ConfigPage, ConfigVariable
|
from openslides.config.api import config, ConfigPage, ConfigVariable
|
||||||
from openslides.config.signals import config_signal
|
from openslides.config.signals import config_signal
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide, get_active_object
|
||||||
from openslides.projector.projector import Overlay
|
from openslides.projector.projector import Overlay
|
||||||
from openslides.projector.signals import projector_overlays
|
from openslides.projector.signals import projector_overlays
|
||||||
|
|
||||||
@ -97,23 +97,30 @@ def agenda_list_of_speakers(sender, **kwargs):
|
|||||||
The overlay is only shown on agenda-items and not on the
|
The overlay is only shown on agenda-items and not on the
|
||||||
list-of-speakers slide.
|
list-of-speakers slide.
|
||||||
"""
|
"""
|
||||||
active_slide = get_active_slide()
|
slide = get_active_object()
|
||||||
slide_type = active_slide.get('type', None)
|
if isinstance(slide, Item):
|
||||||
active_slide_pk = active_slide.get('pk', None)
|
item = slide
|
||||||
if (active_slide['callback'] == 'agenda' and
|
else:
|
||||||
slide_type != 'list_of_speakers' and
|
# TODO: If there are more the one items, use the first one in the
|
||||||
active_slide_pk is not None):
|
# mptt tree that is not closed
|
||||||
item = Item.objects.get(pk=active_slide_pk)
|
try:
|
||||||
|
item = Item.objects.filter(
|
||||||
|
content_type=ContentType.objects.get_for_model(slide),
|
||||||
|
object_id=slide.pk)[0]
|
||||||
|
except IndexError:
|
||||||
|
item = None
|
||||||
|
|
||||||
|
if item and get_active_slide().get('type', None) != 'list_of_speakers':
|
||||||
list_of_speakers = item.get_list_of_speakers(
|
list_of_speakers = item.get_list_of_speakers(
|
||||||
old_speakers_count=config['agenda_show_last_speakers'],
|
old_speakers_count=config['agenda_show_last_speakers'],
|
||||||
coming_speakers_count=5)
|
coming_speakers_count=5)
|
||||||
context = {
|
|
||||||
|
value = render_to_string('agenda/overlay_speaker_projector.html', {
|
||||||
'list_of_speakers': list_of_speakers,
|
'list_of_speakers': list_of_speakers,
|
||||||
'closed': item.speaker_list_closed,
|
'closed': item.speaker_list_closed})
|
||||||
}
|
|
||||||
return render_to_string('agenda/overlay_speaker_projector.html', context)
|
|
||||||
else:
|
else:
|
||||||
return None
|
value = None
|
||||||
|
return value
|
||||||
|
|
||||||
return Overlay(name, get_widget_html, get_projector_html)
|
return Overlay(name, get_widget_html, get_projector_html)
|
||||||
|
|
||||||
|
@ -60,4 +60,4 @@ def agenda_slide(**kwargs):
|
|||||||
return slide
|
return slide
|
||||||
|
|
||||||
|
|
||||||
register_slide('agenda', agenda_slide)
|
register_slide('agenda', agenda_slide, Item)
|
||||||
|
@ -30,4 +30,4 @@ def mediafile_presentation_as_slide(**kwargs):
|
|||||||
return render_to_string('mediafile/presentation_slide.html', context)
|
return render_to_string('mediafile/presentation_slide.html', context)
|
||||||
|
|
||||||
|
|
||||||
register_slide('mediafile', mediafile_presentation_as_slide)
|
register_slide('mediafile', mediafile_presentation_as_slide, Mediafile)
|
||||||
|
@ -20,6 +20,14 @@ 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.
|
callable object which returns the html code for a slide.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
slide_model = {}
|
||||||
|
"""
|
||||||
|
A dictonary for SlideMixin models to reference from the slide_callback_name to
|
||||||
|
the Model
|
||||||
|
"""
|
||||||
|
# TODO: Find a bether way to do this. Maybe by reimplementing slides with
|
||||||
|
# metaclasses
|
||||||
|
|
||||||
|
|
||||||
class SlideError(OpenSlidesError):
|
class SlideError(OpenSlidesError):
|
||||||
pass
|
pass
|
||||||
@ -139,11 +147,15 @@ def get_projector_overlays_js(as_json=False):
|
|||||||
return javascript
|
return javascript
|
||||||
|
|
||||||
|
|
||||||
def register_slide(name, callback):
|
def register_slide(name, callback, model=None):
|
||||||
"""
|
"""
|
||||||
Register a function as slide callback.
|
Registers a function as slide callback.
|
||||||
|
|
||||||
|
The optional argument 'model' is used to register a SlideModelClass.
|
||||||
"""
|
"""
|
||||||
slide_callback[name] = callback
|
slide_callback[name] = callback
|
||||||
|
if model is not None:
|
||||||
|
slide_model[name] = model
|
||||||
|
|
||||||
|
|
||||||
def register_slide_model(SlideModel, template):
|
def register_slide_model(SlideModel, template):
|
||||||
@ -169,7 +181,7 @@ def register_slide_model(SlideModel, template):
|
|||||||
|
|
||||||
return render_to_string(template, context)
|
return render_to_string(template, context)
|
||||||
|
|
||||||
register_slide(SlideModel.slide_callback_name, model_slide)
|
register_slide(SlideModel.slide_callback_name, model_slide, SlideModel)
|
||||||
|
|
||||||
|
|
||||||
def set_active_slide(callback, **kwargs):
|
def set_active_slide(callback, **kwargs):
|
||||||
@ -192,6 +204,26 @@ def get_active_slide():
|
|||||||
return config['projector_active_slide']
|
return config['projector_active_slide']
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_object():
|
||||||
|
"""
|
||||||
|
Returns an object if the active slide is an instance of SlideMixin.
|
||||||
|
In other case, returns None
|
||||||
|
"""
|
||||||
|
active_slide_dict = get_active_slide()
|
||||||
|
callback_name = active_slide_dict.get('callback', None)
|
||||||
|
object_pk = active_slide_dict.get('pk', None)
|
||||||
|
try:
|
||||||
|
Model = slide_model[callback_name]
|
||||||
|
except KeyError:
|
||||||
|
value = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
value = Model.objects.get(pk=object_pk)
|
||||||
|
except Model.DoesNotExist:
|
||||||
|
value = None
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def get_all_widgets(request, session=False):
|
def get_all_widgets(request, session=False):
|
||||||
"""
|
"""
|
||||||
Collects the widgets from all apps and returns the Widget objects as sorted
|
Collects the widgets from all apps and returns the Widget objects as sorted
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
@ -86,7 +87,15 @@ class Overlay(object):
|
|||||||
"""
|
"""
|
||||||
Returns the html code for the projector.
|
Returns the html code for the projector.
|
||||||
"""
|
"""
|
||||||
return self.get_html_wrapper(self.projector_html_callback())
|
try:
|
||||||
|
value = self.get_html_wrapper(self.projector_html_callback())
|
||||||
|
except Exception as exception:
|
||||||
|
if settings.DEBUG:
|
||||||
|
raise exception
|
||||||
|
else:
|
||||||
|
# Catch all errors, so an overlay can not kill the projector
|
||||||
|
value = ''
|
||||||
|
return value
|
||||||
|
|
||||||
def get_javascript(self):
|
def get_javascript(self):
|
||||||
"""
|
"""
|
||||||
|
@ -139,9 +139,14 @@ class ApiFunctions(TestCase):
|
|||||||
|
|
||||||
def test_register_slide(self):
|
def test_register_slide(self):
|
||||||
mock_slide_callback = {}
|
mock_slide_callback = {}
|
||||||
with patch('openslides.projector.api.slide_callback', mock_slide_callback):
|
mock_slide_model = {}
|
||||||
projector_api.register_slide('some name', 'some callback')
|
with patch('openslides.projector.api.slide_model', mock_slide_model):
|
||||||
self.assertEqual(mock_slide_callback, {'some name': 'some callback'})
|
with patch('openslides.projector.api.slide_callback', mock_slide_callback):
|
||||||
|
projector_api.register_slide('some name', 'some callback')
|
||||||
|
projector_api.register_slide('other name', 'other callback', 'Model')
|
||||||
|
self.assertEqual(mock_slide_callback, {'some name': 'some callback',
|
||||||
|
'other name': 'other callback'})
|
||||||
|
self.assertEqual(mock_slide_model, {'other name': 'Model'})
|
||||||
|
|
||||||
@patch('openslides.projector.api.render_to_string')
|
@patch('openslides.projector.api.render_to_string')
|
||||||
@patch('openslides.projector.api.register_slide')
|
@patch('openslides.projector.api.register_slide')
|
||||||
@ -156,6 +161,7 @@ class ApiFunctions(TestCase):
|
|||||||
projector_api.register_slide_model(mock_SlideModel, 'some template')
|
projector_api.register_slide_model(mock_SlideModel, 'some template')
|
||||||
used_args, __ = mock_register_slide.call_args
|
used_args, __ = mock_register_slide.call_args
|
||||||
self.assertEqual(used_args[0], 'mock_callback_name')
|
self.assertEqual(used_args[0], 'mock_callback_name')
|
||||||
|
self.assertEqual(used_args[2], mock_SlideModel)
|
||||||
|
|
||||||
# Test the generated slide function
|
# Test the generated slide function
|
||||||
used_args[1](pk=1)
|
used_args[1](pk=1)
|
||||||
@ -182,3 +188,26 @@ class ApiFunctions(TestCase):
|
|||||||
with patch('openslides.projector.api.config', mock_config):
|
with patch('openslides.projector.api.config', mock_config):
|
||||||
value = projector_api.get_active_slide()
|
value = projector_api.get_active_slide()
|
||||||
self.assertEqual(value, 'value')
|
self.assertEqual(value, 'value')
|
||||||
|
|
||||||
|
def test_get_active_object(self):
|
||||||
|
mock_Model = MagicMock()
|
||||||
|
mock_Model.DoesNotExist = Exception
|
||||||
|
mock_slide_model = {'mock_model': mock_Model}
|
||||||
|
mock_active_slide = {'callback': 'unknown'}
|
||||||
|
mock_get_active_slide = MagicMock(return_value=mock_active_slide)
|
||||||
|
|
||||||
|
with patch('openslides.projector.api.get_active_slide', mock_get_active_slide):
|
||||||
|
with patch('openslides.projector.api.slide_model', mock_slide_model):
|
||||||
|
# test unknwon slide_callback_name
|
||||||
|
self.assertIsNone(projector_api.get_active_object())
|
||||||
|
|
||||||
|
# test unknown object
|
||||||
|
mock_Model.objects.get.side_effect = Exception
|
||||||
|
mock_active_slide.update(callback='mock_model', pk=42)
|
||||||
|
self.assertIsNone(projector_api.get_active_object())
|
||||||
|
mock_Model.objects.get.assert_called_with(pk=42)
|
||||||
|
|
||||||
|
# test success
|
||||||
|
mock_Model.objects.get.side_effect = None
|
||||||
|
mock_Model.objects.get.return_value = 'success'
|
||||||
|
self.assertEqual(projector_api.get_active_object(), 'success')
|
||||||
|
26
tests/projector/test_overlays.py
Normal file
26
tests/projector/test_overlays.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from mock import MagicMock, patch
|
||||||
|
|
||||||
|
from openslides.projector.projector import Overlay
|
||||||
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class OverlayTest(TestCase):
|
||||||
|
def test_error_in_html(self):
|
||||||
|
"""
|
||||||
|
Tests that the methof get_projector_html does not raise any errors.
|
||||||
|
"""
|
||||||
|
get_projector_html = MagicMock(side_effect=Exception('no good error'))
|
||||||
|
overlay = Overlay('test_overlay', lambda: 'widget_html', get_projector_html)
|
||||||
|
|
||||||
|
# Test in productive mode
|
||||||
|
with patch('openslides.projector.projector.settings.DEBUG', False):
|
||||||
|
self.assertEqual(overlay.get_projector_html(), '')
|
||||||
|
|
||||||
|
# Test in debug mode
|
||||||
|
with patch('openslides.projector.projector.settings.DEBUG', True):
|
||||||
|
self.assertRaisesMessage(
|
||||||
|
Exception,
|
||||||
|
'no good error',
|
||||||
|
overlay.get_projector_html)
|
Loading…
Reference in New Issue
Block a user