2015-02-18 01:45:39 +01:00
|
|
|
from django.utils.timezone import now
|
|
|
|
from django.utils.translation import ugettext as _
|
|
|
|
|
2015-06-12 21:08:57 +02:00
|
|
|
from openslides.utils.projector import ProjectorElement, ProjectorRequirement
|
2015-02-18 01:45:39 +01:00
|
|
|
|
2015-09-05 14:58:10 +02:00
|
|
|
from .config import config
|
2015-02-18 01:45:39 +01:00
|
|
|
from .exceptions import ProjectorException
|
2015-09-05 14:58:10 +02:00
|
|
|
from .models import CustomSlide, Projector
|
2015-06-12 21:08:57 +02:00
|
|
|
from .views import CustomSlideViewSet
|
2015-02-18 01:45:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
class CustomSlideSlide(ProjectorElement):
|
|
|
|
"""
|
|
|
|
Slide definitions for custom slide model.
|
|
|
|
"""
|
|
|
|
name = 'core/customslide'
|
|
|
|
|
|
|
|
def get_context(self):
|
2015-09-08 14:14:11 +02:00
|
|
|
if not CustomSlide.objects.filter(pk=self.config_entry.get('id')).exists():
|
2015-02-18 01:45:39 +01:00
|
|
|
raise ProjectorException(_('Custom slide does not exist.'))
|
|
|
|
|
2015-06-12 21:08:57 +02:00
|
|
|
def get_requirements(self, config_entry):
|
2015-06-17 09:45:00 +02:00
|
|
|
pk = config_entry.get('id')
|
|
|
|
if pk is not None:
|
2015-06-12 21:08:57 +02:00
|
|
|
yield ProjectorRequirement(
|
|
|
|
view_class=CustomSlideViewSet,
|
|
|
|
view_action='retrieve',
|
|
|
|
pk=str(pk))
|
|
|
|
|
2015-02-18 01:45:39 +01:00
|
|
|
|
|
|
|
class Clock(ProjectorElement):
|
|
|
|
"""
|
|
|
|
Clock on the projector.
|
|
|
|
"""
|
|
|
|
name = 'core/clock'
|
|
|
|
|
|
|
|
def get_context(self):
|
|
|
|
return {'server_time': now().timestamp()}
|
|
|
|
|
|
|
|
|
|
|
|
class Countdown(ProjectorElement):
|
|
|
|
"""
|
|
|
|
Countdown on the projector.
|
|
|
|
|
|
|
|
To start the countdown write into the config field:
|
|
|
|
|
|
|
|
{
|
|
|
|
"countdown_time": <timestamp>,
|
2015-09-05 14:58:10 +02:00
|
|
|
"status": "running"
|
2015-02-18 01:45:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
The timestamp is a POSIX timestamp (seconds) calculated from server
|
|
|
|
time, server time offset and countdown duration (countdown_time = now -
|
|
|
|
serverTimeOffset + duration).
|
|
|
|
|
2015-09-05 14:58:10 +02:00
|
|
|
To stop the countdown set the countdown time to the current value of the
|
2015-02-18 01:45:39 +01:00
|
|
|
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
|
2015-09-05 14:58:10 +02:00
|
|
|
change the countdown_time. The status value remains "stop".
|
2015-02-18 01:45:39 +01:00
|
|
|
|
2015-09-05 14:58:10 +02:00
|
|
|
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}.
|
2015-02-18 01:45:39 +01:00
|
|
|
"""
|
|
|
|
name = 'core/countdown'
|
|
|
|
|
|
|
|
def get_context(self):
|
2015-09-05 14:58:10 +02:00
|
|
|
self.validate_config(self.config_entry)
|
2015-02-18 01:45:39 +01:00
|
|
|
return {'server_time': now().timestamp()}
|
|
|
|
|
2015-09-05 14:58:10 +02:00
|
|
|
@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)
|
2015-09-06 13:28:25 +02:00
|
|
|
projector_config = {}
|
2015-09-05 14:58:10 +02:00
|
|
|
found = False
|
2015-09-06 13:28:25 +02:00
|
|
|
for key, value in projector_instance.config.items():
|
|
|
|
if value['name'] == cls.name:
|
2015-09-05 14:58:10 +02:00
|
|
|
if index == 0:
|
|
|
|
try:
|
2015-09-06 13:28:25 +02:00
|
|
|
cls.validate_config(value)
|
2015-09-05 14:58:10 +02:00
|
|
|
except ProjectorException:
|
|
|
|
# Do not proceed if the specific procjector config data is invalid.
|
|
|
|
# The variable found remains False.
|
|
|
|
break
|
|
|
|
found = True
|
2015-09-06 13:28:25 +02:00
|
|
|
if action == 'start' and value['status'] == 'stop':
|
|
|
|
value['status'] = 'running'
|
|
|
|
value['countdown_time'] = now().timestamp() + value['countdown_time']
|
|
|
|
elif action == 'stop' and value['status'] == 'running':
|
|
|
|
value['status'] = 'stop'
|
|
|
|
value['countdown_time'] = value['countdown_time'] - now().timestamp()
|
2015-09-05 14:58:10 +02:00
|
|
|
elif action == 'reset':
|
2015-09-06 13:28:25 +02:00
|
|
|
value['status'] = 'stop'
|
|
|
|
value['countdown_time'] = value.get('default', config['projector_default_countdown'])
|
2015-09-05 14:58:10 +02:00
|
|
|
else:
|
|
|
|
index += -1
|
2015-09-06 13:28:25 +02:00
|
|
|
projector_config[key] = value
|
2015-09-05 14:58:10 +02:00
|
|
|
if found:
|
|
|
|
projector_instance.config = projector_config
|
|
|
|
projector_instance.save()
|
|
|
|
|
2015-02-18 01:45:39 +01:00
|
|
|
|
|
|
|
class Message(ProjectorElement):
|
|
|
|
"""
|
|
|
|
Short message on the projector. Rendered as overlay.
|
|
|
|
"""
|
|
|
|
name = 'core/message'
|
|
|
|
|
|
|
|
def get_context(self):
|
|
|
|
if self.config_entry.get('message') is None:
|
|
|
|
raise ProjectorException(_('No message given.'))
|