From 40af20cedb1e9885baefd9b20041a58838ffb500 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Mon, 7 Oct 2013 08:52:18 +0200 Subject: [PATCH 1/2] New feature: couple countdown with list of speakers 'Begin speach' button starts countdown, 'end speach' button stops countdown. --- openslides/agenda/models.py | 11 ++++++++++- openslides/agenda/signals.py | 12 +++++++++++- openslides/projector/api.py | 37 +++++++++++++++++++++++++++++++++++ openslides/projector/views.py | 27 ++++--------------------- 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index 5c0b2858b..07880f6a0 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -11,6 +11,7 @@ """ from datetime import datetime +from time import time from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes import generic @@ -22,7 +23,9 @@ from django.utils.translation import ugettext_lazy, ugettext_noop from mptt.models import MPTTModel, TreeForeignKey from openslides.projector.api import (get_active_slide, update_projector, - update_projector_overlay) + update_projector_overlay, + start_countdown, + stop_countdown) from openslides.projector.models import SlideMixin from openslides.utils.exceptions import OpenSlidesError from openslides.utils.person.models import PersonField @@ -378,6 +381,9 @@ class Speaker(models.Model): self.weight = None self.begin_time = datetime.now() self.save() + # start countdown + if config['agenda_couple_countdown_and_speakers']: + start_countdown() def end_speach(self): """ @@ -385,3 +391,6 @@ class Speaker(models.Model): """ self.end_time = datetime.now() self.save() + # stop countdown + if config['agenda_couple_countdown_and_speakers']: + stop_countdown() diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index f046c10f8..82611989c 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -62,6 +62,14 @@ def setup_agenda_config_page(sender, **kwargs): min_value=0, label=ugettext_lazy('Number of last speakers to be shown on the projector'))) + agenda_couple_countdown_and_speakers = ConfigVariable( + name='agenda_couple_countdown_and_speakers', + default_value=False, + form_field=forms.BooleanField( + label=ugettext_lazy('Couple countdown with the list of speakers'), + help_text=ugettext_lazy('[Begin speach] starts the countdown, [End speach] stops the countdown.'), + required=False)) + extra_stylefiles = ['styles/timepicker.css', 'styles/jquery-ui/jquery-ui.custom.min.css'] extra_javascript = ['javascript/jquery-ui.custom.min.js', 'javascript/jquery-ui-timepicker-addon.min.js', @@ -72,7 +80,9 @@ def setup_agenda_config_page(sender, **kwargs): url='agenda', required_permission='config.can_manage', weight=20, - variables=(agenda_start_event_date_time, agenda_show_last_speakers), + variables=(agenda_start_event_date_time, + agenda_show_last_speakers, + agenda_couple_countdown_and_speakers), extra_context={'extra_stylefiles': extra_stylefiles, 'extra_javascript': extra_javascript}) diff --git a/openslides/projector/api.py b/openslides/projector/api.py index fe34ea2a5..6e818b2b6 100644 --- a/openslides/projector/api.py +++ b/openslides/projector/api.py @@ -11,6 +11,7 @@ """ import json +from time import time from django.conf import settings from django.template.loader import render_to_string @@ -208,3 +209,39 @@ def get_all_widgets(request, session=False): if not session or session_widgets.get(widget.get_name(), True): widgets[widget.get_name()] = widget return widgets + + +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' diff --git a/openslides/projector/views.py b/openslides/projector/views.py index 14f3b47c4..69bea56e5 100644 --- a/openslides/projector/views.py +++ b/openslides/projector/views.py @@ -26,7 +26,7 @@ from openslides.utils.views import (AjaxMixin, CreateView, DeleteView, from .api import (get_active_slide, get_all_widgets, get_overlays, get_projector_content, get_projector_overlays, - get_projector_overlays_js, set_active_slide, + get_projector_overlays_js, set_active_slide, start_countdown, stop_countdown, reset_countdown, update_projector_overlay) from .forms import SelectWidgetsForm from .models import ProjectorSlide @@ -169,31 +169,12 @@ class CountdownEdit(RedirectView): def pre_redirect(self, request, *args, **kwargs): command = kwargs['command'] - # countdown_state is one of 'inactive', 'paused' and 'active', 'expired' - if command in ['reset', 'start', 'stop']: - config['countdown_time'] = config['countdown_time'] - if command == 'reset': - config['countdown_start_stamp'] = time() - config['countdown_pause_stamp'] = 0 - config['countdown_state'] = 'inactive' + reset_countdown() elif command == 'start': - # 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 + start_countdown() elif command == 'stop': - if config['countdown_state'] == 'active': - config['countdown_pause_stamp'] = time() - config['countdown_state'] = 'paused' + stop_countdown() elif command == 'set-default': try: config['countdown_time'] = \ From 40faaa39c6f731c6d7abeee081ad1f85b5352f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Fri, 18 Oct 2013 22:28:38 +0200 Subject: [PATCH 2/2] Changed begin speach behavior. Fix merge conflicts. Add tests. --- openslides/agenda/models.py | 10 +++++----- openslides/projector/views.py | 6 ++---- tests/agenda/test_list_of_speakers.py | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index 07880f6a0..b4cff6f3d 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -11,7 +11,6 @@ """ from datetime import datetime -from time import time from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes import generic @@ -22,10 +21,10 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop from mptt.models import MPTTModel, TreeForeignKey -from openslides.projector.api import (get_active_slide, update_projector, - update_projector_overlay, - start_countdown, - stop_countdown) +from openslides.config.api import config +from openslides.projector.api import (get_active_slide, reset_countdown, + start_countdown, stop_countdown, + update_projector, update_projector_overlay) from openslides.projector.models import SlideMixin from openslides.utils.exceptions import OpenSlidesError from openslides.utils.person.models import PersonField @@ -383,6 +382,7 @@ class Speaker(models.Model): self.save() # start countdown if config['agenda_couple_countdown_and_speakers']: + reset_countdown() start_countdown() def end_speach(self): diff --git a/openslides/projector/views.py b/openslides/projector/views.py index 69bea56e5..af7db6026 100644 --- a/openslides/projector/views.py +++ b/openslides/projector/views.py @@ -10,8 +10,6 @@ :license: GNU GPL, see LICENSE for more details. """ -from time import time - from django.contrib import messages from django.core.context_processors import csrf from django.core.urlresolvers import reverse @@ -26,8 +24,8 @@ from openslides.utils.views import (AjaxMixin, CreateView, DeleteView, from .api import (get_active_slide, get_all_widgets, get_overlays, get_projector_content, get_projector_overlays, - get_projector_overlays_js, set_active_slide, start_countdown, stop_countdown, reset_countdown, - update_projector_overlay) + get_projector_overlays_js, reset_countdown, set_active_slide, + start_countdown, stop_countdown, update_projector_overlay) from .forms import SelectWidgetsForm from .models import ProjectorSlide from .projector import Widget diff --git a/tests/agenda/test_list_of_speakers.py b/tests/agenda/test_list_of_speakers.py index 74516f756..87f85dec9 100644 --- a/tests/agenda/test_list_of_speakers.py +++ b/tests/agenda/test_list_of_speakers.py @@ -10,6 +10,7 @@ from django.test.client import Client from openslides.agenda.models import Item, Speaker +from openslides.config.api import config from openslides.participant.models import Group, User from openslides.projector.api import set_active_slide from openslides.utils.exceptions import OpenSlidesError @@ -74,6 +75,25 @@ class ListOfSpeakerModelTests(TestCase): self.assertIsNotNone(Speaker.objects.get(person=self.speaker1, item=self.item1).end_time) self.assertIsNotNone(speaker2_item1.begin_time) + def test_speach_coupled_with_countdown(self): + config['agenda_couple_countdown_and_speakers'] = True + self.assertTrue(config['countdown_state'] == 'inactive') + speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1) + speaker1_item1.begin_speach() + self.assertTrue(config['countdown_state'] == 'active') + speaker1_item1.end_speach() + self.assertTrue(config['countdown_state'] == 'paused') + + def test_begin_speach_not_coupled_with_countdown(self): + config['agenda_couple_countdown_and_speakers'] = False + self.assertTrue(config['countdown_state'] == 'inactive') + speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1) + speaker1_item1.begin_speach() + self.assertTrue(config['countdown_state'] == 'inactive') + config['countdown_state'] = 'active' + speaker1_item1.end_speach() + self.assertTrue(config['countdown_state'] == 'active') + class SpeakerViewTestCase(TestCase): def setUp(self):