From 2c5b3a8e4f3406082cc47380ccbb79f4a1444497 Mon Sep 17 00:00:00 2001 From: Stefan Frauenknecht Date: Sun, 27 Apr 2014 21:01:23 +0200 Subject: [PATCH 1/4] Automatic numbering of agenda items --- openslides/agenda/forms.py | 5 +- openslides/agenda/models.py | 43 +++++++++++++++++ openslides/agenda/signals.py | 37 +++++++++++++- .../agenda/templates/agenda/overview.html | 6 +++ openslides/agenda/urls.py | 8 ++++ openslides/agenda/views.py | 48 +++++++++++++++++++ openslides/utils/utils.py | 12 +++++ requirements_production.txt | 1 + 8 files changed, 156 insertions(+), 4 deletions(-) diff --git a/openslides/agenda/forms.py b/openslides/agenda/forms.py index 035e0ad3f..288c32713 100644 --- a/openslides/agenda/forms.py +++ b/openslides/agenda/forms.py @@ -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): diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index ffc90430f..348ef9944 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -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): diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index 854a6cfee..b14c69ddd 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -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}) diff --git a/openslides/agenda/templates/agenda/overview.html b/openslides/agenda/templates/agenda/overview.html index c67fb0309..148c7910a 100644 --- a/openslides/agenda/templates/agenda/overview.html +++ b/openslides/agenda/templates/agenda/overview.html @@ -70,6 +70,12 @@ {% else %} {% trans 'Set start time of event' %} {% endif %} + {% if perms.agenda.can_manage_agenda and agenda_enable_auto_numbering %} + {% trans 'Fix numbering' %} + {% trans 'Reset numbering' %} + {% endif %} {% endif %} diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py index 0e854f1c6..c311f8aad 100644 --- a/openslides/agenda/urls.py +++ b/openslides/agenda/urls.py @@ -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\d+)/speaker/$', views.SpeakerAppendView.as_view(), diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 6f41917e6..0ba12c83b 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -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. diff --git a/openslides/utils/utils.py b/openslides/utils/utils.py index 832dbc0d7..66f8044bc 100644 --- a/openslides/utils/utils.py +++ b/openslides/utils/utils.py @@ -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 diff --git a/requirements_production.txt b/requirements_production.txt index 2745c22dd..3e2480a56 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -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 From 5254cc83a6b005e1afe7259832640c928a6af40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Mon, 28 Apr 2014 01:03:10 +0200 Subject: [PATCH 2/4] Updated numbering feature. Fixed organizational item structuring. Prevented organizational items from having agenda items as descendents. Some coding style changes. Added CHANGELOG and README entries. --- CHANGELOG | 2 ++ README.rst | 2 ++ openslides/agenda/forms.py | 4 ++-- openslides/agenda/models.py | 44 ++++++++++++++++++++++++------------ openslides/agenda/signals.py | 11 ++++----- openslides/agenda/views.py | 27 +++++++++------------- tests/agenda/tests.py | 40 ++++++++++++++++++++++++++++++++ 7 files changed, 91 insertions(+), 39 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e387a527a..11b2ed524 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,8 @@ Version 1.6 (unreleased) Agenda: - New projector view with the current list of speakers. - Added CSV import. +- Added automatic numbering of agenda items. +- Fixed organizational item structuring. Assignment: - Coupled assignment candidates with list of speakers. Dashboard: diff --git a/README.rst b/README.rst index b54c3446d..105cfc79a 100644 --- a/README.rst +++ b/README.rst @@ -272,6 +272,8 @@ OpenSlides uses the following projects or parts of them: * `ReportLab `_, License: BSD +* `roman `_, License: Python 2.1.1 + * `sockjs-client `_, License: MIT diff --git a/openslides/agenda/forms.py b/openslides/agenda/forms.py index 288c32713..839cfeed6 100644 --- a/openslides/agenda/forms.py +++ b/openslides/agenda/forms.py @@ -31,7 +31,7 @@ class ItemForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm): class Meta: model = Item - exclude = ('closed', 'weight', 'content_type', 'object_id', 'item_number') + fields = ('item_number', 'title', 'text', 'comment', 'type', 'duration', 'parent', 'speaker_list_closed') class RelatedItemForm(ItemForm): @@ -40,7 +40,7 @@ class RelatedItemForm(ItemForm): """ class Meta: model = Item - exclude = ('closed', 'type', 'weight', 'content_type', 'object_id', 'title', 'text', 'item_number') + fields = ('comment', 'duration', 'parent', 'speaker_list_closed') class ItemOrderForm(CssClassMixin, forms.Form): diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index 348ef9944..b6857e0c0 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -5,6 +5,7 @@ from datetime import datetime from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.db import models from django.utils.translation import ugettext as _ @@ -37,16 +38,16 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): (AGENDA_ITEM, ugettext_lazy('Agenda item')), (ORGANIZATIONAL_ITEM, ugettext_lazy('Organizational item'))) + item_number = models.CharField(blank=True, max_length=255, verbose_name=ugettext_lazy("Number")) + """ + Number of agenda item. + """ + title = models.CharField(null=True, max_length=255, verbose_name=ugettext_lazy("Title")) """ 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. @@ -122,6 +123,16 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): if self.parent and self.parent.is_active_slide(): update_projector() + def clean(self): + """ + Ensures that the children of orga items are only orga items. + """ + if self.type == self.AGENDA_ITEM and self.parent is not None and self.parent.type == self.ORGANIZATIONAL_ITEM: + raise ValidationError(_('Agenda items can not be descendents of an organizational item.')) + if self.type == self.ORGANIZATIONAL_ITEM and self.get_descendants().filter(type=self.AGENDA_ITEM).exists(): + raise ValidationError(_('Organizational items can not have agenda items as descendents.')) + return super(Item, self).clean() + def __unicode__(self): return self.get_title() @@ -155,7 +166,7 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): 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 '%s %s' % (item_no, self.title) if item_no else self.title return self.title try: return self.content_object.get_agenda_title() @@ -296,32 +307,35 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): @property def item_no(self): if config['agenda_agenda_fixed']: - item_no = self.item_number + item_number = self.item_number else: - item_no = self.calc_item_no() - if item_no: - return '%s %s' % (config['agenda_number_prefix'], item_no) + item_number = self.calc_item_no() + if item_number: + item_no = '%s %s' % (config['agenda_number_prefix'], item_number) + else: + item_no = None + return item_no def calc_item_no(self): """ - Returns the number of this agenda item + Returns the number of this agenda item. """ if self.type == self.AGENDA_ITEM: if self.is_root_node(): - if config['agenda_numeral_system'] == 'a': + if config['agenda_numeral_system'] == 'arabic': return str(self._calc_sibling_no()) - else: + else: # config['agenda_numeral_system'] == 'roman' 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 + 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: + while prev_sibling is not None: if prev_sibling.type == self.AGENDA_ITEM: sibling_no += 1 prev_sibling = prev_sibling.get_previous_sibling() diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index b14c69ddd..d5c14cabb 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -77,19 +77,18 @@ def setup_agenda_config(sender, **kwargs): agenda_numeral_system = ConfigVariable( name='agenda_numeral_system', - default_value='a', + default_value='arabic', form_field=forms.ChoiceField( - label=ugettext_lazy('Numeral System for Top items'), + label=ugettext_lazy('Numeral system for agenda items'), widget=forms.Select(), choices=( - ('a', ugettext_lazy('Arabic')), - ('r', ugettext_lazy('Roman'))), + ('arabic', ugettext_lazy('Arabic')), + ('roman', ugettext_lazy('Roman'))), required=False)) agenda_agenda_fixed = ConfigVariable( name='agenda_agenda_fixed', - default_value=False, - form_field=None) + default_value=False) extra_stylefiles = ['css/jquery-ui-timepicker.css'] extra_javascript = ['js/jquery/jquery-ui-timepicker-addon.min.js', diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 0ba12c83b..8a708bcec 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -109,14 +109,11 @@ 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, + 'agenda_enable_auto_numbering': config['agenda_enable_auto_numbering'], + 'agenda_numbering_fixed': config['agenda_agenda_fixed'], 'duration': duration, 'start': start, 'end': end, @@ -140,8 +137,14 @@ class Overview(TemplateView): parent = Item.objects.get(id=form.cleaned_data['parent']) except Item.DoesNotExist: parent = None - item.weight = form.cleaned_data['weight'] + else: + if item.type == item.AGENDA_ITEM and parent.type == item.ORGANIZATIONAL_ITEM: + transaction.rollback() + messages.error( + request, _('Agenda items can not be descendents of an organizational item.')) + break item.parent = parent + item.weight = form.cleaned_data['weight'] Model.save(item) else: transaction.rollback() @@ -349,13 +352,9 @@ class FixAgendaView(QuestionView): 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: + for item in Item.objects.all(): item.item_number = item.calc_item_no() item.save() @@ -370,13 +369,9 @@ class ResetAgendaView(QuestionView): 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: + for item in Item.objects.all(): item.item_number = '' item.save() diff --git a/tests/agenda/tests.py b/tests/agenda/tests.py index 48c868a28..cec6a6351 100644 --- a/tests/agenda/tests.py +++ b/tests/agenda/tests.py @@ -229,6 +229,20 @@ class ViewTest(TestCase): self.assertIsNone(Item.objects.get(pk=1).parent) self.assertEqual(Item.objects.get(pk=2).parent_id, 1) + def test_change_item_order_with_orga_item(self): + self.item1.type = 2 + self.item1.save() + data = { + 'i1-self': 1, + 'i1-weight': 50, + 'i1-parent': 0, + 'i2-self': 2, + 'i2-weight': 50, + 'i2-parent': 1} + response = self.adminClient.post('/agenda/', data) + self.assertNotEqual(Item.objects.get(pk=2).parent_id, 1) + self.assertContains(response, 'Agenda items can not be descendents of an organizational item.') + def test_delete(self): response = self.adminClient.get('/agenda/%s/del/' % self.item1.pk) self.assertRedirects(response, '/agenda/') @@ -276,6 +290,32 @@ class ViewTest(TestCase): response = client.get('/agenda/2/') self.assertEqual(response.status_code, 200) + def test_orga_item_with_orga_parent_one(self): + item1 = Item.objects.create(title='item1_Taeboog1de1sahSeiM8y', type=2) + response = self.adminClient.post( + '/agenda/new/', + {'title': 'item2_faelohD2uK7ohNgeepi2', + 'type': '1', + 'parent': item1.pk}) + self.assertFormError( + response, + 'form', + None, + 'Agenda items can not be descendents of an organizational item.') + + def test_orga_item_with_orga_parent_two(self): + item1 = Item.objects.create(title='item1_aeNg4Heibee8ULooneep') + Item.objects.create(title='item2_fooshaeroo7Ohvoow0hoo', parent=item1) + response = self.adminClient.post( + '/agenda/%s/edit/' % item1.pk, + {'title': 'item1_aeNg4Heibee8ULooneep_changed', + 'type': '2'}) + self.assertFormError( + response, + 'form', + None, + 'Organizational items can not have agenda items as descendents.') + def test_csv_import(self): item_number = Item.objects.all().count() new_csv_file = SimpleUploadedFile( From f8bd4b0d86e2b3c255c9f3d717d06714df18a32a Mon Sep 17 00:00:00 2001 From: Stefan Frauenknecht Date: Sat, 3 May 2014 19:38:12 +0200 Subject: [PATCH 3/4] Numbering of agenda items, part 2. Config item agenda_agenda_fixed removed'. Config items agenda_agenda_fixed and agenda_enable_auto_numbering removed. FixAgendaView and ResetAgendaView removed. AgendaNumberingView added. Old two fix- and reset numbering buttons replaced by 'Number agenda items'. --- openslides/agenda/models.py | 14 +++------- openslides/agenda/signals.py | 15 +---------- .../agenda/templates/agenda/overview.html | 8 +++--- openslides/agenda/urls.py | 10 +++---- openslides/agenda/views.py | 26 +++---------------- 5 files changed, 14 insertions(+), 59 deletions(-) diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index b6857e0c0..088afed44 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -164,10 +164,8 @@ 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 '%s %s' % (item_no, self.title) if item_no else self.title - return self.title + item_no = self.item_no + return '%s %s' % (item_no, self.title) if item_no else self.title try: return self.content_object.get_agenda_title() except AttributeError: @@ -306,12 +304,8 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): @property def item_no(self): - if config['agenda_agenda_fixed']: - item_number = self.item_number - else: - item_number = self.calc_item_no() - if item_number: - item_no = '%s %s' % (config['agenda_number_prefix'], item_number) + if self.item_number: + item_no = '%s %s' % (config['agenda_number_prefix'], self.item_number) else: item_no = None return item_no diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index d5c14cabb..aa818ccf1 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -60,13 +60,6 @@ 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='', @@ -86,10 +79,6 @@ def setup_agenda_config(sender, **kwargs): ('roman', ugettext_lazy('Roman'))), required=False)) - agenda_agenda_fixed = ConfigVariable( - name='agenda_agenda_fixed', - default_value=False) - extra_stylefiles = ['css/jquery-ui-timepicker.css'] extra_javascript = ['js/jquery/jquery-ui-timepicker-addon.min.js', 'js/jquery/jquery-ui-sliderAccess.min.js', @@ -102,10 +91,8 @@ def setup_agenda_config(sender, **kwargs): variables=(agenda_start_event_date_time, agenda_show_last_speakers, agenda_couple_countdown_and_speakers, - agenda_enable_auto_numbering, agenda_number_prefix, - agenda_numeral_system, - agenda_agenda_fixed), + agenda_numeral_system), extra_context={'extra_stylefiles': extra_stylefiles, 'extra_javascript': extra_javascript}) diff --git a/openslides/agenda/templates/agenda/overview.html b/openslides/agenda/templates/agenda/overview.html index 148c7910a..6b96db6db 100644 --- a/openslides/agenda/templates/agenda/overview.html +++ b/openslides/agenda/templates/agenda/overview.html @@ -70,11 +70,9 @@ {% else %} {% trans 'Set start time of event' %} {% endif %} - {% if perms.agenda.can_manage_agenda and agenda_enable_auto_numbering %} - {% trans 'Fix numbering' %} - {% trans 'Reset numbering' %} + {% if perms.agenda.can_manage_agenda %} + {% trans 'Number agenda items' %} {% endif %} {% endif %} diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py index c311f8aad..218e3148f 100644 --- a/openslides/agenda/urls.py +++ b/openslides/agenda/urls.py @@ -40,13 +40,9 @@ 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'), + url(r'^numbering/$', + views.AgendaNumberingView.as_view(), + name='agenda_numbering'), # List of speakers url(r'^(?P\d+)/speaker/$', diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 8a708bcec..140838981 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -112,8 +112,6 @@ class Overview(TemplateView): context.update({ 'items': items, 'agenda_is_active': agenda_is_active, - 'agenda_enable_auto_numbering': config['agenda_enable_auto_numbering'], - 'agenda_numbering_fixed': config['agenda_agenda_fixed'], 'duration': duration, 'start': start, 'end': end, @@ -345,38 +343,20 @@ class CreateRelatedAgendaItemView(SingleObjectMixin, RedirectView): self.item = Item.objects.create(content_object=self.object) -class FixAgendaView(QuestionView): +class AgendaNumberingView(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?') + question_message = ugettext_lazy('Do you really want to generate agenda numbering? Manually added item numbers will be overwritten!') url_name_args = [] def on_clicked_yes(self): - config['agenda_agenda_fixed'] = True for item in Item.objects.all(): 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 on_clicked_yes(self): - config['agenda_agenda_fixed'] = False - for item in Item.objects.all(): - item.item_number = '' - item.save() - - def get_final_message(self): - return ugettext_lazy('The agenda has been reset.') + return ugettext_lazy('The agenda has been numbered.') class AgendaPDF(PDFView): From d638d5b7e265a3052c4a2f1580f4080ab2323c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Sun, 4 May 2014 13:41:55 +0200 Subject: [PATCH 4/4] Fixed bug of numbering view when there are orga items. Renamed toRoman to to_roman. --- openslides/agenda/models.py | 6 ++++-- openslides/utils/utils.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index 088afed44..a8bfb6b94 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -20,7 +20,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 +from openslides.utils.utils import to_roman class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): @@ -319,9 +319,11 @@ class Item(SlideMixin, AbsoluteUrlMixin, MPTTModel): if config['agenda_numeral_system'] == 'arabic': return str(self._calc_sibling_no()) else: # config['agenda_numeral_system'] == 'roman' - return toRoman(self._calc_sibling_no()) + return to_roman(self._calc_sibling_no()) else: return '%s.%s' % (self.parent.calc_item_no(), self._calc_sibling_no()) + else: + return '' def _calc_sibling_no(self): """ diff --git a/openslides/utils/utils.py b/openslides/utils/utils.py index 66f8044bc..d509c7a41 100644 --- a/openslides/utils/utils.py +++ b/openslides/utils/utils.py @@ -74,7 +74,7 @@ def int_or_none(var): return None -def toRoman(number): +def to_roman(number): """ Converts an arabic number within range from 1 to 4999 to the corresponding roman number. Returns None on error conditions.