Bundle countdown with list of speakers. Fixed #1541.
This commit is contained in:
parent
948e776d33
commit
719b5ffedd
@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.core.config import config
|
||||
from openslides.core.models import Tag
|
||||
from openslides.core.projector import Countdown
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
@ -409,12 +410,9 @@ class Speaker(RESTModelMixin, models.Model):
|
||||
self.weight = None
|
||||
self.begin_time = datetime.now()
|
||||
self.save()
|
||||
# start countdown
|
||||
if config['agenda_couple_countdown_and_speakers']:
|
||||
# TODO: Fix me with the new countdown api
|
||||
# reset_countdown()
|
||||
# start_countdown()
|
||||
pass
|
||||
Countdown.control(action='reset')
|
||||
Countdown.control(action='start')
|
||||
|
||||
def end_speech(self):
|
||||
"""
|
||||
@ -422,11 +420,8 @@ class Speaker(RESTModelMixin, models.Model):
|
||||
"""
|
||||
self.end_time = datetime.now()
|
||||
self.save()
|
||||
# stop countdown
|
||||
if config['agenda_couple_countdown_and_speakers']:
|
||||
# TODO: Fix me with the new countdown api
|
||||
# stop_countdown()
|
||||
pass
|
||||
Countdown.control(action='stop')
|
||||
|
||||
def get_root_rest_element(self):
|
||||
"""
|
||||
|
35
openslides/core/migrations/0002_countdown.py
Normal file
35
openslides/core/migrations/0002_countdown.py
Normal file
@ -0,0 +1,35 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def add_default_projector_2(apps, schema_editor):
|
||||
"""
|
||||
Adds default projector, activates countdown.
|
||||
"""
|
||||
# We get the model from the versioned app registry;
|
||||
# if we directly import it, it will be the wrong version.
|
||||
Projector = apps.get_model('core', 'Projector')
|
||||
projector = Projector.objects.get()
|
||||
config = projector.config
|
||||
config.append({
|
||||
'name': 'core/countdown',
|
||||
'stable': True,
|
||||
'status': 'stop',
|
||||
'countdown_time': 60
|
||||
})
|
||||
projector.config = config
|
||||
projector.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=add_default_projector_2,
|
||||
reverse_code=None,
|
||||
atomic=True,
|
||||
),
|
||||
]
|
@ -3,8 +3,9 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.utils.projector import ProjectorElement, ProjectorRequirement
|
||||
|
||||
from .config import config
|
||||
from .exceptions import ProjectorException
|
||||
from .models import CustomSlide
|
||||
from .models import CustomSlide, Projector
|
||||
from .views import CustomSlideViewSet
|
||||
|
||||
|
||||
@ -47,31 +48,84 @@ class Countdown(ProjectorElement):
|
||||
|
||||
{
|
||||
"countdown_time": <timestamp>,
|
||||
"status": "go"
|
||||
"status": "running"
|
||||
}
|
||||
|
||||
The timestamp is a POSIX timestamp (seconds) calculated from server
|
||||
time, server time offset and countdown duration (countdown_time = now -
|
||||
serverTimeOffset + duration).
|
||||
|
||||
To stop the countdown set the countdown time to the actual value of the
|
||||
To stop the countdown set the countdown time to the current value of the
|
||||
countdown (countdown_time = countdown_time - now + serverTimeOffset)
|
||||
and set status to "stop".
|
||||
|
||||
To reset the countdown (it is not a reset in a functional way) just
|
||||
change the countdown_time. The status value remain 'stop'.
|
||||
change the countdown_time. The status value remains "stop".
|
||||
|
||||
To hide a running countdown add {"hidden": true}.
|
||||
There might be an additional value for the "default" countdown time
|
||||
which is used for the internal reset method if the countdown is coupled
|
||||
with the list of speakers.
|
||||
|
||||
To hide a countdown add {"hidden": true}.
|
||||
"""
|
||||
name = 'core/countdown'
|
||||
|
||||
def get_context(self):
|
||||
if self.config_entry.get('countdown_time') is None:
|
||||
raise ProjectorException(_('No countdown time given.'))
|
||||
if self.config_entry.get('status') is None:
|
||||
raise ProjectorException(_('No status given.'))
|
||||
self.validate_config(self.config_entry)
|
||||
return {'server_time': now().timestamp()}
|
||||
|
||||
@classmethod
|
||||
def validate_config(cls, config_data):
|
||||
"""
|
||||
Raises ProjectorException if the given data are invalid.
|
||||
"""
|
||||
if not isinstance(config_data.get('countdown_time'), (int, float)):
|
||||
raise ProjectorException(_('Invalid countdown time. Use integer or float.'))
|
||||
if config_data.get('status') not in ('running', 'stop'):
|
||||
raise ProjectorException(_("Invalid status. Use 'running' or 'stop'."))
|
||||
if config_data.get('default') is not None and not isinstance(config_data.get('default'), int):
|
||||
raise ProjectorException(_('Invalid default value. Use integer.'))
|
||||
|
||||
@classmethod
|
||||
def control(cls, action, projector_id=1, index=0):
|
||||
"""
|
||||
Starts, stops or resets the countdown with the given index on the
|
||||
given projector.
|
||||
|
||||
Action must be 'start', 'stop' or 'reset'.
|
||||
"""
|
||||
if action not in ('start', 'stop', 'reset'):
|
||||
raise ValueError("Action must be 'start', 'stop' or 'reset', not {}.".format(action))
|
||||
|
||||
projector_instance = Projector.objects.get(pk=projector_id)
|
||||
projector_config = []
|
||||
found = False
|
||||
for element in projector_instance.config:
|
||||
if element['name'] == cls.name:
|
||||
if index == 0:
|
||||
try:
|
||||
cls.validate_config(element)
|
||||
except ProjectorException:
|
||||
# Do not proceed if the specific procjector config data is invalid.
|
||||
# The variable found remains False.
|
||||
break
|
||||
found = True
|
||||
if action == 'start' and element['status'] == 'stop':
|
||||
element['status'] = 'running'
|
||||
element['countdown_time'] = now().timestamp() + element['countdown_time']
|
||||
elif action == 'stop' and element['status'] == 'running':
|
||||
element['status'] = 'stop'
|
||||
element['countdown_time'] = element['countdown_time'] - now().timestamp()
|
||||
elif action == 'reset':
|
||||
element['status'] = 'stop'
|
||||
element['countdown_time'] = element.get('default', config['projector_default_countdown'])
|
||||
else:
|
||||
index += -1
|
||||
projector_config.append(element)
|
||||
if found:
|
||||
projector_instance.config = projector_config
|
||||
projector_instance.save()
|
||||
|
||||
|
||||
class Message(ProjectorElement):
|
||||
"""
|
||||
|
@ -135,6 +135,13 @@ def setup_general_config(sender, **kwargs):
|
||||
group=ugettext_lazy('Projector'),
|
||||
translatable=True)
|
||||
|
||||
yield ConfigVariable(
|
||||
name='projector_default_countdown',
|
||||
default_value=60,
|
||||
label=ugettext_lazy('Default countdown'),
|
||||
weight=185,
|
||||
group=ugettext_lazy('Projector'))
|
||||
|
||||
|
||||
config_signal = Signal(providing_args=[])
|
||||
"""Signal to get all config tabs from all apps."""
|
||||
|
@ -3,6 +3,8 @@ from django.core.urlresolvers import reverse
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from openslides.agenda.models import Item, Speaker
|
||||
from openslides.core.config import config
|
||||
from openslides.core.models import Projector
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -178,3 +180,21 @@ class Speak(TestCase):
|
||||
def test_end_speech_no_current_speaker(self):
|
||||
response = self.client.delete(reverse('item-speak', args=[self.item.pk]))
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_begin_speech_with_countdown(self):
|
||||
config['agenda_couple_countdown_and_speakers'] = True
|
||||
Speaker.objects.add(self.user, self.item)
|
||||
speaker = Speaker.objects.add(get_user_model().objects.get(username='admin'), self.item)
|
||||
self.assertEqual(Projector.objects.get().config[2]['name'], 'core/countdown')
|
||||
self.client.put(
|
||||
reverse('item-speak', args=[self.item.pk]),
|
||||
{'speaker': speaker.pk})
|
||||
self.assertEqual(Projector.objects.get().config[2]['status'], 'running')
|
||||
|
||||
def test_end_speech_with_countdown(self):
|
||||
config['agenda_couple_countdown_and_speakers'] = True
|
||||
speaker = Speaker.objects.add(get_user_model().objects.get(username='admin'), self.item)
|
||||
speaker.begin_speech()
|
||||
self.assertEqual(Projector.objects.get().config[2]['name'], 'core/countdown')
|
||||
self.client.delete(reverse('item-speak', args=[self.item.pk]))
|
||||
self.assertEqual(Projector.objects.get().config[2]['status'], 'stop')
|
||||
|
Loading…
Reference in New Issue
Block a user