Skip to content

Commit

Permalink
Added test for mark_descendants
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Obrist committed Dec 31, 2010
1 parent b546ef4 commit 035cb53
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 65 deletions.
5 changes: 3 additions & 2 deletions cms/models/pluginmodel.py
Expand Up @@ -257,9 +257,10 @@ def delete_with_public(self):
"""
position = self.position
slot = self.placeholder.slot
if self.page and getattr(self.page, 'publisher_public'):
page = get_page_from_placeholder_if_exists(self.placeholder)
if page and getattr(page, 'publisher_public'):
try:
placeholder = Placeholder.objects.get(page=self.page.publisher_public, slot=slot)
placeholder = Placeholder.objects.get(page=page.publisher_public, slot=slot)
except Placeholder.DoesNotExist:
pass
else:
Expand Down
7 changes: 7 additions & 0 deletions cms/templatetags/cms_tags.py
Expand Up @@ -16,6 +16,7 @@
from django.utils.translation import ugettext_lazy as _
from itertools import chain
import operator
import re

register = template.Library()

Expand All @@ -35,11 +36,17 @@ def has_permission(page, request):
return page.has_change_permission(request)
register.filter(has_permission)

CLEAN_KEY_PATTERN = re.compile(r'[^a-zA-Z0-9_-]')

def _clean_key(key):
return CLEAN_KEY_PATTERN.sub('-', key)

def _get_cache_key(name, page_lookup, lang, site_id):
if isinstance(page_lookup, Page):
page_key = str(page_lookup.pk)
else:
page_key = str(page_lookup)
page_key = _clean_key(page_key)
return name+'__page_lookup:'+page_key+'_site:'+str(site_id)+'_lang:'+str(lang)

def _get_page_by_untyped_arg(page_lookup, request, site_id):
Expand Down
25 changes: 20 additions & 5 deletions cms/tests/menu.py
Expand Up @@ -6,9 +6,20 @@
from django.contrib.auth.models import User
from django.template import Template
from menus.base import NavigationNode
from menus.menu_pool import menu_pool
from menus.menu_pool import menu_pool, _build_nodes_inner_for_one_menu
from menus.utils import mark_descendants

class MenusTestCase(CMSTestCase):

def _get_nodes(self):
node1 = NavigationNode('1', '/1/', 1)
node2 = NavigationNode('2', '/2/', 2, 1)
node3 = NavigationNode('3', '/3/', 3, 2)
node4 = NavigationNode('4', '/4/', 4, 2)
node5 = NavigationNode('5', '/5/', 5)
nodes = [node1, node2, node3, node4, node5]
tree = _build_nodes_inner_for_one_menu(nodes, "test")
return tree, nodes

def setUp(self):
settings.CMS_MODERATOR = False
Expand Down Expand Up @@ -404,8 +415,7 @@ def test_20_build_nodes_inner_for_worst_case_menu(self):
nodes = [node1,node2,node3,node4,node5,]
len_nodes = len(nodes)

final_list = menu_pool._build_nodes_inner_for_one_menu(nodes,
menu_class_name)
final_list = _build_nodes_inner_for_one_menu(nodes, menu_class_name)
self.assertEqual(len(final_list), len_nodes)

self.assertEqual(node1.parent, node2)
Expand Down Expand Up @@ -450,8 +460,7 @@ def test_22_build_nodes_inner_for_broken_menu(self):
menu_class_name = 'Test'
nodes = [node1,node2,node3,node4,node5,]

final_list = menu_pool._build_nodes_inner_for_one_menu(nodes,
menu_class_name)
final_list = _build_nodes_inner_for_one_menu(nodes, menu_class_name)
self.assertEqual(len(final_list), 3)
self.assertFalse(node1 in final_list)
self.assertFalse(node2 in final_list)
Expand All @@ -467,3 +476,9 @@ def test_22_build_nodes_inner_for_broken_menu(self):
self.assertEqual(node3.children, [])
self.assertEqual(node4.children, [node3])
self.assertEqual(node5.children, [node4])

def test_23_utils_mark_descendants(self):
tree_nodes, flat_nodes = self._get_nodes()
mark_descendants(tree_nodes)
for node in flat_nodes:
self.assertTrue(node.descendant, node)
112 changes: 55 additions & 57 deletions menus/menu_pool.py
Expand Up @@ -13,6 +13,60 @@ def lex_cache_key(key):
"""
return key.rsplit('_', 2)[1:]

def _build_nodes_inner_for_one_menu(nodes, menu_class_name):
'''
This is an easier to test "inner loop" building the menu tree structure
for one menu (one language, one site)
'''
done_nodes = {} # Dict of node.id:Node
final_nodes = []

# This is to prevent infinite loops - we need to compare the number of
# times we see a specific node to "something", and for the time being,
# it's the total number of nodes
list_total_length = len(nodes)

while nodes:
# For when the node has a parent_id but we haven't seen it yet.
# We must not append it to the final list in this case!
should_add_to_final_list = True

node = nodes.pop(0)

# Increment the "seen" counter for this specific node.
node._counter = getattr(node,'_counter',0) + 1

# Implicit namespacing by menu.__name__
if not node.namespace:
node.namespace = menu_class_name
if node.namespace not in done_nodes:
# We need to create the namespace dict to avoid KeyErrors
done_nodes[node.namespace] = {}

# If we have seen the parent_id already...
if node.parent_id in done_nodes[node.namespace] :
# Implicit parent namespace by menu.__name__
if not node.parent_namespace:
node.parent_namespace = menu_class_name
parent = done_nodes[node.namespace][node.parent_id]
parent.children.append(node)
node.parent = parent
# If it has a parent_id but we haven't seen it yet...
elif node.parent_id:
# We check for infinite loops here, by comparing the number of
# times we "saw" this node to the number of nodes in the list
if node._counter < list_total_length:
nodes.append(node)
# Never add this node to the final list until it has a real
# parent (node.parent)
should_add_to_final_list = False

if should_add_to_final_list:
final_nodes.append(node)
# add it to the "seen" list
done_nodes[node.namespace][node.id] = node
return final_nodes

class MenuPool(object):
def __init__(self):
self.menus = {}
Expand Down Expand Up @@ -83,8 +137,7 @@ def _build_nodes(self, request, site_id):
for menu_class_name in self.menus:
nodes = self.menus[menu_class_name].get_nodes(request)
# nodes is a list of navigation nodes (page tree in cms + others)
final_nodes += self._build_nodes_inner_for_one_menu(nodes,
menu_class_name)
final_nodes += _build_nodes_inner_for_one_menu(nodes, menu_class_name)
duration = getattr(settings, "MENU_CACHE_DURATION", 60*60)
cache.set(key, final_nodes, duration)
# We need to have a list of the cache keys for languages and sites that
Expand All @@ -95,68 +148,13 @@ def _build_nodes(self, request, site_id):
CacheKey.objects.create(key=key, language=lang, site=site_id)
return final_nodes

def _build_nodes_inner_for_one_menu(self, nodes, menu_class_name):
'''
This is an easier to test "inner loop" building the menu tree structure
for one menu (one language, one site)
'''
done_nodes = {} # Dict of node.id:Node
final_nodes = []

# This is to prevent infinite loops - we need to compare the number of
# times we see a specific node to "something", and for the time being,
# it's the total number of nodes
list_total_length = len(nodes)

while nodes:
# For when the node has a parent_id but we haven't seen it yet.
# We must not append it to the final list in this case!
should_add_to_final_list = True

node = nodes.pop(0)

# Increment the "seen" counter for this specific node.
node._counter = getattr(node,'_counter',0) + 1

# Implicit namespacing by menu.__name__
if not node.namespace:
node.namespace = menu_class_name
if node.namespace not in done_nodes:
# We need to create the namespace dict to avoid KeyErrors
done_nodes[node.namespace] = {}

# If we have seen the parent_id already...
if node.parent_id in done_nodes[node.namespace] :
# Implicit parent namespace by menu.__name__
if not node.parent_namespace:
node.parent_namespace = menu_class_name
parent = done_nodes[node.namespace][node.parent_id]
parent.children.append(node)
node.parent = parent
# If it has a parent_id but we haven't seen it yet...
elif node.parent_id:
# We check for infinite loops here, by comparing the number of
# times we "saw" this node to the number of nodes in the list
if node._counter < list_total_length:
nodes.append(node)
# Never add this node to the final list until it has a real
# parent (node.parent)
should_add_to_final_list = False

if should_add_to_final_list:
final_nodes.append(node)
# add it to the "seen" list
done_nodes[node.namespace][node.id] = node
return final_nodes

def apply_modifiers(self, nodes, request, namespace=None, root_id=None, post_cut=False, breadcrumb=False):
if not post_cut:
nodes = self._mark_selected(request, nodes)
for cls in self.modifiers:
inst = cls()
nodes = inst.modify(request, nodes, namespace, root_id, post_cut, breadcrumb)
return nodes


def get_nodes(self, request, namespace=None, root_id=None, site_id=None, breadcrumb=False):
self.discover_menus()
Expand Down
2 changes: 1 addition & 1 deletion menus/utils.py
Expand Up @@ -5,7 +5,7 @@
def mark_descendants(nodes):
for node in nodes:
node.descendant = True
mark_descendants(node.childrens)
mark_descendants(node.children)

def make_tree(request, items, levels, url, ancestors, descendants=False, current_level=0, to_levels=100, active_levels=0):
from cms.models import Page
Expand Down

0 comments on commit 035cb53

Please sign in to comment.