Merge pull request #1407 from ostcar/super_signal
Remove update_projector and add inform_changed_data.
This commit is contained in:
commit
2b744942e7
@ -233,7 +233,8 @@ def runserver(settings, args):
|
|||||||
start_browser(browser_url)
|
start_browser(browser_url)
|
||||||
|
|
||||||
# Now the settings is available and the function can be imported.
|
# Now the settings is available and the function can be imported.
|
||||||
from openslides.utils.tornado_webserver import run_tornado
|
# TODO: only start tornado when it is used as wsgi server
|
||||||
|
from openslides.utils.autoupdate import run_tornado
|
||||||
run_tornado(args.address, port)
|
run_tornado(args.address, port)
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,17 +12,17 @@ from mptt.models import MPTTModel, TreeForeignKey
|
|||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.projector.api import (get_active_slide, reset_countdown,
|
from openslides.projector.api import (reset_countdown,
|
||||||
start_countdown, stop_countdown,
|
start_countdown, stop_countdown)
|
||||||
update_projector, update_projector_overlay)
|
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
from openslides.utils.models import AbsoluteUrlMixin
|
from openslides.utils.models import AbsoluteUrlMixin
|
||||||
|
from openslides.utils.rest_api import RESTModelMixin
|
||||||
from openslides.utils.utils import to_roman
|
from openslides.utils.utils import to_roman
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
|
|
||||||
|
|
||||||
class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
||||||
"""
|
"""
|
||||||
An Agenda Item
|
An Agenda Item
|
||||||
|
|
||||||
@ -122,11 +122,6 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
|||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
order_insertion_by = ['weight']
|
order_insertion_by = ['weight']
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
super(Item, self).save(*args, **kwargs)
|
|
||||||
if self.parent and self.parent.is_active_slide():
|
|
||||||
update_projector()
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
Ensures that the children of orga items are only orga items.
|
Ensures that the children of orga items are only orga items.
|
||||||
@ -135,7 +130,7 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
|||||||
raise ValidationError(_('Agenda items can not be child elements of an organizational item.'))
|
raise ValidationError(_('Agenda items can not be child elements of an organizational item.'))
|
||||||
if self.type == self.ORGANIZATIONAL_ITEM and self.get_descendants().filter(type=self.AGENDA_ITEM).exists():
|
if self.type == self.ORGANIZATIONAL_ITEM and self.get_descendants().filter(type=self.AGENDA_ITEM).exists():
|
||||||
raise ValidationError(_('Organizational items can not have agenda items as child elements.'))
|
raise ValidationError(_('Organizational items can not have agenda items as child elements.'))
|
||||||
return super(Item, self).clean()
|
return super().clean()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.get_title()
|
return self.get_title()
|
||||||
@ -153,14 +148,14 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
|||||||
elif link == 'delete':
|
elif link == 'delete':
|
||||||
url = reverse('item_delete', args=[str(self.id)])
|
url = reverse('item_delete', args=[str(self.id)])
|
||||||
elif link == 'projector_list_of_speakers':
|
elif link == 'projector_list_of_speakers':
|
||||||
url = '%s&type=list_of_speakers' % super(Item, self).get_absolute_url('projector')
|
url = '%s&type=list_of_speakers' % super().get_absolute_url('projector')
|
||||||
elif link == 'projector_summary':
|
elif link == 'projector_summary':
|
||||||
url = '%s&type=summary' % super(Item, self).get_absolute_url('projector')
|
url = '%s&type=summary' % super().get_absolute_url('projector')
|
||||||
elif (link in ('projector', 'projector_preview') and
|
elif (link in ('projector', 'projector_preview') and
|
||||||
self.content_object and isinstance(self.content_object, SlideMixin)):
|
self.content_object and isinstance(self.content_object, SlideMixin)):
|
||||||
url = self.content_object.get_absolute_url(link)
|
url = self.content_object.get_absolute_url(link)
|
||||||
else:
|
else:
|
||||||
url = super(Item, self).get_absolute_url(link)
|
url = super().get_absolute_url(link)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
@ -359,7 +354,7 @@ class SpeakerManager(models.Manager):
|
|||||||
return self.create(item=item, user=user, weight=weight + 1)
|
return self.create(item=item, user=user, weight=weight + 1)
|
||||||
|
|
||||||
|
|
||||||
class Speaker(AbsoluteUrlMixin, models.Model):
|
class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
Model for the Speaker list.
|
Model for the Speaker list.
|
||||||
"""
|
"""
|
||||||
@ -396,14 +391,6 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
|||||||
('can_be_speaker', ugettext_noop('Can put oneself on the list of speakers')),
|
('can_be_speaker', ugettext_noop('Can put oneself on the list of speakers')),
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
super(Speaker, self).save(*args, **kwargs)
|
|
||||||
self.check_and_update_projector()
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
|
||||||
super(Speaker, self).delete(*args, **kwargs)
|
|
||||||
self.check_and_update_projector()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.user)
|
return str(self.user)
|
||||||
|
|
||||||
@ -417,17 +404,6 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
|||||||
url = super(Speaker, self).get_absolute_url(link)
|
url = super(Speaker, self).get_absolute_url(link)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def check_and_update_projector(self):
|
|
||||||
"""
|
|
||||||
Checks, if the agenda item, or parts of it, is on the projector.
|
|
||||||
If yes, it updates the projector.
|
|
||||||
"""
|
|
||||||
if self.item.is_active_slide():
|
|
||||||
if get_active_slide().get('type', None) == 'list_of_speakers':
|
|
||||||
update_projector()
|
|
||||||
else:
|
|
||||||
update_projector_overlay('agenda_speaker')
|
|
||||||
|
|
||||||
def begin_speach(self):
|
def begin_speach(self):
|
||||||
"""
|
"""
|
||||||
Let the user speak.
|
Let the user speak.
|
||||||
@ -448,10 +424,6 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
|||||||
if config['agenda_couple_countdown_and_speakers']:
|
if config['agenda_couple_countdown_and_speakers']:
|
||||||
reset_countdown()
|
reset_countdown()
|
||||||
start_countdown()
|
start_countdown()
|
||||||
if self.item.is_active_slide():
|
|
||||||
# TODO: only update the overlay if the overlay is active and
|
|
||||||
# slide type is None.
|
|
||||||
update_projector_overlay('projector_countdown')
|
|
||||||
|
|
||||||
def end_speach(self):
|
def end_speach(self):
|
||||||
"""
|
"""
|
||||||
@ -462,7 +434,9 @@ class Speaker(AbsoluteUrlMixin, models.Model):
|
|||||||
# stop countdown
|
# stop countdown
|
||||||
if config['agenda_couple_countdown_and_speakers']:
|
if config['agenda_couple_countdown_and_speakers']:
|
||||||
stop_countdown()
|
stop_countdown()
|
||||||
if self.item.is_active_slide():
|
|
||||||
# TODO: only update the overlay if the overlay is active and
|
def get_root_rest_element(self):
|
||||||
# slide type is None.
|
"""
|
||||||
update_projector_overlay('projector_countdown')
|
Returns the item to this instance, which is the root rest element.
|
||||||
|
"""
|
||||||
|
return self.item
|
||||||
|
@ -21,8 +21,7 @@ from openslides.projector.api import (
|
|||||||
get_active_object,
|
get_active_object,
|
||||||
get_active_slide,
|
get_active_slide,
|
||||||
get_projector_overlays_js,
|
get_projector_overlays_js,
|
||||||
get_overlays,
|
get_overlays)
|
||||||
update_projector)
|
|
||||||
from openslides.utils import rest_api
|
from openslides.utils import rest_api
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
@ -159,10 +158,6 @@ class Overview(TemplateView):
|
|||||||
# TODO: assure, that it is a valid tree
|
# TODO: assure, that it is a valid tree
|
||||||
|
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
|
|
||||||
if get_active_slide()['callback'] == 'agenda':
|
|
||||||
update_projector()
|
|
||||||
context = self.get_context_data(**kwargs)
|
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,15 +11,14 @@ from openslides.config.api import config
|
|||||||
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
||||||
CollectDefaultVotesMixin,
|
CollectDefaultVotesMixin,
|
||||||
PublishPollMixin)
|
PublishPollMixin)
|
||||||
from openslides.projector.api import get_active_object, update_projector
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
from openslides.utils.models import AbsoluteUrlMixin
|
from openslides.utils.models import AbsoluteUrlMixin
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
|
|
||||||
|
|
||||||
class AssignmentCandidate(RelatedModelMixin, models.Model):
|
class AssignmentCandidate(models.Model):
|
||||||
"""
|
"""
|
||||||
Many2Many table between an assignment and the candidates.
|
Many2Many table between an assignment and the candidates.
|
||||||
"""
|
"""
|
||||||
@ -34,12 +33,6 @@ class AssignmentCandidate(RelatedModelMixin, models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.person)
|
return str(self.person)
|
||||||
|
|
||||||
def get_related_model(self):
|
|
||||||
"""
|
|
||||||
Returns the assignment
|
|
||||||
"""
|
|
||||||
return self.assignment
|
|
||||||
|
|
||||||
|
|
||||||
class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||||
slide_callback_name = 'assignment'
|
slide_callback_name = 'assignment'
|
||||||
@ -190,11 +183,6 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
candidate = self.assignment_candidates.get(person=person)
|
candidate = self.assignment_candidates.get(person=person)
|
||||||
candidate.elected = value
|
candidate.elected = value
|
||||||
candidate.save()
|
candidate.save()
|
||||||
# update projector if assignment or assignmentpoll slide is active
|
|
||||||
active_object = get_active_object()
|
|
||||||
if (type(active_object) is type(self) and active_object.pk == self.pk) or \
|
|
||||||
(type(active_object) is AssignmentPoll and active_object.assignment_id == self.pk):
|
|
||||||
update_projector()
|
|
||||||
|
|
||||||
def is_elected(self, person):
|
def is_elected(self, person):
|
||||||
return person in self.elected
|
return person in self.elected
|
||||||
@ -224,16 +212,13 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
|
|
||||||
items = Item.objects.filter(content_type=ContentType.objects.get_for_model(Assignment), object_id=self.pk)
|
items = Item.objects.filter(content_type=ContentType.objects.get_for_model(Assignment), object_id=self.pk)
|
||||||
for item in items:
|
for item in items:
|
||||||
someone_added = None
|
|
||||||
for candidate in self.candidates:
|
for candidate in self.candidates:
|
||||||
try:
|
try:
|
||||||
someone_added = Speaker.objects.add(candidate, item)
|
Speaker.objects.add(candidate, item)
|
||||||
except OpenSlidesError:
|
except OpenSlidesError:
|
||||||
# The Speaker is already on the list. Do nothing.
|
# The Speaker is already on the list. Do nothing.
|
||||||
# TODO: Find a smart way not to catch the error concerning AnonymousUser.
|
# TODO: Find a smart way not to catch the error concerning AnonymousUser.
|
||||||
pass
|
pass
|
||||||
if someone_added is not None:
|
|
||||||
someone_added.check_and_update_projector()
|
|
||||||
|
|
||||||
return poll
|
return poll
|
||||||
|
|
||||||
@ -290,7 +275,7 @@ class AssignmentOption(BaseOption):
|
|||||||
return str(self.candidate)
|
return str(self.candidate)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
|
class AssignmentPoll(SlideMixin, CollectDefaultVotesMixin,
|
||||||
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
||||||
|
|
||||||
slide_callback_name = 'assignmentpoll'
|
slide_callback_name = 'assignmentpoll'
|
||||||
@ -323,9 +308,6 @@ class AssignmentPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
|
|||||||
def get_assignment(self):
|
def get_assignment(self):
|
||||||
return self.assignment
|
return self.assignment
|
||||||
|
|
||||||
def get_related_model(self):
|
|
||||||
return self.assignment
|
|
||||||
|
|
||||||
def get_vote_values(self):
|
def get_vote_values(self):
|
||||||
if self.yesnoabstain:
|
if self.yesnoabstain:
|
||||||
return [ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
|
return [ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
|
||||||
|
@ -11,8 +11,10 @@ class CoreAppConfig(AppConfig):
|
|||||||
from . import main_menu, widgets # noqa
|
from . import main_menu, widgets # noqa
|
||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
|
from django.db.models import signals
|
||||||
from openslides.config.signals import config_signal
|
from openslides.config.signals import config_signal
|
||||||
from openslides.projector.api import register_slide_model
|
from openslides.projector.api import register_slide_model
|
||||||
|
from openslides.utils.autoupdate import inform_changed_data_receiver
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from .signals import setup_general_config
|
from .signals import setup_general_config
|
||||||
from .views import CustomSlideViewSet
|
from .views import CustomSlideViewSet
|
||||||
@ -26,3 +28,8 @@ class CoreAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Register viewset.
|
# Register viewset.
|
||||||
router.register('core/customslide', CustomSlideViewSet)
|
router.register('core/customslide', CustomSlideViewSet)
|
||||||
|
|
||||||
|
# Update data when any model of any installed app is saved or deleted
|
||||||
|
signals.post_save.connect(inform_changed_data_receiver, dispatch_uid='inform_changed_data_receiver')
|
||||||
|
signals.post_delete.connect(inform_changed_data_receiver, dispatch_uid='inform_changed_data_receiver')
|
||||||
|
# TODO: test if the m2m_changed signal is also needed
|
||||||
|
@ -4,7 +4,6 @@ from django.utils.translation import ugettext as _
|
|||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
|
||||||
from openslides.projector.api import update_projector
|
|
||||||
|
|
||||||
post_database_setup = Signal()
|
post_database_setup = Signal()
|
||||||
|
|
||||||
@ -108,8 +107,7 @@ def setup_general_config(sender, **kwargs):
|
|||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
label=ugettext_lazy('Title'),
|
label=ugettext_lazy('Title'),
|
||||||
help_text=ugettext_lazy('Also used for the default welcome slide.'),
|
help_text=ugettext_lazy('Also used for the default welcome slide.'),
|
||||||
required=False),
|
required=False))
|
||||||
on_change=update_projector)
|
|
||||||
|
|
||||||
welcome_text = ConfigVariable(
|
welcome_text = ConfigVariable(
|
||||||
name='welcome_text',
|
name='welcome_text',
|
||||||
|
@ -173,3 +173,8 @@ CKEDITOR_CONFIGS = {
|
|||||||
'default': CKEDITOR_DEFAULT_CONFIG,
|
'default': CKEDITOR_DEFAULT_CONFIG,
|
||||||
'images': CKEDITOR_IMG_CONFIG,
|
'images': CKEDITOR_IMG_CONFIG,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Use small alternative with tornado as frontend or big alternative with a
|
||||||
|
# webserver as wsgi server.
|
||||||
|
USE_TORNADO_AS_WSGI_SERVER = True
|
||||||
|
@ -2,7 +2,6 @@ from django.http import HttpResponse
|
|||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
from openslides.utils.tornado_webserver import ProjectorSocketHandler
|
|
||||||
from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView,
|
from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView,
|
||||||
UpdateView)
|
UpdateView)
|
||||||
|
|
||||||
@ -119,8 +118,6 @@ class PdfNavBaseView(AjaxView):
|
|||||||
Tell connected clients to load an other pdf page.
|
Tell connected clients to load an other pdf page.
|
||||||
"""
|
"""
|
||||||
config['projector_active_slide'] = active_slide
|
config['projector_active_slide'] = active_slide
|
||||||
ProjectorSocketHandler.send_updates(
|
|
||||||
{'calls': {'load_pdf_page': active_slide['page_num']}})
|
|
||||||
|
|
||||||
|
|
||||||
class PdfNextView(PdfNavBaseView):
|
class PdfNextView(PdfNavBaseView):
|
||||||
@ -199,8 +196,4 @@ class PdfToggleFullscreenView(RedirectView):
|
|||||||
|
|
||||||
def get_ajax_context(self, *args, **kwargs):
|
def get_ajax_context(self, *args, **kwargs):
|
||||||
config['pdf_fullscreen'] = not config['pdf_fullscreen']
|
config['pdf_fullscreen'] = not config['pdf_fullscreen']
|
||||||
active_slide = get_active_slide()
|
|
||||||
if active_slide['callback'] == 'mediafile':
|
|
||||||
ProjectorSocketHandler.send_updates(
|
|
||||||
{'calls': {'toggle_fullscreen': config['pdf_fullscreen']}})
|
|
||||||
return {'fullscreen': config['pdf_fullscreen']}
|
return {'fullscreen': config['pdf_fullscreen']}
|
||||||
|
@ -9,7 +9,7 @@ from openslides.config.api import config
|
|||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.mediafile.models import Mediafile
|
from openslides.mediafile.models import Mediafile
|
||||||
from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefaultVotesMixin)
|
from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefaultVotesMixin)
|
||||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
from openslides.utils.models import AbsoluteUrlMixin
|
from openslides.utils.models import AbsoluteUrlMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
@ -612,7 +612,7 @@ class MotionVersion(AbsoluteUrlMixin, models.Model):
|
|||||||
return self.active_version.exists()
|
return self.active_version.exists()
|
||||||
|
|
||||||
|
|
||||||
class MotionSubmitter(RelatedModelMixin, models.Model):
|
class MotionSubmitter(models.Model):
|
||||||
"""Save the submitter of a Motion."""
|
"""Save the submitter of a Motion."""
|
||||||
|
|
||||||
motion = models.ForeignKey('Motion', related_name="submitter")
|
motion = models.ForeignKey('Motion', related_name="submitter")
|
||||||
@ -625,9 +625,6 @@ class MotionSubmitter(RelatedModelMixin, models.Model):
|
|||||||
"""Return the name of the submitter as string."""
|
"""Return the name of the submitter as string."""
|
||||||
return str(self.person)
|
return str(self.person)
|
||||||
|
|
||||||
def get_related_model(self):
|
|
||||||
return self.motion
|
|
||||||
|
|
||||||
|
|
||||||
class MotionSupporter(models.Model):
|
class MotionSupporter(models.Model):
|
||||||
"""Save the submitter of a Motion."""
|
"""Save the submitter of a Motion."""
|
||||||
@ -723,7 +720,7 @@ class MotionOption(BaseOption):
|
|||||||
"""The VoteClass, to witch this Class links."""
|
"""The VoteClass, to witch this Class links."""
|
||||||
|
|
||||||
|
|
||||||
class MotionPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
|
class MotionPoll(SlideMixin, CollectDefaultVotesMixin,
|
||||||
AbsoluteUrlMixin, BasePoll):
|
AbsoluteUrlMixin, BasePoll):
|
||||||
"""The Class to saves the vote result for a motion poll."""
|
"""The Class to saves the vote result for a motion poll."""
|
||||||
|
|
||||||
@ -775,9 +772,6 @@ class MotionPoll(SlideMixin, RelatedModelMixin, CollectDefaultVotesMixin,
|
|||||||
# or call this in save()
|
# or call this in save()
|
||||||
self.get_option_class()(poll=self).save()
|
self.get_option_class()(poll=self).save()
|
||||||
|
|
||||||
def get_related_model(self):
|
|
||||||
return self.motion
|
|
||||||
|
|
||||||
def get_percent_base_choice(self):
|
def get_percent_base_choice(self):
|
||||||
return config['motion_poll_100_percent_base']
|
return config['motion_poll_100_percent_base']
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ from django.shortcuts import get_object_or_404
|
|||||||
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
|
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
from openslides.projector.api import get_active_slide, update_projector
|
|
||||||
from openslides.utils.utils import html_strong, htmldiff
|
from openslides.utils.utils import html_strong, htmldiff
|
||||||
from openslides.utils.views import (CreateView, CSVImportView, DeleteView, DetailView,
|
from openslides.utils.views import (CreateView, CSVImportView, DeleteView, DetailView,
|
||||||
ListView, PDFView, QuestionView,
|
ListView, PDFView, QuestionView,
|
||||||
@ -142,14 +141,6 @@ class MotionEditMixin(object):
|
|||||||
self.object.tags.clear()
|
self.object.tags.clear()
|
||||||
self.object.tags.add(*form.cleaned_data['tags'])
|
self.object.tags.add(*form.cleaned_data['tags'])
|
||||||
|
|
||||||
# Update the projector if the motion is on it. This can not be done in
|
|
||||||
# the model, because bulk_create does not call the save method.
|
|
||||||
active_slide = get_active_slide()
|
|
||||||
active_slide_pk = active_slide.get('pk', None)
|
|
||||||
if (active_slide['callback'] == 'motion' and
|
|
||||||
str(self.object.pk) == str(active_slide_pk)):
|
|
||||||
update_projector()
|
|
||||||
|
|
||||||
messages.success(self.request, self.get_success_message())
|
messages.success(self.request, self.get_success_message())
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ from time import time
|
|||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.utils.tornado_webserver import ProjectorSocketHandler
|
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
|
|
||||||
from .signals import projector_overlays
|
from .signals import projector_overlays
|
||||||
@ -28,41 +27,6 @@ class SlideError(OpenSlidesError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def update_projector():
|
|
||||||
"""
|
|
||||||
Sends the data to the clients, who listen to the projector.
|
|
||||||
"""
|
|
||||||
# TODO: only send necessary html
|
|
||||||
ProjectorSocketHandler.send_updates({'content': get_projector_content()})
|
|
||||||
|
|
||||||
|
|
||||||
def update_projector_overlay(overlay):
|
|
||||||
"""
|
|
||||||
Update one or all overlay on the projector.
|
|
||||||
|
|
||||||
Checks if the overlay is activated and updates it in this case.
|
|
||||||
|
|
||||||
The argument 'overlay' has to be an overlay object, the name of a
|
|
||||||
ovleray or None. If it is None, all overlays will be updated.
|
|
||||||
"""
|
|
||||||
if overlay is None:
|
|
||||||
overlays = [item for item in get_overlays().values()]
|
|
||||||
elif isinstance(overlay, str):
|
|
||||||
overlays = [get_overlays()[overlay]]
|
|
||||||
else:
|
|
||||||
overlays = [overlay]
|
|
||||||
|
|
||||||
overlay_dict = {}
|
|
||||||
for overlay in overlays:
|
|
||||||
if overlay.is_active():
|
|
||||||
overlay_dict[overlay.name] = {
|
|
||||||
'html': overlay.get_projector_html(),
|
|
||||||
'javascript': overlay.get_javascript()}
|
|
||||||
else:
|
|
||||||
overlay_dict[overlay.name] = None
|
|
||||||
ProjectorSocketHandler.send_updates({'overlays': overlay_dict})
|
|
||||||
|
|
||||||
|
|
||||||
def call_on_projector(calls):
|
def call_on_projector(calls):
|
||||||
"""
|
"""
|
||||||
Sends data to the projector.
|
Sends data to the projector.
|
||||||
@ -70,10 +34,10 @@ def call_on_projector(calls):
|
|||||||
The argument call has to be a dictionary with the javascript function name
|
The argument call has to be a dictionary with the javascript function name
|
||||||
as key and the argument for it as value.
|
as key and the argument for it as value.
|
||||||
"""
|
"""
|
||||||
|
# TODO: remove this function
|
||||||
projector_js_cache = config['projector_js_cache']
|
projector_js_cache = config['projector_js_cache']
|
||||||
projector_js_cache.update(calls)
|
projector_js_cache.update(calls)
|
||||||
config['projector_js_cache'] = projector_js_cache
|
config['projector_js_cache'] = projector_js_cache
|
||||||
ProjectorSocketHandler.send_updates({'calls': calls})
|
|
||||||
|
|
||||||
|
|
||||||
def get_projector_content(slide_dict=None):
|
def get_projector_content(slide_dict=None):
|
||||||
@ -181,8 +145,6 @@ def set_active_slide(callback, **kwargs):
|
|||||||
"""
|
"""
|
||||||
kwargs.update(callback=callback)
|
kwargs.update(callback=callback)
|
||||||
config['projector_active_slide'] = kwargs
|
config['projector_active_slide'] = kwargs
|
||||||
update_projector()
|
|
||||||
update_projector_overlay(None)
|
|
||||||
|
|
||||||
|
|
||||||
def get_active_slide():
|
def get_active_slide():
|
||||||
|
@ -1,42 +1,8 @@
|
|||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from openslides.utils.utils import int_or_none
|
from openslides.utils.utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
class RelatedModelMixin(object):
|
|
||||||
"""
|
|
||||||
Mixin for motion related models, that appear on the motion slide.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Saves the model and updates the projector, if the motion in on it.
|
|
||||||
"""
|
|
||||||
from .api import update_projector
|
|
||||||
value = super(RelatedModelMixin, self).save(*args, **kwargs)
|
|
||||||
if self.get_related_model().is_active_slide():
|
|
||||||
update_projector()
|
|
||||||
return value
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Deletes the model and updates the projector, if the motion in on it.
|
|
||||||
"""
|
|
||||||
from .api import update_projector
|
|
||||||
value = super(RelatedModelMixin, self).delete(*args, **kwargs)
|
|
||||||
if self.get_related_model().is_active_slide():
|
|
||||||
update_projector()
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get_related_model(self):
|
|
||||||
"""
|
|
||||||
Return the pk of the related model.
|
|
||||||
"""
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
'%s has to have a method "get_related_model_pk"' % type(self))
|
|
||||||
|
|
||||||
|
|
||||||
class SlideMixin(object):
|
class SlideMixin(object):
|
||||||
"""
|
"""
|
||||||
A Mixin for a Django-Model, for making the model a slide.
|
A Mixin for a Django-Model, for making the model a slide.
|
||||||
@ -47,31 +13,6 @@ class SlideMixin(object):
|
|||||||
Name of the callback to render the model as slide.
|
Name of the callback to render the model as slide.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Updates the projector, if the object is on the projector and changed.
|
|
||||||
"""
|
|
||||||
from openslides.projector.api import update_projector
|
|
||||||
value = super(SlideMixin, self).save(*args, **kwargs)
|
|
||||||
if self.is_active_slide():
|
|
||||||
update_projector()
|
|
||||||
return value
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Updates the projector if the object is on the projector and is deleted.
|
|
||||||
"""
|
|
||||||
from openslides.projector.api import update_projector
|
|
||||||
# Checking active slide has to be done before calling super().delete()
|
|
||||||
# because super().delete() deletes the object and than we have no
|
|
||||||
# access to the former existing primary key any more. But updating
|
|
||||||
# projector has to be done after deleting the object of course.
|
|
||||||
update_required = self.is_active_slide()
|
|
||||||
value = super(SlideMixin, self).delete(*args, **kwargs)
|
|
||||||
if update_required:
|
|
||||||
update_projector()
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get_absolute_url(self, link='projector'):
|
def get_absolute_url(self, link='projector'):
|
||||||
"""
|
"""
|
||||||
Return the url to activate the slide, if link == 'projector'.
|
Return the url to activate the slide, if link == 'projector'.
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.mediafile.models import Mediafile
|
|
||||||
from openslides.utils.tornado_webserver import ProjectorSocketHandler
|
|
||||||
from openslides.utils.views import RedirectView, TemplateView
|
from openslides.utils.views import RedirectView, TemplateView
|
||||||
|
|
||||||
from .api import (call_on_projector, get_active_slide,
|
from .api import (call_on_projector, get_active_slide,
|
||||||
get_overlays, get_projector_content,
|
get_overlays, get_projector_content,
|
||||||
get_projector_overlays_js, reset_countdown, set_active_slide,
|
get_projector_overlays_js, reset_countdown, set_active_slide,
|
||||||
start_countdown, stop_countdown, update_projector_overlay)
|
start_countdown, stop_countdown)
|
||||||
|
|
||||||
|
|
||||||
class ProjectorView(TemplateView):
|
class ProjectorView(TemplateView):
|
||||||
@ -53,14 +51,13 @@ class ActivateView(RedirectView):
|
|||||||
# we dont have to use set_active_slide, because is causes a content
|
# we dont have to use set_active_slide, because is causes a content
|
||||||
# reload.
|
# reload.
|
||||||
kwargs.update({'page_num': 1, 'pk': request.GET.get('pk')})
|
kwargs.update({'page_num': 1, 'pk': request.GET.get('pk')})
|
||||||
url = Mediafile.objects.get(pk=kwargs['pk'], is_presentable=True).mediafile.url
|
# TODO: fix me
|
||||||
|
# url = Mediafile.objects.get(pk=kwargs['pk'], is_presentable=True).mediafile.url
|
||||||
config['projector_active_slide'] = kwargs
|
config['projector_active_slide'] = kwargs
|
||||||
ProjectorSocketHandler.send_updates(
|
# ProjectorSocketHandler.send_updates(
|
||||||
{'calls': {'load_pdf': {'url': url, 'page_num': kwargs['page_num']}}})
|
# {'calls': {'load_pdf': {'url': url, 'page_num': kwargs['page_num']}}})
|
||||||
else:
|
else:
|
||||||
set_active_slide(kwargs['callback'], **dict(request.GET.items()))
|
set_active_slide(kwargs['callback'], **dict(request.GET.items()))
|
||||||
call_on_projector({'scroll': config['projector_scroll'],
|
|
||||||
'scale': config['projector_scale']})
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectorControllView(RedirectView):
|
class ProjectorControllView(RedirectView):
|
||||||
@ -121,7 +118,7 @@ class CountdownControllView(RedirectView):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
reset_countdown()
|
reset_countdown()
|
||||||
update_projector_overlay('projector_countdown')
|
# TODO: send signal to update data
|
||||||
|
|
||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
return {
|
return {
|
||||||
@ -143,7 +140,7 @@ class OverlayMessageView(RedirectView):
|
|||||||
config['projector_message'] = request.POST['message_text']
|
config['projector_message'] = request.POST['message_text']
|
||||||
elif 'message-clean' in request.POST:
|
elif 'message-clean' in request.POST:
|
||||||
config['projector_message'] = ''
|
config['projector_message'] = ''
|
||||||
update_projector_overlay('projector_message')
|
# TODO: update data
|
||||||
|
|
||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
return {
|
return {
|
||||||
@ -165,12 +162,12 @@ class ActivateOverlay(RedirectView):
|
|||||||
if kwargs['activate']:
|
if kwargs['activate']:
|
||||||
if not overlay.is_active():
|
if not overlay.is_active():
|
||||||
overlay.set_active(True)
|
overlay.set_active(True)
|
||||||
update_projector_overlay(overlay)
|
# Push new overlay to projector, somehow...
|
||||||
self.active = True
|
self.active = True
|
||||||
else:
|
else:
|
||||||
if overlay.is_active():
|
if overlay.is_active():
|
||||||
overlay.set_active(False)
|
overlay.set_active(False)
|
||||||
update_projector_overlay(overlay)
|
# Push new overlay to projector, somehow...
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
def get_ajax_context(self, **kwargs):
|
def get_ajax_context(self, **kwargs):
|
||||||
|
@ -18,7 +18,11 @@ from tornado.wsgi import WSGIContainer
|
|||||||
|
|
||||||
|
|
||||||
class DjangoStaticFileHandler(StaticFileHandler):
|
class DjangoStaticFileHandler(StaticFileHandler):
|
||||||
"""Handels static data by using the django finders."""
|
"""
|
||||||
|
Handels static data by using the django finders.
|
||||||
|
|
||||||
|
Only needed in the "small" version with tornado as wsgi server.
|
||||||
|
"""
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""Overwrite some attributes."""
|
"""Overwrite some attributes."""
|
||||||
@ -47,25 +51,31 @@ class DjangoStaticFileHandler(StaticFileHandler):
|
|||||||
return absolute_path
|
return absolute_path
|
||||||
|
|
||||||
|
|
||||||
class ProjectorSocketHandler(SockJSConnection):
|
class OpenSlidesSockJSConnection(SockJSConnection):
|
||||||
"""
|
"""
|
||||||
Handels the websocket for the projector.
|
Sockjs connections for OpenSlides.
|
||||||
"""
|
"""
|
||||||
waiters = set()
|
waiters = set()
|
||||||
|
|
||||||
def on_open(self, info):
|
def on_open(self, info):
|
||||||
ProjectorSocketHandler.waiters.add(self)
|
OpenSlidesSockJSConnection.waiters.add(self)
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
ProjectorSocketHandler.waiters.remove(self)
|
OpenSlidesSockJSConnection.waiters.remove(self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def send_updates(cls, data):
|
def send_updates(cls, data):
|
||||||
|
# TODO: use a bluk send
|
||||||
for waiter in cls.waiters:
|
for waiter in cls.waiters:
|
||||||
waiter.send(data)
|
waiter.send(data)
|
||||||
|
|
||||||
|
|
||||||
def run_tornado(addr, port):
|
def run_tornado(addr, port):
|
||||||
|
"""
|
||||||
|
Starts the tornado webserver as wsgi server for OpenSlides.
|
||||||
|
|
||||||
|
It runs in one thread.
|
||||||
|
"""
|
||||||
# Don't try to read the command line args from openslides
|
# Don't try to read the command line args from openslides
|
||||||
parse_command_line(args=[])
|
parse_command_line(args=[])
|
||||||
|
|
||||||
@ -81,9 +91,9 @@ def run_tornado(addr, port):
|
|||||||
app = WSGIContainer(get_wsgi_application())
|
app = WSGIContainer(get_wsgi_application())
|
||||||
|
|
||||||
# Collect urls
|
# Collect urls
|
||||||
projectpr_socket_js_router = SockJSRouter(ProjectorSocketHandler, '/projector/socket')
|
|
||||||
from openslides.core.chatbox import ChatboxSocketHandler
|
from openslides.core.chatbox import ChatboxSocketHandler
|
||||||
chatbox_socket_js_router = SockJSRouter(ChatboxSocketHandler, '/core/chatbox')
|
chatbox_socket_js_router = SockJSRouter(ChatboxSocketHandler, '/core/chatbox')
|
||||||
|
sock_js_router = SockJSRouter(OpenSlidesSockJSConnection, '/sockjs')
|
||||||
other_urls = [
|
other_urls = [
|
||||||
(r"%s(.*)" % settings.STATIC_URL, DjangoStaticFileHandler),
|
(r"%s(.*)" % settings.STATIC_URL, DjangoStaticFileHandler),
|
||||||
(r'%s(.*)' % settings.MEDIA_URL, StaticFileHandler, {'path': settings.MEDIA_ROOT}),
|
(r'%s(.*)' % settings.MEDIA_URL, StaticFileHandler, {'path': settings.MEDIA_ROOT}),
|
||||||
@ -91,7 +101,36 @@ def run_tornado(addr, port):
|
|||||||
|
|
||||||
# Start the application
|
# Start the application
|
||||||
debug = settings.DEBUG
|
debug = settings.DEBUG
|
||||||
tornado_app = Application(projectpr_socket_js_router.urls + chatbox_socket_js_router.urls + other_urls, debug=debug)
|
tornado_app = Application(sock_js_router.urls + chatbox_socket_js_router.urls + other_urls, debug=debug)
|
||||||
server = HTTPServer(tornado_app)
|
server = HTTPServer(tornado_app)
|
||||||
server.listen(port=port, address=addr)
|
server.listen(port=port, address=addr)
|
||||||
IOLoop.instance().start()
|
IOLoop.instance().start()
|
||||||
|
|
||||||
|
|
||||||
|
def inform_changed_data(*args):
|
||||||
|
"""
|
||||||
|
Informs all users about changed data.
|
||||||
|
|
||||||
|
The arguments are Django/OpenSlides models.
|
||||||
|
"""
|
||||||
|
rest_urls = set()
|
||||||
|
for instance in args:
|
||||||
|
try:
|
||||||
|
rest_urls.add(instance.get_root_rest_url())
|
||||||
|
except AttributeError:
|
||||||
|
# instance has no method get_root_rest_url
|
||||||
|
pass
|
||||||
|
|
||||||
|
if settings.USE_TORNADO_AS_WSGI_SERVER:
|
||||||
|
for url in rest_urls:
|
||||||
|
OpenSlidesSockJSConnection.send_updates(url)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# TODO: fix me
|
||||||
|
|
||||||
|
|
||||||
|
def inform_changed_data_receiver(sender, instance, **kwargs):
|
||||||
|
"""
|
||||||
|
Receiver for the inform_changed_data function to use in a signal.
|
||||||
|
"""
|
||||||
|
inform_changed_data(instance)
|
@ -1,3 +1,28 @@
|
|||||||
|
from django.core.urlresolvers import reverse
|
||||||
from rest_framework import permissions, routers, viewsets # noqa
|
from rest_framework import permissions, routers, viewsets # noqa
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class RESTModelMixin:
|
||||||
|
"""
|
||||||
|
Mixin for django models which are used in our rest api.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the root rest instance.
|
||||||
|
|
||||||
|
Uses self as default.
|
||||||
|
"""
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_root_rest_url(self):
|
||||||
|
"""
|
||||||
|
Returns the detail url of the root model of this object.
|
||||||
|
"""
|
||||||
|
# Gets the default url-name in the same way as django rest framework
|
||||||
|
# does in relations.HyperlinkedModelSerializer
|
||||||
|
root_instance = self.get_root_rest_element()
|
||||||
|
rest_url = '%s-detail' % type(root_instance)._meta.object_name.lower()
|
||||||
|
return reverse(rest_url, args=[str(root_instance.pk)])
|
||||||
|
@ -72,23 +72,18 @@ class ListOfSpeakerModelTests(TestCase):
|
|||||||
self.assertIsNotNone(Speaker.objects.get(user=self.speaker1, item=self.item1).end_time)
|
self.assertIsNotNone(Speaker.objects.get(user=self.speaker1, item=self.item1).end_time)
|
||||||
self.assertIsNotNone(speaker2_item1.begin_time)
|
self.assertIsNotNone(speaker2_item1.begin_time)
|
||||||
|
|
||||||
@patch('openslides.agenda.models.update_projector_overlay')
|
def test_speach_coupled_with_countdown(self):
|
||||||
def test_speach_coupled_with_countdown(self, mock_update_projector_overlay):
|
|
||||||
config['agenda_couple_countdown_and_speakers'] = True
|
config['agenda_couple_countdown_and_speakers'] = True
|
||||||
speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1)
|
speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1)
|
||||||
self.item1.is_active_slide = MagicMock(return_value=True)
|
self.item1.is_active_slide = MagicMock(return_value=True)
|
||||||
|
|
||||||
speaker1_item1.begin_speach()
|
speaker1_item1.begin_speach()
|
||||||
self.assertEqual(config['countdown_state'], 'active')
|
self.assertEqual(config['countdown_state'], 'active')
|
||||||
mock_update_projector_overlay.assert_called_with('projector_countdown')
|
|
||||||
|
|
||||||
mock_update_projector_overlay.reset_mock()
|
|
||||||
speaker1_item1.end_speach()
|
speaker1_item1.end_speach()
|
||||||
self.assertEqual(config['countdown_state'], 'paused')
|
self.assertEqual(config['countdown_state'], 'paused')
|
||||||
mock_update_projector_overlay.assert_called_with('projector_countdown')
|
|
||||||
|
|
||||||
@patch('openslides.agenda.models.update_projector_overlay')
|
def test_begin_speach_not_coupled_with_countdown(self):
|
||||||
def test_begin_speach_not_coupled_with_countdown(self, mock_update_projector_overlay):
|
|
||||||
config['agenda_couple_countdown_and_speakers'] = False
|
config['agenda_couple_countdown_and_speakers'] = False
|
||||||
speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1)
|
speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1)
|
||||||
|
|
||||||
@ -98,7 +93,6 @@ class ListOfSpeakerModelTests(TestCase):
|
|||||||
config['countdown_state'] = 'active'
|
config['countdown_state'] = 'active'
|
||||||
speaker1_item1.end_speach()
|
speaker1_item1.end_speach()
|
||||||
self.assertEqual(config['countdown_state'], 'active')
|
self.assertEqual(config['countdown_state'], 'active')
|
||||||
self.assertFalse(mock_update_projector_overlay.called)
|
|
||||||
|
|
||||||
|
|
||||||
class SpeakerViewTestCase(TestCase):
|
class SpeakerViewTestCase(TestCase):
|
||||||
|
@ -5,58 +5,6 @@ from openslides.utils.test import TestCase
|
|||||||
|
|
||||||
|
|
||||||
class ApiFunctions(TestCase):
|
class ApiFunctions(TestCase):
|
||||||
@patch('openslides.projector.api.get_projector_content')
|
|
||||||
@patch('openslides.projector.api.ProjectorSocketHandler')
|
|
||||||
def test_update_projector(self, mock_ProjectorSocketHandler,
|
|
||||||
mock_get_projector_content):
|
|
||||||
mock_get_projector_content.return_value = 'mock_string'
|
|
||||||
projector_api.update_projector()
|
|
||||||
mock_ProjectorSocketHandler.send_updates.assert_called_with(
|
|
||||||
{'content': 'mock_string'})
|
|
||||||
|
|
||||||
@patch('openslides.projector.api.get_overlays')
|
|
||||||
@patch('openslides.projector.api.ProjectorSocketHandler')
|
|
||||||
def test_update_projector_overlay(self, mock_ProjectorSocketHandler,
|
|
||||||
mock_get_overlays):
|
|
||||||
mock_overlay = MagicMock()
|
|
||||||
mock_overlay.name = 'mock_overlay_name'
|
|
||||||
mock_overlay.get_projector_html.return_value = 'mock_html_code'
|
|
||||||
mock_overlay.get_javascript.return_value = 'mock_javascript'
|
|
||||||
mock_get_overlays.return_value = {'mock_overlay': mock_overlay}
|
|
||||||
|
|
||||||
# Test with active overlay
|
|
||||||
mock_overlay.is_active.return_value = False
|
|
||||||
projector_api.update_projector_overlay(None)
|
|
||||||
mock_ProjectorSocketHandler.send_updates.assert_called_with(
|
|
||||||
{'overlays': {'mock_overlay_name': None}})
|
|
||||||
|
|
||||||
# Test with active overlay
|
|
||||||
mock_overlay.is_active.return_value = True
|
|
||||||
projector_api.update_projector_overlay(None)
|
|
||||||
expected_data = {'overlays': {'mock_overlay_name': {
|
|
||||||
'html': 'mock_html_code',
|
|
||||||
'javascript': 'mock_javascript'}}}
|
|
||||||
mock_ProjectorSocketHandler.send_updates.assert_called_with(expected_data)
|
|
||||||
|
|
||||||
# Test with overlay name as argument
|
|
||||||
projector_api.update_projector_overlay('mock_overlay')
|
|
||||||
mock_ProjectorSocketHandler.send_updates.assert_called_with(expected_data)
|
|
||||||
|
|
||||||
# Test with overlay object as argument
|
|
||||||
projector_api.update_projector_overlay(mock_overlay)
|
|
||||||
mock_ProjectorSocketHandler.send_updates.assert_called_with(expected_data)
|
|
||||||
|
|
||||||
@patch('openslides.projector.api.config')
|
|
||||||
@patch('openslides.projector.api.ProjectorSocketHandler')
|
|
||||||
def test_call_on_projector(self, mock_ProjectorSocketHandler, mock_config):
|
|
||||||
mock_config.__getitem__.return_value = {}
|
|
||||||
data = {'some_call': 'argument'}
|
|
||||||
projector_api.call_on_projector(data)
|
|
||||||
mock_ProjectorSocketHandler.send_updates.assert_called_with(
|
|
||||||
{'calls': data})
|
|
||||||
mock_config.__getitem__.assert_called_with('projector_js_cache')
|
|
||||||
mock_config.__setitem__.assert_called_with('projector_js_cache', data)
|
|
||||||
|
|
||||||
@patch('openslides.projector.api.default_slide')
|
@patch('openslides.projector.api.default_slide')
|
||||||
def test_get_projector_content(self, mock_default_slide):
|
def test_get_projector_content(self, mock_default_slide):
|
||||||
mock_slide = MagicMock()
|
mock_slide = MagicMock()
|
||||||
@ -156,17 +104,13 @@ class ApiFunctions(TestCase):
|
|||||||
mock_SlideModel.objects.get.side_effect = Exception
|
mock_SlideModel.objects.get.side_effect = Exception
|
||||||
self.assertRaises(projector_api.SlideError, used_args[1], pk=1)
|
self.assertRaises(projector_api.SlideError, used_args[1], pk=1)
|
||||||
|
|
||||||
@patch('openslides.projector.api.update_projector_overlay')
|
def test_set_active_slide(self):
|
||||||
@patch('openslides.projector.api.update_projector')
|
|
||||||
def test_set_active_slide(self, mock_update_projector, mock_update_projector_overlay):
|
|
||||||
mock_config = {}
|
mock_config = {}
|
||||||
with patch('openslides.projector.api.config', mock_config):
|
with patch('openslides.projector.api.config', mock_config):
|
||||||
projector_api.set_active_slide('callback_name', some='kwargs')
|
projector_api.set_active_slide('callback_name', some='kwargs')
|
||||||
self.assertEqual(mock_config,
|
self.assertEqual(mock_config,
|
||||||
{'projector_active_slide': {'callback': 'callback_name',
|
{'projector_active_slide': {'callback': 'callback_name',
|
||||||
'some': 'kwargs'}})
|
'some': 'kwargs'}})
|
||||||
mock_update_projector.assert_called_with()
|
|
||||||
mock_update_projector_overlay.assert_called_with(None)
|
|
||||||
|
|
||||||
def test_get_active_slide(self):
|
def test_get_active_slide(self):
|
||||||
mock_config = {'projector_active_slide': 'value'}
|
mock_config = {'projector_active_slide': 'value'}
|
||||||
|
@ -41,10 +41,9 @@ class ProjectorViewTest(TestCase):
|
|||||||
class ActivateViewTest(TestCase):
|
class ActivateViewTest(TestCase):
|
||||||
rf = RequestFactory()
|
rf = RequestFactory()
|
||||||
|
|
||||||
@patch('openslides.projector.views.call_on_projector')
|
|
||||||
@patch('openslides.projector.views.config')
|
@patch('openslides.projector.views.config')
|
||||||
@patch('openslides.projector.views.set_active_slide')
|
@patch('openslides.projector.views.set_active_slide')
|
||||||
def test_get(self, mock_set_active_slide, mock_config, mock_call_on_projector):
|
def test_get(self, mock_set_active_slide, mock_config):
|
||||||
view = views.ActivateView()
|
view = views.ActivateView()
|
||||||
view.request = self.rf.get('/?some_key=some_value')
|
view.request = self.rf.get('/?some_key=some_value')
|
||||||
|
|
||||||
@ -54,7 +53,6 @@ class ActivateViewTest(TestCase):
|
|||||||
**{'some_key': 'some_value'})
|
**{'some_key': 'some_value'})
|
||||||
mock_config.get_default.assert_has_calls([])
|
mock_config.get_default.assert_has_calls([])
|
||||||
self.assertEqual(mock_config.__setitem__.call_count, 0)
|
self.assertEqual(mock_config.__setitem__.call_count, 0)
|
||||||
self.assertTrue(mock_call_on_projector.called)
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectorControllViewTest(TestCase):
|
class ProjectorControllViewTest(TestCase):
|
||||||
|
@ -7,7 +7,6 @@ from django.core.exceptions import ImproperlyConfigured
|
|||||||
from openslides.__main__ import (
|
from openslides.__main__ import (
|
||||||
add_general_arguments,
|
add_general_arguments,
|
||||||
django_command_line_utility,
|
django_command_line_utility,
|
||||||
runserver,
|
|
||||||
start,
|
start,
|
||||||
syncdb)
|
syncdb)
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
@ -117,15 +116,6 @@ class TestOtherFunctions(TestCase):
|
|||||||
self.assertTrue(mock_syncdb.called)
|
self.assertTrue(mock_syncdb.called)
|
||||||
mock_runserver.assert_called_with(None, mock_args)
|
mock_runserver.assert_called_with(None, mock_args)
|
||||||
|
|
||||||
@patch('openslides.__main__.get_port')
|
|
||||||
@patch('openslides.utils.tornado_webserver.run_tornado')
|
|
||||||
@patch('openslides.__main__.start_browser')
|
|
||||||
def test_runserver(self, mock_start_browser, mock_run_tornado, mock_get_port):
|
|
||||||
mock_get_port.return_value = 8000
|
|
||||||
mock_args = MagicMock()
|
|
||||||
runserver(settings=None, args=mock_args)
|
|
||||||
self.assertTrue(mock_run_tornado.called)
|
|
||||||
|
|
||||||
@patch('openslides.__main__.os.path.exists')
|
@patch('openslides.__main__.os.path.exists')
|
||||||
@patch('openslides.__main__.os.makedirs')
|
@patch('openslides.__main__.os.makedirs')
|
||||||
@patch('openslides.__main__.execute_from_command_line')
|
@patch('openslides.__main__.execute_from_command_line')
|
||||||
|
Loading…
Reference in New Issue
Block a user