Merge pull request #1896 from normanjaeckel/AgendaNumbering

Fixed agenda numbering. Fixed #1892.
This commit is contained in:
Norman Jäckel 2016-01-27 10:39:12 +01:00
commit 741cae028c
2 changed files with 52 additions and 7 deletions

View File

@ -21,13 +21,10 @@ class ItemManager(models.Manager):
Customized model manager with special methods for agenda tree and Customized model manager with special methods for agenda tree and
numbering. numbering.
""" """
def get_only_agenda_items(self, queryset=None): def get_only_agenda_items(self):
""" """
Generator, which yields only agenda items. Skips hidden items. Generator, which yields only agenda items. Skips hidden items.
""" """
if queryset is None:
queryset = self.all()
# Do not execute item.is_hidden() because this would create a lot of db queries # Do not execute item.is_hidden() because this would create a lot of db queries
root_items, item_children = self.get_root_and_children(only_agenda_items=True) root_items, item_children = self.get_root_and_children(only_agenda_items=True)
@ -38,6 +35,29 @@ class ItemManager(models.Manager):
for item in items: for item in items:
yield item yield item
yield from yield_items(item_children[item.pk]) yield from yield_items(item_children[item.pk])
yield from yield_items(root_items)
def get_only_hidden_items(self):
"""
Generator, which yields only hidden items, that means only items
which type is HIDDEN_ITEM or which are children of hidden items.
"""
# Do not execute item.is_hidden() because this would create a lot of db queries
root_items, item_children = self.get_root_and_children(only_agenda_items=False)
def yield_items(items, parent_is_hidden=False):
"""
Generator that yields a list of items and their children.
"""
for item in items:
if parent_is_hidden or item.type == item.HIDDEN_ITEM:
item_is_hidden = True
yield item
else:
item_is_hidden = False
yield from yield_items(item_children[item.pk], parent_is_hidden=item_is_hidden)
yield from yield_items(root_items) yield from yield_items(root_items)
def get_root_and_children(self, only_agenda_items=False): def get_root_and_children(self, only_agenda_items=False):
@ -45,7 +65,8 @@ class ItemManager(models.Manager):
Returns a list with all root items and a dictonary where the key is an Returns a list with all root items and a dictonary where the key is an
item pk and the value is a list with all children of the item. item pk and the value is a list with all children of the item.
If only_agenda_items is True, the tree hides HIDDEN_ITEM. If only_agenda_items is True, the tree hides items with type
HIDDEN_ITEM and all of their children.
""" """
queryset = self.order_by('weight') queryset = self.order_by('weight')
item_children = defaultdict(list) item_children = defaultdict(list)
@ -57,7 +78,6 @@ class ItemManager(models.Manager):
item_children[item.parent_id].append(item) item_children[item.parent_id].append(item)
else: else:
root_items.append(item) root_items.append(item)
return root_items, item_children return root_items, item_children
def get_tree(self, only_agenda_items=False, include_content=False): def get_tree(self, only_agenda_items=False, include_content=False):
@ -66,7 +86,8 @@ class ItemManager(models.Manager):
and children, where id is the id of one agenda item and children is a and children, where id is the id of one agenda item and children is a
generator that yields dictonaries like the one discribed. generator that yields dictonaries like the one discribed.
If only_agenda_items is True, the tree hides HIDDEN_ITEM. If only_agenda_items is True, the tree hides items with type
HIDDEN_ITEM and all of their children.
If include_content is True, the yielded dictonaries have no key 'id' If include_content is True, the yielded dictonaries have no key 'id'
but a key 'item' with the entire object. but a key 'item' with the entire object.
@ -133,22 +154,31 @@ class ItemManager(models.Manager):
""" """
def walk_tree(tree, number=None): def walk_tree(tree, number=None):
for index, tree_element in enumerate(tree): for index, tree_element in enumerate(tree):
# Calculate number of visable agenda items.
if numeral_system == 'roman' and number is None: if numeral_system == 'roman' and number is None:
item_number = to_roman(index + 1) item_number = to_roman(index + 1)
else: else:
item_number = str(index + 1) item_number = str(index + 1)
if number is not None: if number is not None:
item_number = '.'.join((number, item_number)) item_number = '.'.join((number, item_number))
# Add prefix.
if config['agenda_number_prefix']: if config['agenda_number_prefix']:
item_number_tmp = "%s %s" % (config['agenda_number_prefix'], item_number) item_number_tmp = "%s %s" % (config['agenda_number_prefix'], item_number)
else: else:
item_number_tmp = item_number item_number_tmp = item_number
# Save the new value and go down the tree.
tree_element['item'].item_number = item_number_tmp tree_element['item'].item_number = item_number_tmp
tree_element['item'].save() tree_element['item'].save()
walk_tree(tree_element['children'], item_number) walk_tree(tree_element['children'], item_number)
# Start numbering visable agenda items.
walk_tree(self.get_tree(only_agenda_items=True, include_content=True)) walk_tree(self.get_tree(only_agenda_items=True, include_content=True))
# Reset number of hidden items.
for item in self.get_only_hidden_items():
item.item_number = ''
item.save()
class Item(RESTModelMixin, models.Model): class Item(RESTModelMixin, models.Model):
""" """

View File

@ -288,3 +288,18 @@ class Numbering(TestCase):
self.assertEqual(Item.objects.get(pk=self.item_2.pk).item_number, '') self.assertEqual(Item.objects.get(pk=self.item_2.pk).item_number, '')
self.assertEqual(Item.objects.get(pk=self.item_2_1.pk).item_number, '') self.assertEqual(Item.objects.get(pk=self.item_2_1.pk).item_number, '')
self.assertEqual(Item.objects.get(pk=self.item_3.pk).item_number, '2') self.assertEqual(Item.objects.get(pk=self.item_3.pk).item_number, '2')
def test_reset_numbering_with_hidden_item(self):
self.item_2.item_number = 'test_number_Cieghae6ied5ool4hiem'
self.item_2.type = Item.HIDDEN_ITEM
self.item_2.save()
self.item_2_1.item_number = 'test_number_roQueTohg7fe1Is7aemu'
self.item_2_1.save()
response = self.client.post(reverse('item-numbering'))
self.assertEqual(response.status_code, 200)
self.assertEqual(Item.objects.get(pk=self.item_1.pk).item_number, '1')
self.assertEqual(Item.objects.get(pk=self.item_2.pk).item_number, '')
self.assertEqual(Item.objects.get(pk=self.item_2_1.pk).item_number, '')
self.assertEqual(Item.objects.get(pk=self.item_3.pk).item_number, '2')