New projector view with the current list of speakers.

This commit is contained in:
Norman Jäckel 2014-04-06 15:46:49 +02:00
parent f8cdad711c
commit ac1ab81d7a
10 changed files with 171 additions and 5 deletions

View File

@ -9,6 +9,7 @@ Version 1.6 (unreleased)
[https://github.com/OpenSlides/OpenSlides/issues?milestone=14] [https://github.com/OpenSlides/OpenSlides/issues?milestone=14]
Agenda: Agenda:
- New projector view with the current list of speakers.
- Added CSV import. - Added CSV import.
Assignment: Assignment:
- Coupled assignment candidates with list of speakers. - Coupled assignment candidates with list of speakers.

View File

@ -137,6 +137,12 @@ Redner. Die Einblendung erscheint nur auf Folien von Tagesordnungspunkten.
:scale-latex: 80 :scale-latex: 80
:alt: Projektor-Ansicht mit Rednerlisten-Overlay :alt: Projektor-Ansicht mit Rednerlisten-Overlay
Schließlich haben Sie die Möglichkeit, die Rednerliste des jeweiligen
Tagesordnungspunktes auf einem gesonderten Projektor anzeigen zu lassen.
Klicken Sie auf der Tagesordnungsseite oben rechts auf auf den
Glocken-Button |bell| und legen Sie die Seite im Vollbildmodus auf einen
eigenen Projektor oder Bildschirm.
Die Rednerliste verwalten Die Rednerliste verwalten
------------------------- -------------------------

View File

@ -101,8 +101,8 @@ def agenda_list_of_speakers(sender, **kwargs):
if slide is None or isinstance(slide, Item): if slide is None or isinstance(slide, Item):
item = slide item = slide
else: else:
# TODO: If there are more the one items, use the first one in the # TODO: If there is more than one item, use the first one in the
# mptt tree that is not closed # mptt tree that is not closed.
try: try:
item = Item.objects.filter( item = Item.objects.filter(
content_type=ContentType.objects.get_for_model(slide), content_type=ContentType.objects.get_for_model(slide),

View File

@ -0,0 +1,14 @@
/*
* JavaScript functions for agenda CurrentListOfSpeakersProjectorView
*/
function reloadListOfSpeakers() {
$.ajax({
url: '',
success: function (data) {
updater.updateProjector(data);
setTimeout('reloadListOfSpeakers()', 2000);
},
dataType: 'json'
});
}

View File

@ -0,0 +1,4 @@
{% extends 'projector.html' %}
{% load i18n %}
{% block title %}{% trans 'List of speakers' %} {{ block.super }}{% endblock %}

View File

@ -39,6 +39,10 @@
<a href="{% url 'item_csv_import' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Import agenda items' %}"><i class="icon-import"></i> {% trans "Import" %}</a> <a href="{% url 'item_csv_import' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Import agenda items' %}"><i class="icon-import"></i> {% trans "Import" %}</a>
{% endif %} {% endif %}
<a href="{% url 'print_agenda' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print agenda as PDF' %}" target="_blank"><i class="icon-print"></i> PDF</a> <a href="{% url 'print_agenda' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print agenda as PDF' %}" target="_blank"><i class="icon-print"></i> PDF</a>
{% if perms.core.can_see_projector %}
<a href="{% url 'agenda_current_list_of_speakers_projector' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Current list of speakers' %}">
<i class="icon-bell"></i> {% trans 'List of speakers' %}</a>
{% endif %}
</small> </small>
</h1> </h1>

View File

@ -89,6 +89,10 @@ urlpatterns = patterns(
views.CurrentListOfSpeakersView.as_view(end_speach=True), views.CurrentListOfSpeakersView.as_view(end_speach=True),
name='agenda_end_speach_on_current_list_of_speakers'), name='agenda_end_speach_on_current_list_of_speakers'),
url(r'^list_of_speakers/projector/$',
views.CurrentListOfSpeakersProjectorView.as_view(),
name='agenda_current_list_of_speakers_projector'),
url(r'^csv_import/$', url(r'^csv_import/$',
views.ItemCSVImportView.as_view(), views.ItemCSVImportView.as_view(),
name='item_csv_import')) name='item_csv_import'))

View File

@ -2,21 +2,33 @@
# TODO: Rename all views and template names # TODO: Rename all views and template names
from datetime import datetime, timedelta from datetime import datetime, timedelta
from json import dumps
from django.contrib import messages from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from django.db.models import Model from django.db.models import Model
from django.template.loader import render_to_string
from django.utils.datastructures import SortedDict
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from reportlab.platypus import Paragraph from reportlab.platypus import Paragraph
from openslides.config.api import config from openslides.config.api import config
from openslides.projector.api import get_active_slide, update_projector from openslides.projector.api import (
get_active_object,
get_active_slide,
get_projector_overlays_js,
get_overlays,
update_projector)
from openslides.utils.exceptions import OpenSlidesError from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.pdf import stylesheet from openslides.utils.pdf import stylesheet
from openslides.utils.utils import html_strong from openslides.utils.utils import html_strong
from openslides.utils.views import ( from openslides.utils.views import (
AjaxMixin,
CreateView, CreateView,
CSVImportView, CSVImportView,
DeleteView, DeleteView,
@ -630,6 +642,108 @@ class CurrentListOfSpeakersView(RedirectView):
return reverse('item_view', args=[item.pk]) return reverse('item_view', args=[item.pk])
class CurrentListOfSpeakersProjectorView(AjaxMixin, TemplateView):
"""
View with the current list of speakers depending on the active slide.
Usefule for the projector.
"""
template_name = 'agenda/current_list_of_speakers_projector.html'
def get(self, request, *args, **kwargs):
"""
Returns response object depending on request type (ajax or normal).
"""
if request.is_ajax():
value = self.ajax_get(request, *args, **kwargs)
else:
value = super(CurrentListOfSpeakersProjectorView, self).get(request, *args, **kwargs)
return value
def get_item(self):
"""
Returns the item of the current slide is an agenda item slide or a
slide of a related model else returns None.
"""
slide_object = get_active_object()
if slide_object is None or isinstance(slide_object, Item):
item = slide_object
else:
# TODO: If there is more than one item, use the first one in the
# mptt tree that is not closed.
try:
item = Item.objects.filter(
content_type=ContentType.objects.get_for_model(slide_object),
object_id=slide_object.pk)[0]
except IndexError:
item = None
return item
def get_content(self):
"""
Returns the content of this slide.
"""
item = self.get_item()
if item is None:
content = mark_safe('<h1>%s</h1><i>%s</i>\n' % (_('List of speakers'), _('Not available.')))
else:
content_dict = {
'title': item.get_title(),
'item': item,
'list_of_speakers': item.get_list_of_speakers(
old_speakers_count=config['agenda_show_last_speakers'])}
content = render_to_string('agenda/item_slide_list_of_speaker.html', content_dict)
return content
def get_overlays_and_overlay_js(self):
"""
Returns the overlays and their JavaScript for this slide as a
two-tuple. The overlay 'agenda_speaker' is always excluded.
The required JavaScript fot this view is inserted.
"""
overlays = get_overlays(only_active=True)
overlays.pop('agenda_speaker', None)
overlay_js = get_projector_overlays_js(as_json=True)
# Note: The JavaScript content of overlay 'agenda_speaker' is not
# excluded because this overlay has no such content at the moment.
extra_js = SortedDict()
extra_js['load_file'] = static('js/agenda_current_list_of_speakers_projector.js')
extra_js['call'] = 'reloadListOfSpeakers();'
extra_js = dumps(extra_js)
overlay_js.append(extra_js)
return overlays, overlay_js
def get_context_data(self, **context):
"""
Returns the context for the projector template. Contains the content
of this slide.
"""
overlays, overlay_js = self.get_overlays_and_overlay_js()
return super(CurrentListOfSpeakersProjectorView, self).get_context_data(
content=self.get_content(),
overlays=overlays,
overlay_js=overlay_js,
**context)
def get_ajax_context(self, **context):
"""
Returns the context including the slide content for ajax response. The
overlay 'agenda_speaker' is always excluded.
"""
overlay_dict = {}
for overlay in get_overlays().values():
if overlay.is_active() and overlay.name != 'agenda_speaker':
overlay_dict[overlay.name] = {
'html': overlay.get_projector_html(),
'javascript': overlay.get_javascript()}
else:
overlay_dict[overlay.name] = None
return super(CurrentListOfSpeakersProjectorView, self).get_ajax_context(
content=self.get_content(),
overlays=overlay_dict,
**context)
class ItemCSVImportView(CSVImportView): class ItemCSVImportView(CSVImportView):
""" """
Imports agenda items from an uploaded csv file. Imports agenda items from an uploaded csv file.

View File

@ -8,8 +8,7 @@
<link href="{% static 'css/bootstrap.min.css' %}" type="text/css" rel="stylesheet"> <link href="{% static 'css/bootstrap.min.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'css/projector.css' %}" type="text/css" rel="stylesheet"> <link href="{% static 'css/projector.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon"> <link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon">
<title>{% trans 'Projector' %} {{ 'event_name'|get_config }}</title> <title>{% block title %}{% trans 'Projector' %} {{ 'event_name'|get_config }}{% endblock %}</title>
<script type="text/javascript" src="{% static 'js/jquery/jquery.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery/jquery.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/sockjs-0.3.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/sockjs-0.3.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/projector.js' %}"></script> <script type="text/javascript" src="{% static 'js/projector.js' %}"></script>

View File

@ -314,3 +314,23 @@ class TestOverlay(TestCase):
value = agenda_list_of_speakers(sender='test').get_projector_html() value = agenda_list_of_speakers(sender='test').get_projector_html()
self.assertEqual(value, '') self.assertEqual(value, '')
class TestCurrentListOfSpeakersOnProjectorView(SpeakerViewTestCase):
"""
Test the view with the current list of speakers depending on the actual
slide.
"""
def test_get_none(self):
response = self.admin_client.get('/agenda/list_of_speakers/projector/')
self.assertContains(response, 'List of speakers</h1><i>Not available')
def test_get_normal(self):
self.item1.title = 'title_gupooDee8ahahnaxoo2a'
self.item1.save()
Speaker.objects.add(self.speaker1, self.item1)
config['projector_active_slide'] = {'callback': 'agenda', 'pk': self.item1.pk}
response = self.admin_client.get('/agenda/list_of_speakers/projector/')
self.assertContains(response, 'List of speakers')
self.assertContains(response, 'title_gupooDee8ahahnaxoo2a')
self.assertContains(response, 'speaker1')