Automatic numbering of agenda items
This commit is contained in:
parent
943ede2e81
commit
2c5b3a8e4f
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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})
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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(),
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user