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

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

View File

@ -60,4 +60,4 @@ def agenda_slide(**kwargs):
return slide 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) 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. 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

View File

@ -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):
""" """

View File

@ -139,9 +139,14 @@ class ApiFunctions(TestCase):
def test_register_slide(self): def test_register_slide(self):
mock_slide_callback = {} mock_slide_callback = {}
mock_slide_model = {}
with patch('openslides.projector.api.slide_model', mock_slide_model):
with patch('openslides.projector.api.slide_callback', mock_slide_callback): with patch('openslides.projector.api.slide_callback', mock_slide_callback):
projector_api.register_slide('some name', 'some callback') projector_api.register_slide('some name', 'some callback')
self.assertEqual(mock_slide_callback, {'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')

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)