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]
Agenda:
- New projector view with the current list of speakers.
- Added CSV import.
Assignment:
- 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
: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
-------------------------

View File

@ -101,8 +101,8 @@ def agenda_list_of_speakers(sender, **kwargs):
if slide is None or isinstance(slide, Item):
item = slide
else:
# TODO: If there are more the one items, use the first one in the
# mptt tree that is not closed
# 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),

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>
{% 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>
{% 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>
</h1>

View File

@ -89,6 +89,10 @@ urlpatterns = patterns(
views.CurrentListOfSpeakersView.as_view(end_speach=True),
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/$',
views.ItemCSVImportView.as_view(),
name='item_csv_import'))

View File

@ -2,21 +2,33 @@
# TODO: Rename all views and template names
from datetime import datetime, timedelta
from json import dumps
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.db import transaction
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_lazy
from reportlab.platypus import Paragraph
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.pdf import stylesheet
from openslides.utils.utils import html_strong
from openslides.utils.views import (
AjaxMixin,
CreateView,
CSVImportView,
DeleteView,
@ -630,6 +642,108 @@ class CurrentListOfSpeakersView(RedirectView):
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):
"""
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/projector.css' %}" type="text/css" rel="stylesheet">
<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/sockjs-0.3.min.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()
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')