Rework on list of speakers. Insert a method to get a formated list for various use in the templates.

This commit is contained in:
Norman Jäckel 2013-04-29 20:03:50 +02:00
parent 52e16d2e34
commit 719b6ff790
10 changed files with 211 additions and 202 deletions

View File

@ -6,7 +6,7 @@
The OpenSlides agenda app appends the functionality to OpenSlides to
manage agendas.
It includes time-management and list of speakers.
It includes time-management and lists of speakers.
:copyright: (c) 20112013 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.

View File

@ -99,10 +99,44 @@ class Item(MPTTModel, SlideMixin):
True, if the list of speakers is closed.
"""
class Meta:
permissions = (
('can_see_agenda', ugettext_noop("Can see agenda")),
('can_manage_agenda', ugettext_noop("Can manage agenda")),
('can_see_orga_items', ugettext_noop("Can see orga items and time scheduling of agenda")))
class MPTTMeta:
order_insertion_by = ['weight']
def __unicode__(self):
return self.get_title()
def get_absolute_url(self, link='view'):
"""
Return the URL to this item. By default it is the link to its
view or the view of a related object.
The link can be:
* view
* edit
* delete
"""
if link == 'view':
if self.related_sid:
return self.get_related_slide().get_absolute_url(link)
return reverse('item_view', args=[str(self.id)])
if link == 'edit':
if self.related_sid:
return self.get_related_slide().get_absolute_url(link)
return reverse('item_edit', args=[str(self.id)])
if link == 'delete':
return reverse('item_delete', args=[str(self.id)])
def get_related_slide(self):
"""
return the object, of which the item points.
Return the object at which the item points.
"""
# TODO: Rename it to 'get_related_object'
object = get_slide_from_sid(self.related_sid, element=True)
if object is None:
self.title = 'Item for deleted slide: %s' % self.related_sid
@ -114,7 +148,7 @@ class Item(MPTTModel, SlideMixin):
def get_related_type(self):
"""
return the type of the releated slide.
Return the type of the releated slide.
"""
return self.get_related_slide().prefix
@ -129,7 +163,7 @@ class Item(MPTTModel, SlideMixin):
def get_title(self):
"""
return the title of this item.
Return the title of this item.
"""
if self.related_sid is None:
return self.title
@ -137,7 +171,7 @@ class Item(MPTTModel, SlideMixin):
def get_title_supplement(self):
"""
return a supplement for the title.
Return a supplement for the title.
"""
if self.related_sid is None:
return ''
@ -148,45 +182,36 @@ class Item(MPTTModel, SlideMixin):
def slide(self):
"""
Return a map with all Data for the Slide
Return a map with all data for the slide.
There are four cases:
* summary slide
* list of speakers
* related slide, i. e. the slide of the related object
* normal slide of the item
The method returns only one of them according to the config value
'presentation_argument' and the attribut 'related_sid'.
"""
if config['presentation_argument'] == 'summary':
data = {
'title': self.get_title(),
'items': self.get_children(),
'template': 'projector/AgendaSummary.html',
}
data = {'title': self.get_title(),
'items': self.get_children(),
'template': 'projector/AgendaSummary.html'}
elif config['presentation_argument'] == 'show_list_of_speakers':
speaker_query = Speaker.objects.filter(item=self)
coming_speakers = speaker_query.filter(begin_time=None).order_by('weight')
old_speakers_count = config['agenda_show_last_speakers']
if old_speakers_count > 0:
old_speakers = speaker_query.exclude(end_time=None)
old_speakers = old_speakers[max(0, old_speakers.count()) - old_speakers_count:]
else:
old_speakers = speaker_query.none()
try:
actual_speaker = speaker_query.filter(end_time=None).exclude(begin_time=None).get()
except Speaker.DoesNotExist:
actual_speaker = None
speakers = list(old_speakers) + [actual_speaker] + list(coming_speakers)
list_of_speakers = self.get_list_of_speakers(
old_speakers_count=config['agenda_show_last_speakers'])
data = {'title': self.get_title(),
'template': 'projector/agenda_list_of_speaker.html',
'speakers': speakers,
'actual_speaker': actual_speaker,
'old_speakers_count': -(old_speakers_count + 1)}
'list_of_speakers': list_of_speakers}
elif self.related_sid:
data = self.get_related_slide().slide()
else:
data = {
'item': self,
'title': self.get_title(),
'template': 'projector/AgendaText.html',
}
data = {'item': self,
'title': self.get_title(),
'template': 'projector/AgendaText.html'}
return data
def set_closed(self, closed=True):
@ -224,39 +249,67 @@ class Item(MPTTModel, SlideMixin):
super(Item, self).delete()
Item.objects.rebuild()
def get_absolute_url(self, link='view'):
def get_list_of_speakers(self, old_speakers_count=None, coming_speakers_count=None):
"""
Return the URL to this item. By default it is the Link to its
slide
link can be:
* view
* edit
* delete
Returns the list of speakers as a list of dictionaries. Each
dictionary contains a prefix, the speaker and its type. Types
are old_speaker, actual_speaker and coming_speaker.
"""
if link == 'view':
if self.related_sid:
return self.get_related_slide().get_absolute_url(link)
return reverse('item_view', args=[str(self.id)])
if link == 'edit':
if self.related_sid:
return self.get_related_slide().get_absolute_url(link)
return reverse('item_edit', args=[str(self.id)])
if link == 'delete':
return reverse('item_delete', args=[str(self.id)])
speaker_query = Speaker.objects.filter(item=self)
list_of_speakers = []
def __unicode__(self):
return self.get_title()
# Parse old speakers
old_speakers = speaker_query.exclude(begin_time=None).exclude(end_time=None).order_by('end_time')
if old_speakers_count is None:
old_speakers_count = old_speakers.count()
last_old_speakers_count = max(0, old_speakers.count() - old_speakers_count)
old_speakers = old_speakers[last_old_speakers_count:]
for number, speaker in enumerate(old_speakers):
prefix = old_speakers_count - number
speaker_dict = {
'prefix': '-%d' % prefix,
'speaker': speaker,
'type': 'old_speaker',
'first_in_group': False,
'last_in_group': False}
if number == 0:
speaker_dict['first_in_group'] = True
if number == old_speakers_count - 1:
speaker_dict['last_in_group'] = True
list_of_speakers.append(speaker_dict)
class Meta:
permissions = (
('can_see_agenda', ugettext_noop("Can see agenda")),
('can_manage_agenda', ugettext_noop("Can manage agenda")),
('can_see_orga_items', ugettext_noop("Can see orga items and time scheduling of agenda")),
)
# Parse actual speaker
try:
actual_speaker = speaker_query.filter(end_time=None).exclude(begin_time=None).get()
except Speaker.DoesNotExist:
pass
else:
list_of_speakers.append({
'prefix': '0',
'speaker': actual_speaker,
'type': 'actual_speaker',
'first_in_group': True,
'last_in_group': True})
class MPTTMeta:
order_insertion_by = ['weight']
# Parse coming speakers
coming_speakers = speaker_query.filter(begin_time=None).order_by('weight')
if coming_speakers_count is None:
coming_speakers_count = coming_speakers.count()
coming_speakers = coming_speakers[:max(0, coming_speakers_count)]
for number, speaker in enumerate(coming_speakers):
speaker_dict = {
'prefix': number + 1,
'speaker': speaker,
'type': 'coming_speaker',
'first_in_group': False,
'last_in_group': False}
if number == 0:
speaker_dict['first_in_group'] = True
if number == coming_speakers_count - 1:
speaker_dict['last_in_group'] = True
list_of_speakers.append(speaker_dict)
return list_of_speakers
class SpeakerManager(models.Manager):

View File

@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
from django.template.loader import render_to_string
from openslides.config.signals import config_signal
from openslides.config.api import ConfigVariable, ConfigPage, config
from openslides.config.api import config, ConfigVariable, ConfigPage
from openslides.projector.signals import projector_overlays
from openslides.projector.projector import Overlay
@ -45,10 +45,10 @@ def setup_agenda_config_page(sender, **kwargs):
agenda_show_last_speakers = ConfigVariable(
name='agenda_show_last_speakers',
default_value=0,
default_value=1,
form_field=forms.IntegerField(
min_value=0,
label=_('Number of last speakers, to show on the projector')))
label=_('Number of last speakers to be shown on the projector')))
extra_stylefiles = ['styles/timepicker.css', 'styles/jquery-ui/jquery-ui.custom.min.css']
extra_javascript = ['javascript/jquery-ui.custom.min.js',
@ -87,27 +87,10 @@ def agenda_list_of_speakers(sender, **kwargs):
# Only show list of speakers on Agenda-Items
return None
clear_projector_cache()
speaker_query = Speaker.objects.filter(item=slide)
try:
actual_speaker = speaker_query.filter(end_time=None).exclude(begin_time=None).get()
except Speaker.DoesNotExist:
actual_speaker = None
coming_speakers = speaker_query.filter(begin_time=None)[:5]
old_speakers_count = config['agenda_show_last_speakers']
if old_speakers_count > 0:
old_speakers = speaker_query.exclude(end_time=None)
old_speakers = old_speakers[max(0, old_speakers.count()) - old_speakers_count:]
else:
old_speakers = speaker_query.none()
speakers = list(old_speakers) + [actual_speaker] + list(coming_speakers)
context = {
'actual_speaker': actual_speaker,
'speakers': speakers,
'old_speakers_count': -(old_speakers_count + 1)}
list_of_speakers = slide.get_list_of_speakers(
old_speakers_count=config['agenda_show_last_speakers'],
coming_speakers_count=5)
context = {'list_of_speakers': list_of_speakers}
return render_to_string('agenda/overlay_speaker_projector.html', context)
return Overlay(name, get_widget_html, get_projector_html)

View File

@ -24,8 +24,8 @@ function hideClosedSlides(hide) {
return false;
}
$('#speaker_list_changed_form').submit(function() {
$('#sort_order').val($('#list_of_speakers').sortable("toArray"));
$('#coming_speakers_changed_form').submit(function() {
$('#sort_order').val($('#coming_speakers').sortable("toArray"));
});
$(function() {
@ -76,10 +76,10 @@ $(function() {
//# $('#hide_closed_items').attr('checked', true);
//# }
if ($('#list_of_speakers').length > 0) {
$('#list_of_speakers').sortable({axis: "y", containment: "parent", update: function(event, ui) {
$('#speaker_list_changed_form').show();
if ($('#coming_speakers').length > 0) {
$('#coming_speakers').sortable({axis: "y", containment: "parent", update: function(event, ui) {
$('#coming_speakers_changed_form').show();
}});
$('#list_of_speakers').disableSelection();
$('#coming_speakers').disableSelection();
}
});

View File

@ -22,15 +22,15 @@ table#agendatime td {
white-space: nowrap;
}
#list_of_speakers li {
line-height: 30px;
}
#list_of_speakers {
div#complete_list_of_speakers li {
list-style-type: none;
}
#list_of_speakers span.ui-icon {
div#complete_list_of_speakers li {
line-height: 30px;
}
#coming_speakers span.ui-icon {
position: absolute;
margin-left: -15px;
margin-top: 6px;

View File

@ -35,18 +35,18 @@
</style>
<div id="overlay_list_of_speaker_box">
{% if speakers %}
{% if list_of_speakers %}
<h3>{% trans "List of speakers" %}:</h3>
<ul>
{% for speaker in speakers %}
{% for speaker_dict in list_of_speakers %}
<li>
{% if speaker == actual_speaker %}
{% if speaker_dict.type == 'actual_speaker' %}
<span class="number"></span><strong>
{% else %}
<span class="number">{{ forloop.counter|addition:old_speakers_count }}</span>
<span class="number">{{ speaker_dict.prefix }}</span>
{% endif %}
<span class="name">{{ speaker }}</span>
{% if speaker == actual_speaker %}
<span class="name">{{ speaker_dict.speaker }}</span>
{% if speaker_dict.type == 'actual_speaker' %}
</strong>
{% endif %}
</li>

View File

@ -70,52 +70,11 @@
<i class="icon icon-facetime-video {% if item.active and show_list %}icon-white{% endif %}"></i>
{% trans 'Show list' %}
</a>
{% endif %}
</p>
{% if old_speakers %}
<div class="well">
<b>{% trans "Last speakers" %}:</b>
<div class="btn-group" data-toggle="buttons-checkbox">
<button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#all_speakers">
{% trans "Show all speakers" %}
</button>
</div>
<br />
<div id="all_speakers" class="collapse out">
{% for speaker in old_speakers %}
<small>{{forloop.counter}}.
[{{ speaker.begin_time }} {{ speaker.end_time }}]
<a href="{% model_url speaker %}">{{ speaker }}</a>
{% if perms.agenda.can_manage_agenda %}
<a href="{% model_url speaker 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i>
</a>
{% endif %}
</small><br />
{% endfor %}
</div>
</div>
{% endif %}
{% if actual_speaker %}
<div class="well">
<b>{% trans 'Actual speaker' %}:</b><br />
[{{ actual_speaker.begin_time }}]
<a href="{% model_url actual_speaker %}">{{ actual_speaker }}</a>
{% if perms.agenda.can_manage_agenda %}
<a href="{% url 'agenda_speaker_end_speach' item.pk %}" class="btn btn-mini"><i class="icon-bell"></i> {% trans 'End speach' %}</a>
<a href="{% model_url actual_speaker 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i>
</a>
{% endif %}
</div>
{% endif %}
{% if perms.agenda.can_manage_agenda %}
<form id="speaker_list_changed_form" action="{% url 'agenda_speaker_change_order' item.pk %}" method="post" style="display:none" class="alert alert-warning">{% csrf_token %}
<form id="coming_speakers_changed_form" action="{% url 'agenda_speaker_change_order' item.pk %}" method="post" style="display:none" class="alert alert-warning">{% csrf_token %}
<button type="button" class="close" data-dismiss="alert">×</button>
<p>{% trans "Do you want to save the changed order of speakers?" %}</p>
<input id="sort_order" name="sort_order" type="hidden"></hidden>
@ -126,25 +85,52 @@
</form>
{% endif %}
<div class="well">
<b>{% trans "Next speakers" %}:</b>
<ul {% if perms.agenda.can_manage_agenda %}id="list_of_speakers"{% endif %}>
{% for speaker in coming_speakers %}
<li id="speaker_{{ speaker.pk }}">
<span {% if perms.agenda.can_manage_agenda %}class="ui-icon ui-icon-arrowthick-2-n-s"{% endif %}></span>
<span>{{ forloop.counter }}.</span>
<a href="{% model_url speaker %}">{{ speaker }}</a>
{% if perms.agenda.can_manage_agenda %}
<a href="{% url 'agenda_speaker_speak' item.pk speaker.person.person_id %}" class="btn btn-mini"><i class="icon-bell"></i> {% trans "Next speaker" %}</a>
<a href="{% model_url speaker 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i>
</a>
<div id="complete_list_of_speakers" class="well">
{% for speaker_dict in list_of_speakers %}
{% if speaker_dict.first_in_group %}
{% if speaker_dict.type == 'old_speaker' %}
<b>{% trans "Last speakers" %}:</b>
<div class="btn-group" data-toggle="buttons-checkbox">
<button type="button" class="btn btn-mini" data-toggle="collapse" data-target="#old_speakers">
{% trans "Show all speakers" %}
</button>
</div>
{% elif speaker_dict.type == 'actual_speaker' %}
<b>{% trans 'Actual speaker' %}:</b>
{% else %}
<b>{% trans "Next speakers" %}:</b>
{% endif %}
<ul
{% if speaker_dict.type == 'old_speaker' %}
id="old_speakers" class="collapse out"
{% elif speaker_dict.type == 'coming_speaker' %}
id="coming_speakers"
{% endif %}
</li>
{% empty %}
<i>{% trans "The list of speakers is empty." %}</i>
{% endfor %}
</ul>
>
{% endif %}
<li id="speaker_{{ speaker_dict.speaker.pk }}">
{% if speaker_dict.type == 'coming_speaker' %}
<span {% if perms.agenda.can_manage_agenda %}class="ui-icon ui-icon-arrowthick-2-n-s"{% endif %}></span>
{{ speaker_dict.prefix }}.
{% else %}
[{{ speaker_dict.speaker.begin_time }}{% if speaker_dict.type == 'old_speaker' %} {{ speaker_dict.speaker.end_time }}{% endif %}]
{% endif %}
<a href="{% model_url speaker_dict.speaker %}">{{ speaker_dict.speaker }}</a>
{% if perms.agenda.can_manage_agenda %}
{% if speaker_dict.type == 'actual_speaker' %}
<a href="{% url 'agenda_speaker_end_speach' item.pk %}" class="btn btn-mini"><i class="icon-bell"></i> {% trans 'End speach' %}</a>
{% elif speaker_dict.type == 'coming_speaker' %}
<a href="{% url 'agenda_speaker_speak' item.pk speaker_dict.speaker.person.person_id %}" class="btn btn-mini"><i class="icon-bell"></i> {% trans "Next speaker" %}</a>
{% endif %}
<a href="{% model_url speaker_dict.speaker 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i>
</a>
{% endif %}
</li>
{% if speaker_dict.last_in_group %}
</ul>
{% endif %}
{% endfor %}
<p>
{% if is_on_the_list_of_speakers %}
@ -171,6 +157,6 @@
{% endfor %}
</form>
{% endif %}
</div>
{% endblock %}

View File

@ -12,29 +12,29 @@
{% block scrollcontent %}
<style type="text/css">
ul#list_of_speakers {
list-style-type: None;
}
#overlay_list_of_speaker_box li {
margin-bottom: 1px;
}
#overlay_list_of_speaker_box li span.number {
display: block;
float: left;
width: 1.3em;
}
ul#list_of_speakers {
list-style-type: None;
}
#overlay_list_of_speaker_box li {
margin-bottom: 1px;
}
#overlay_list_of_speaker_box li span.number {
display: block;
float: left;
width: 1.3em;
}
</style>
{% if speakers %}
{% if list_of_speakers %}
<ul id="list_of_speakers">
{% for speaker in speakers %}
{% for speaker_dict in list_of_speakers %}
<li>
{% if speaker == actual_speaker %}
{% if speaker_dict.type == 'actual_speaker' %}
<span class="number"></span><strong>
{% else %}
<span class="number">{{ forloop.counter|addition:old_speakers_count }}</span>
<span class="number">{{ speaker_dict.prefix }} </span>
{% endif %}
<span class="name">{{ speaker }}</span>
{% if speaker == actual_speaker %}
<span class="name">{{ speaker_dict.speaker }}</span>
{% if speaker_dict.type == 'actual_speaker' %}
</strong>
{% endif %}
</li>

View File

@ -37,7 +37,7 @@ from .forms import ItemOrderForm, ItemForm, AppendSpeakerForm
class Overview(TemplateView):
"""
Show all agenda items, and update there range via post.
Show all agenda items, and update their range via post.
"""
permission_required = 'agenda.can_see_agenda'
template_name = 'agenda/overview.html'
@ -127,19 +127,11 @@ class AgendaItemView(SingleObjectMixin, FormView):
def get_context_data(self, **kwargs):
self.object = self.get_object()
speaker_query = Speaker.objects.filter(item=self.object)
coming_speakers = speaker_query.filter(begin_time=None).order_by('weight')
old_speakers = speaker_query.exclude(begin_time=None).exclude(end_time=None).order_by('end_time')
try:
actual_speaker = speaker_query.filter(end_time=None).exclude(begin_time=None).get()
except Speaker.DoesNotExist:
actual_speaker = None
list_of_speakers = self.object.get_list_of_speakers()
kwargs.update({
'object': self.object,
'coming_speakers': coming_speakers,
'old_speakers': old_speakers,
'actual_speaker': actual_speaker,
'is_on_the_list_of_speakers': speaker_query.filter(begin_time=None, person=self.request.user).exists(),
'list_of_speakers': list_of_speakers,
'is_on_the_list_of_speakers': Speaker.objects.filter(item=self.object, begin_time=None, person=self.request.user).exists(),
'show_list': config['presentation_argument'] == 'show_list_of_speakers',
})
return super(AgendaItemView, self).get_context_data(**kwargs)
@ -292,7 +284,7 @@ class SpeakerDeleteView(DeleteView):
if 'speaker' in kwargs:
return request.user.has_perm('agenda.can_manage_agenda')
else:
# Any person how is on the list of speakers can delete him self from the list
# Any person who is on the list of speakers can delete himself from the list.
return True
def get(self, *args, **kwargs):

View File

@ -27,11 +27,6 @@ def get_config(key):
return config[key]
@register.filter
def addition(value, add):
return int(value) + int(add)
@register.simple_tag
def active(request, pattern):
if request.path.startswith(pattern):