Automatic numbering of agenda items

This commit is contained in:
Stefan Frauenknecht 2014-04-27 21:01:23 +02:00 committed by Norman Jäckel
parent 943ede2e81
commit 2c5b3a8e4f
8 changed files with 156 additions and 4 deletions

View File

@ -16,7 +16,6 @@ class ItemForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
"""
Form to create of update an item.
"""
clean_html_fields = ('text', )
parent = TreeNodeChoiceField(
@ -32,7 +31,7 @@ class ItemForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
class Meta:
model = Item
exclude = ('closed', 'weight', 'content_type', 'object_id')
exclude = ('closed', 'weight', 'content_type', 'object_id', 'item_number')
class RelatedItemForm(ItemForm):
@ -41,7 +40,7 @@ class RelatedItemForm(ItemForm):
"""
class Meta:
model = Item
exclude = ('closed', 'type', 'weight', 'content_type', 'object_id', 'title', 'text')
exclude = ('closed', 'type', 'weight', 'content_type', 'object_id', 'title', 'text', 'item_number')
class ItemOrderForm(CssClassMixin, forms.Form):

View File

@ -19,6 +19,7 @@ from openslides.projector.models import SlideMixin
from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import AbsoluteUrlMixin
from openslides.utils.person.models import PersonField
from openslides.utils.utils import toRoman
class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
@ -41,6 +42,11 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
Title of the agenda item.
"""
item_number = models.CharField(null=True, max_length=255, verbose_name=ugettext_lazy("Number"))
"""
Number of agenda item.
"""
text = models.TextField(null=True, blank=True, verbose_name=ugettext_lazy("Text"))
"""
The optional text of the agenda item.
@ -147,6 +153,9 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
Return the title of this item.
"""
if not self.content_object:
if config['agenda_enable_auto_numbering']:
item_no = self.item_no
return item_no + ' ' + self.title if item_no else self.title
return self.title
try:
return self.content_object.get_agenda_title()
@ -284,6 +293,40 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel):
value = False
return value
@property
def item_no(self):
if config['agenda_agenda_fixed']:
item_no = self.item_number
else:
item_no = self.calc_item_no()
if item_no:
return '%s %s' % (config['agenda_number_prefix'], item_no)
def calc_item_no(self):
"""
Returns the number of this agenda item
"""
if self.type == self.AGENDA_ITEM:
if self.is_root_node():
if config['agenda_numeral_system'] == 'a':
return str(self._calc_sibling_no())
else:
return toRoman(self._calc_sibling_no())
else:
return '%s.%s' % (self.parent.calc_item_no(), self._calc_sibling_no())
def _calc_sibling_no(self):
"""
Counts all siblings on the same level which are AGENDA_ITEMs
"""
sibling_no = 0
prev_sibling = self.get_previous_sibling()
while not prev_sibling is None:
if prev_sibling.type == self.AGENDA_ITEM:
sibling_no += 1
prev_sibling = prev_sibling.get_previous_sibling()
return sibling_no + 1
class SpeakerManager(models.Manager):
def add(self, person, item):

View File

@ -60,6 +60,37 @@ def setup_agenda_config(sender, **kwargs):
help_text=ugettext_lazy('[Begin speach] starts the countdown, [End speach] stops the countdown.'),
required=False))
agenda_enable_auto_numbering = ConfigVariable(
name='agenda_enable_auto_numbering',
default_value=False,
form_field=forms.BooleanField(
label=ugettext_lazy('Enable automatic numbering of agenda items'),
required=False))
agenda_number_prefix = ConfigVariable(
name='agenda_number_prefix',
default_value='',
form_field=forms.CharField(
label=ugettext_lazy('Numbering prefix for agenda items'),
max_length=20,
required=False))
agenda_numeral_system = ConfigVariable(
name='agenda_numeral_system',
default_value='a',
form_field=forms.ChoiceField(
label=ugettext_lazy('Numeral System for Top items'),
widget=forms.Select(),
choices=(
('a', ugettext_lazy('Arabic')),
('r', ugettext_lazy('Roman'))),
required=False))
agenda_agenda_fixed = ConfigVariable(
name='agenda_agenda_fixed',
default_value=False,
form_field=None)
extra_stylefiles = ['css/jquery-ui-timepicker.css']
extra_javascript = ['js/jquery/jquery-ui-timepicker-addon.min.js',
'js/jquery/jquery-ui-sliderAccess.min.js',
@ -71,7 +102,11 @@ def setup_agenda_config(sender, **kwargs):
weight=20,
variables=(agenda_start_event_date_time,
agenda_show_last_speakers,
agenda_couple_countdown_and_speakers),
agenda_couple_countdown_and_speakers,
agenda_enable_auto_numbering,
agenda_number_prefix,
agenda_numeral_system,
agenda_agenda_fixed),
extra_context={'extra_stylefiles': extra_stylefiles,
'extra_javascript': extra_javascript})

View File

@ -70,6 +70,12 @@
{% else %}
<a href="{% url 'config_agenda' %}" class="btn btn-mini pull-right">{% trans 'Set start time of event' %}</a>
{% endif %}
{% if perms.agenda.can_manage_agenda and agenda_enable_auto_numbering %}
<a href="{% url 'fix_agenda' %}"
class="btn btn-mini pull-left" {% if agenda_numbering_fixed %}disabled="disabled"{% endif %}>{% trans 'Fix numbering' %}</a>
<a href="{% url 'reset_agenda' %}"
class="btn btn-mini pull-left" {% if not agenda_numbering_fixed %}disabled="disabled"{% endif %}>{% trans 'Reset numbering' %}</a>
{% endif %}
{% endif %}
</div>
</div>

View File

@ -40,6 +40,14 @@ urlpatterns = patterns(
views.AgendaPDF.as_view(),
name='print_agenda'),
url(r'^fix/$',
views.FixAgendaView.as_view(),
name='fix_agenda'),
url(r'^reset/$',
views.ResetAgendaView.as_view(),
name='reset_agenda'),
# List of speakers
url(r'^(?P<pk>\d+)/speaker/$',
views.SpeakerAppendView.as_view(),

View File

@ -34,6 +34,7 @@ from openslides.utils.views import (
DeleteView,
FormView,
PDFView,
QuestionView,
RedirectView,
SingleObjectMixin,
TemplateView,
@ -108,9 +109,14 @@ class Overview(TemplateView):
agenda_is_active = None
active_type = None
agenda_enable_auto_numbering = True if config['agenda_enable_auto_numbering'] else False
agenda_numbering_fixed = True if config['agenda_agenda_fixed'] else False
context.update({
'items': items,
'agenda_is_active': agenda_is_active,
'agenda_enable_auto_numbering': agenda_enable_auto_numbering,
'agenda_numbering_fixed': agenda_numbering_fixed,
'duration': duration,
'start': start,
'end': end,
@ -336,6 +342,48 @@ class CreateRelatedAgendaItemView(SingleObjectMixin, RedirectView):
self.item = Item.objects.create(content_object=self.object)
class FixAgendaView(QuestionView):
permission_required = 'agenda.can_manage_agenda'
question_url_name = 'item_overview'
url_name = 'item_overview'
question_message = ugettext_lazy('Do you really want to fix the agenda numbering?')
url_name_args = []
def get(self, request, *args, **kwargs):
self.items = Item.objects.all()
return super(FixAgendaView, self).get(request, *args, **kwargs)
def on_clicked_yes(self):
config['agenda_agenda_fixed'] = True
for item in self.items:
item.item_number = item.calc_item_no()
item.save()
def get_final_message(self):
return ugettext_lazy('The agenda has been fixed.')
class ResetAgendaView(QuestionView):
permission_required = 'agenda.can_manage_agenda'
question_url_name = 'item_overview'
url_name = 'item_overview'
question_message = ugettext_lazy('Do you really want to reset the agenda numbering?')
url_name_args = []
def get(self, request, *args, **kwargs):
self.items = Item.objects.all()
return super(ResetAgendaView, self).get(request, *args, **kwargs)
def on_clicked_yes(self):
config['agenda_agenda_fixed'] = False
for item in self.items:
item.item_number = ''
item.save()
def get_final_message(self):
return ugettext_lazy('The agenda has been reset.')
class AgendaPDF(PDFView):
"""
Create a full agenda-PDF.

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import difflib
import roman
from django.contrib.auth.models import Permission
from django.shortcuts import render_to_response
@ -71,3 +72,14 @@ def int_or_none(var):
return int(var)
except (TypeError, ValueError):
return None
def toRoman(number):
"""
Converts an arabic number within range from 1 to 4999 to the corresponding roman number.
Returns None on error conditions.
"""
try:
return roman.toRoman(number)
except (roman.NotIntegerError, roman.OutOfRangeError):
return None

View File

@ -7,6 +7,7 @@ django-mptt>=0.6,<0.7
jsonfield>=0.9,<0.10
natsort>=3.1,<3.2
reportlab>=2.7,<2.8
roman>=2.0,<2.1
setuptools>=2.1,<3.5
sockjs-tornado>=1.0,<1.1
tornado>=3.1,<3.3