Merge pull request #1123 from ostcar/overlay_error

dont throw errors in overlay.get_projector_html
This commit is contained in:
Oskar Hahn 2013-11-25 08:13:24 -08:00
commit e1e5ffd14c
8 changed files with 129 additions and 33 deletions

View File

@ -112,10 +112,7 @@ class Item(SlideMixin, MPTTModel):
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
active_slide = get_active_slide()
active_slide_pk = active_slide.get('pk', None)
if (active_slide['callback'] == 'agenda' and
unicode(self.parent_id) == unicode(active_slide_pk)):
if self.parent and self.parent.is_active_slide():
update_projector()
def __unicode__(self):
@ -359,14 +356,10 @@ class Speaker(models.Model):
Checks, if the agenda item, or parts of it, is on the projector.
If yes, it updates the projector.
"""
active_slide = get_active_slide()
active_slide_pk = active_slide.get('pk', None)
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':
if self.item.is_active_slide():
if get_active_slide().get('type', None) == 'list_of_speakers':
update_projector()
elif slide_type is None:
else:
update_projector_overlay('agenda_speaker')
def begin_speach(self):

View File

@ -13,7 +13,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import config, ConfigPage, ConfigVariable
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.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
list-of-speakers slide.
"""
active_slide = get_active_slide()
slide_type = active_slide.get('type', None)
active_slide_pk = active_slide.get('pk', None)
if (active_slide['callback'] == 'agenda' and
slide_type != 'list_of_speakers' and
active_slide_pk is not None):
item = Item.objects.get(pk=active_slide_pk)
slide = get_active_object()
if isinstance(slide, Item):
item = slide
else:
# TODO: If there are more the one items, use the first one in the
# mptt tree that is not closed
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(
old_speakers_count=config['agenda_show_last_speakers'],
coming_speakers_count=5)
context = {
value = render_to_string('agenda/overlay_speaker_projector.html', {
'list_of_speakers': list_of_speakers,
'closed': item.speaker_list_closed,
}
return render_to_string('agenda/overlay_speaker_projector.html', context)
'closed': item.speaker_list_closed})
else:
return None
value = None
return value
return Overlay(name, get_widget_html, get_projector_html)

View File

@ -60,4 +60,4 @@ def agenda_slide(**kwargs):
return slide
register_slide('agenda', agenda_slide)
register_slide('agenda', agenda_slide, Item)

View File

@ -30,4 +30,4 @@ def mediafile_presentation_as_slide(**kwargs):
return render_to_string('mediafile/presentation_slide.html', context)
register_slide('mediafile', mediafile_presentation_as_slide)
register_slide('mediafile', mediafile_presentation_as_slide, Mediafile)

View File

@ -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.
"""
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):
pass
@ -139,11 +147,15 @@ def get_projector_overlays_js(as_json=False):
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
if model is not None:
slide_model[name] = model
def register_slide_model(SlideModel, template):
@ -169,7 +181,7 @@ def register_slide_model(SlideModel, template):
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):
@ -192,6 +204,26 @@ def get_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):
"""
Collects the widgets from all apps and returns the Widget objects as sorted

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.template import RequestContext
from django.template.loader import render_to_string
@ -86,7 +87,15 @@ class Overlay(object):
"""
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):
"""

View File

@ -139,9 +139,14 @@ class ApiFunctions(TestCase):
def test_register_slide(self):
mock_slide_callback = {}
with patch('openslides.projector.api.slide_callback', mock_slide_callback):
projector_api.register_slide('some name', 'some callback')
self.assertEqual(mock_slide_callback, {'some name': 'some callback'})
mock_slide_model = {}
with patch('openslides.projector.api.slide_model', mock_slide_model):
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.register_slide')
@ -156,6 +161,7 @@ class ApiFunctions(TestCase):
projector_api.register_slide_model(mock_SlideModel, 'some template')
used_args, __ = mock_register_slide.call_args
self.assertEqual(used_args[0], 'mock_callback_name')
self.assertEqual(used_args[2], mock_SlideModel)
# Test the generated slide function
used_args[1](pk=1)
@ -182,3 +188,26 @@ class ApiFunctions(TestCase):
with patch('openslides.projector.api.config', mock_config):
value = projector_api.get_active_slide()
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')

View 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)