clean up the projector App

This commit is contained in:
Oskar Hahn 2012-07-07 14:01:40 +02:00
parent eb3adcd8ca
commit 15621b7702
8 changed files with 203 additions and 164 deletions

View File

@ -17,6 +17,9 @@ from openslides.projector.projector import SLIDE, Slide, Widget
def split_sid(sid):
"""
Slit a SID in the model-part and in the model-id
"""
try:
data = sid.split('-')
except AttributeError:
@ -34,6 +37,11 @@ def split_sid(sid):
def get_slide_from_sid(sid, element=False):
"""
Return the Slide for an given sid.
If element== False, return the slide-dict,
else, return the object.
"""
try:
key, id = split_sid(sid)
except TypeError:
@ -56,10 +64,10 @@ def get_slide_from_sid(sid, element=False):
def get_active_slide(only_sid=False):
"""
Returns the active slide. If no slide is active, or it can not find an Item,
it raise an error
return None
if only_sid is True, returns only the id of this item. Returns None if not Item
is active. Does not Raise Item.DoesNotExist
if only_sid is True, returns only the id of this item. Returns None if not
Item is active.
"""
sid = config["presentation"]
@ -69,12 +77,18 @@ def get_active_slide(only_sid=False):
def set_active_slide(sid, argument=None):
"""
Set the active Slide.
"""
config["presentation"] = sid
config['presentation_argument'] = argument
def register_slidemodel(model, model_name=None, control_template=None, weight=0):
#TODO: Warn if there already is a slide with this prefix
def register_slidemodel(model, model_name=None, control_template=None,
weight=0):
"""
Register a Model as a slide.
"""
if model_name is None:
model_name = model.prefix
@ -94,7 +108,9 @@ def register_slidemodel(model, model_name=None, control_template=None, weight=0)
def register_slidefunc(key, func, control_template=None, weight=0, name=''):
#TODO: Warn if there already is a slide with this prefix
"""
Register a function for as a slide.
"""
if control_template is None:
control_template = 'projector/default_control_slidefunc.html'
category = func.__module__.split('.')[0]
@ -110,6 +126,10 @@ def register_slidefunc(key, func, control_template=None, weight=0, name=''):
def projector_message_set(message, sid=None):
"""
Set the overlay-message.
if sid is set, only show the message on the sid-slide.
"""
from models import ProjectorOverlay
config['projector_message'] = message
try:
@ -121,4 +141,7 @@ def projector_message_set(message, sid=None):
def projector_message_delete():
"""
Delete the overlay-message.
"""
config['projector_message'] = ''

View File

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""
openslides.projector.models
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Models for the projector app.
@ -12,22 +12,25 @@
from django.db import models
from django.dispatch import receiver
from django.utils.translation import ugettext as _, ugettext_noop
from openslides.config.signals import default_config_value
from api import register_slidemodel
from projector import SlideMixin
from openslides.projector.api import register_slidemodel
from openslides.projector.projector import SlideMixin
from openslides.config.models import config
from config.models import config
from utils.translation_ext import ugettext as _
class ProjectorSlide(models.Model, SlideMixin):
"""
Model for Slides, only for the projector. Also called custom slides.
"""
prefix = 'ProjectorSlide'
title = models.CharField(max_length=256, verbose_name=_("Title"))
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
#weight = models.IntegerField(default=0, verbose_name=_("Weight"))
def slide(self):
return {
@ -46,12 +49,19 @@ class ProjectorSlide(models.Model, SlideMixin):
class Meta:
permissions = (
('can_manage_projector', _("Can manage the projector", fixstr=True)),
('can_see_projector', _("Can see projector", fixstr=True)),
('can_manage_projector', ugettext_noop("Can manage the projector")),
('can_see_projector', ugettext_noop("Can see projector")),
)
register_slidemodel(ProjectorSlide,
control_template='projector/control_customslide.html')
class ProjectorOverlay(models.Model):
"""
Save information for a overlay.
"""
active = models.BooleanField(verbose_name=_('Active'))
def_name = models.CharField(max_length=64)
sid = models.CharField(max_length=64, null=True, blank=True)
@ -62,9 +72,6 @@ class ProjectorOverlay(models.Model):
return self.def_name
register_slidemodel(ProjectorSlide, control_template='projector/control_customslide.html')
@receiver(default_config_value, dispatch_uid="projector_default_config")
def default_config(sender, key, **kwargs):
return {

View File

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""
openslides.projector.projector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Slide functions for the projector app.
@ -29,7 +29,7 @@ class SlideMixin(object):
def slide(self):
"""
Return a map with all Data for a Slide
Return a map with all Data for the Slide.
"""
return {
'slide': self,
@ -47,19 +47,22 @@ class SlideMixin(object):
@property
def active(self):
"""
Return True, if the the slide is the active one.
Return True, if the the slide is the active slide.
"""
from api import get_active_slide
return True if get_active_slide(only_sid=True) == self.sid else False
return get_active_slide(only_sid=True) == self.sid
def set_active(self):
"""
Appoint this item as the active one.
Appoint this item as the active slide.
"""
config["presentation"] = "%s-%d" % (self.prefix, self.id)
set_active_slide(self.sid)
class Slide(object):
"""
Represents a Slide for the projector. Can be a modelinstanz, or a function.
"""
def __init__(self, model_slide=False, func=None, model=None, category=None,
key=None, model_name='', control_template='', weight=0, name=''):
"""
@ -82,10 +85,16 @@ class Slide(object):
@property
def active(self):
"""
Return True if the Slide is active, else: False.
"""
from api import get_active_slide
return get_active_slide(True) == self.key
def get_items(self):
"""
If the Slide is a Slide from a Model, return all Objects.
"""
try:
return self.model.objects.all()
except AttributeError:
@ -93,6 +102,9 @@ class Slide(object):
class Widget(object):
"""
Class for a Widget for the Projector-Tab.
"""
def __init__(self, name, html=None, template=None, context={}):
self.name = name
if html is not None:

View File

@ -13,5 +13,3 @@
from django.dispatch import Signal
projector_overlays = Signal(providing_args=['register', 'call'])
projector_control_box = Signal()

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.projector.tests
~~~~~~~~~~~~~~~~~~~~~~~~~~
Unit tests for the projector app.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

View File

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""
openslides.projector.urls
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~
URL list for the projector app.
@ -10,11 +10,8 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults import *
from django.conf.urls.defaults import patterns, url
from openslides.utils.views import CreateView
from openslides.projector.models import ProjectorSlide
from openslides.projector.views import (ControlView, ActivateView,
CustomSlideCreateView, CustomSlideUpdateView, CustomSlideDeleteView,
CountdownEdit, ProjectorEdit, Projector, ActivateOverlay)
@ -104,12 +101,6 @@ urlpatterns = patterns('projector.views',
name='projector_clean',
),
# TODO: Merge the following lines with this one:
## url(r'^countdown/(?P<command>[^/]*)/$',
## CountdownEdit.as_view(),
## name='countdown_edit',
## ),
url(r'^countdown/reset/$',
CountdownEdit.as_view(),
{'command': 'reset'},

View File

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""
openslides.projector.views
~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
Views for the projector app.
@ -13,41 +13,48 @@
from datetime import datetime
from time import time
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.db.models import Q
from django.dispatch import receiver
from django.utils.datastructures import SortedDict
from django.utils.importlib import import_module
from django.dispatch import receiver
from django.db.models import Q
from django.conf import settings
from django.utils.translation import ugettext as _
from openslides.utils.template import render_block_to_string, Tab
from openslides.utils.utils import html_strong
from openslides.utils.views import (TemplateView, RedirectView, CreateView,
UpdateView, DeleteView, AjaxMixin)
from openslides.utils.template import render_block_to_string, Tab
from openslides.config.models import config
from openslides.projector.api import (get_active_slide, set_active_slide,
projector_message_set, projector_message_delete, get_slide_from_sid)
from openslides.projector.projector import SLIDE, Widget
from openslides.projector.models import ProjectorOverlay, ProjectorSlide
from openslides.projector.projector import SLIDE, Widget
from openslides.projector.signals import projector_overlays
class ControlView(TemplateView, AjaxMixin):
"""
Overview over all possible slides, the overlays and a liveview.
"""
template_name = 'projector/control.html'
permission_required = 'projector.can_manage_projector'
def get_projector_overlays(self):
overlays = []
for receiver, name in projector_overlays.send(sender='registerer', register=True):
for receiver, name in projector_overlays.send(sender='registerer',
register=True):
if name is not None:
try:
projector_overlay = ProjectorOverlay.objects.get(def_name=name)
projector_overlay = ProjectorOverlay.objects.get(
def_name=name)
except ProjectorOverlay.DoesNotExist:
active = name == 'Message'
projector_overlay = ProjectorOverlay(def_name=name, active=active)
projector_overlay = ProjectorOverlay(def_name=name,
active=active)
projector_overlay.save()
overlays.append(projector_overlay)
return overlays
@ -57,13 +64,6 @@ class ControlView(TemplateView, AjaxMixin):
projector_message_set(request.POST['message_text'])
elif 'message-clean' in request.POST:
projector_message_delete()
else:
for overlay in self.get_projector_overlays():
if overlay.def_name in request.POST:
overlay.active = True
else:
overlay.active = False
overlay.save()
if request.is_ajax():
return self.ajax_get(request, *args, **kwargs)
return self.get(request, *args, **kwargs)
@ -101,87 +101,10 @@ class ControlView(TemplateView, AjaxMixin):
return context
class ActivateOverlay(RedirectView):
url = 'projector_control'
allow_ajax = True
@property
def overlay(self):
try:
return self._overlay
except AttributeError:
self._overlay = ProjectorOverlay.objects.get(def_name=self.kwargs['name'])
return self._overlay
def pre_redirect(self, request, *args, **kwargs):
if kwargs['activate']:
self.overlay.active = True
else:
self.overlay.active = False
self.overlay.save()
def get_ajax_context(self, **kwargs):
return {
'active': self.overlay.active,
'def_name': self.overlay.def_name,
}
class ActivateView(RedirectView):
url = 'projector_control'
allow_ajax = True
def pre_redirect(self, request, *args, **kwargs):
try:
set_active_slide(kwargs['sid'], kwargs['argument'])
except KeyError:
set_active_slide(kwargs['sid'])
config['up'] = 0
config['bigger'] = 100
class CustomSlideCreateView(CreateView):
permission_required = 'agenda.can_manage_agenda'
template_name = 'projector/new.html'
model = ProjectorSlide
context_object_name = 'customslide'
success_url = 'projector_control'
apply_url = 'customslide_edit'
def get_success_url(self):
messages.success(self.request, _("Custom slide <b>%s</b> was successfully created.") % self.request.POST['title'])
if 'apply' in self.request.POST:
return reverse(self.get_apply_url(), args=[self.object.id])
return reverse(super(CreateView, self).get_success_url())
class CustomSlideUpdateView(UpdateView):
permission_required = 'projector.can_manage_projector'
template_name = 'projector/new.html'
model = ProjectorSlide
context_object_name = 'customslide'
success_url = 'projector_control'
apply_url = 'customslide_edit'
def get_success_url(self):
messages.success(self.request, _("Custom slide <b>%s</b> was successfully modified.") % self.request.POST['title'])
if 'apply' in self.request.POST:
return ''
return reverse(super(UpdateView, self).get_success_url())
class CustomSlideDeleteView(DeleteView):
permission_required = 'projector.can_manage_projector'
model = ProjectorSlide
url = 'projector_control'
def pre_post_redirect(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
messages.success(request, _("Custom slide <b>%s</b> was successfully deleted.") % self.object)
class Projector(TemplateView, AjaxMixin):
"""
The Projector-Page.
"""
permission_required = 'projector.can_see_projector'
@property
@ -211,8 +134,11 @@ class Projector(TemplateView, AjaxMixin):
# Projector Overlays
if self.kwargs['sid'] is None:
active_defs = ProjectorOverlay.objects.filter(active=True).filter(Q(sid=sid) | Q(sid=None)).values_list('def_name', flat=True)
for receiver, response in projector_overlays.send(sender=sid, register=False, call=active_defs):
active_defs = ProjectorOverlay.objects.filter(active=True) \
.filter(Q(sid=sid) | Q(sid=None)).values_list('def_name',
flat=True)
for receiver, response in projector_overlays.send(sender=sid,
register=False, call=active_defs):
if response is not None:
data['overlays'].append(response)
self._data = data
@ -227,8 +153,10 @@ class Projector(TemplateView, AjaxMixin):
return context
def get_ajax_context(self, **kwargs):
content = render_block_to_string(self.get_template_names()[0], 'content', self.data)
scrollcontent = render_block_to_string(self.get_template_names()[0], 'scrollcontent', self.data)
content = render_block_to_string(self.get_template_names()[0],
'content', self.data)
scrollcontent = render_block_to_string(self.get_template_names()[0],
'scrollcontent', self.data)
context = super(Projector, self).get_ajax_context(**kwargs)
content_hash = hash(content)
@ -250,8 +178,27 @@ class Projector(TemplateView, AjaxMixin):
return super(Projector, self).get(request, *args, **kwargs)
class ActivateView(RedirectView):
"""
Activate a Slide.
"""
permission_required = 'projector.can_manage_projector'
url = 'projector_control'
allow_ajax = True
def pre_redirect(self, request, *args, **kwargs):
try:
set_active_slide(kwargs['sid'], kwargs['argument'])
except KeyError:
set_active_slide(kwargs['sid'])
config['up'] = 0
config['bigger'] = 100
class ProjectorEdit(RedirectView):
"""
Scale or scroll the projector.
"""
permission_required = 'projector.can_manage_projector'
url = 'projector_control'
allow_ajax = True
@ -273,6 +220,9 @@ class ProjectorEdit(RedirectView):
class CountdownEdit(RedirectView):
"""
Start, stop or reset the countdown.
"""
permission_required = 'projector.can_manage_projector'
url = 'projector_control'
allow_ajax = True
@ -293,8 +243,8 @@ class CountdownEdit(RedirectView):
start_stamp = config['countdown_start_stamp']
pause_stamp = config['countdown_pause_stamp']
now = time()
config['countdown_start_stamp'] = now - (pause_stamp - start_stamp)
config['countdown_start_stamp'] = now - \
(pause_stamp - start_stamp)
else:
config['countdown_start_stamp'] = time()
@ -306,7 +256,8 @@ class CountdownEdit(RedirectView):
config['countdown_state'] = 'paused'
elif command == 'set-default':
try:
config['countdown_time'] = int(self.request.GET['countdown_time'])
config['countdown_time'] = \
int(self.request.GET['countdown_time'])
except ValueError:
pass
except AttributeError:
@ -319,7 +270,74 @@ class CountdownEdit(RedirectView):
}
class ActivateOverlay(RedirectView):
"""
Activate or deactivate an overlay.
"""
url = 'projector_control'
allow_ajax = True
permission_required = 'projector.can_manage_projector'
@property
def overlay(self):
try:
return self._overlay
except AttributeError:
self._overlay = ProjectorOverlay.objects.get(
def_name=self.kwargs['name'])
return self._overlay
def pre_redirect(self, request, *args, **kwargs):
if kwargs['activate']:
self.overlay.active = True
else:
self.overlay.active = False
self.overlay.save()
def get_ajax_context(self, **kwargs):
return {
'active': self.overlay.active,
'def_name': self.overlay.def_name,
}
class CustomSlideCreateView(CreateView):
"""
Create a custom slide.
"""
permission_required = 'agenda.can_manage_agenda'
template_name = 'projector/new.html'
model = ProjectorSlide
context_object_name = 'customslide'
success_url = 'projector_control'
apply_url = 'customslide_edit'
class CustomSlideUpdateView(UpdateView):
"""
Update a custom slide.
"""
permission_required = 'projector.can_manage_projector'
template_name = 'projector/new.html'
model = ProjectorSlide
context_object_name = 'customslide'
success_url = 'projector_control'
apply_url = 'customslide_edit'
class CustomSlideDeleteView(DeleteView):
"""
Delete a custom slide.
"""
permission_required = 'projector.can_manage_projector'
model = ProjectorSlide
url = 'projector_control'
def register_tab(request):
"""
Register the projector tab.
"""
selected = True if request.path.startswith('/projector/') else False
return Tab(
title=_('Projector'),
@ -330,6 +348,9 @@ def register_tab(request):
def get_widgets(request):
"""
Return the custom slide widget.
"""
return [
Widget(
name='projector',

View File

@ -52,7 +52,7 @@ from django.views.generic.list import TemplateResponseMixin
from openslides.config.models import config
from openslides.utils.utils import render_to_forbitten
from openslides.utils.utils import render_to_forbitten, html_strong
from openslides.utils.signals import template_manipulation
from openslides.utils.pdf import firstPage, laterPages
@ -164,6 +164,7 @@ class FormView(PermissionMixin, _FormView):
class UpdateView(PermissionMixin, _UpdateView):
def get_success_url(self):
messages.success(self.request, self.get_success_message())
if 'apply' in self.request.POST:
return ''
return reverse(super(UpdateView, self).get_success_url())
@ -177,9 +178,13 @@ class UpdateView(PermissionMixin, _UpdateView):
messages.error(self.request, _('Please check the form for errors.'))
return super(UpdateView, self).form_invalid(form)
def get_success_message(self):
return _('%s was successfully modified.') % html_strong(self.object)
class CreateView(PermissionMixin, _CreateView):
def get_success_url(self):
messages.success(self.request, self.get_success_message())
if 'apply' in self.request.POST:
return reverse(self.get_apply_url(), args=[self.object.id])
return reverse(super(CreateView, self).get_success_url())
@ -197,13 +202,16 @@ class CreateView(PermissionMixin, _CreateView):
messages.error(self.request, _('Please check the form for errors.'))
return super(CreateView, self).form_invalid(form)
def get_success_message(self):
return _('%s was successfully created.') % html_strong(self.object)
class DeleteView(RedirectView, SingleObjectMixin):
def get_confirm_question(self):
return _('Do you really want to delete <b>%s</b>?') % self.object
return _('Do you really want to delete %s?') % html_strong(self.object)
def get_success_message(self):
return _('Item <b>%s</b> was successfully deleted.') % self.object
return _('%s was successfully deleted.') % html_strong(self.object)
def pre_redirect(self, request, *args, **kwargs):
self.confirm_form(request, self.object)