mptt.utils: 60 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/mptt/utils.py

Stats: 0 executed, 56 missed, 4 excluded, 122 ignored

  1. """
  2. Utilities for working with lists of model instances which represent
  3. trees.
  4. """
  5. import copy
  6. import csv
  7. import itertools
  8. import sys
  9. __all__ = ('previous_current_next', 'tree_item_iterator',
  10. 'drilldown_tree_for_node')
  11. def previous_current_next(items):
  12. """
  13. From http://www.wordaligned.org/articles/zippy-triples-served-with-python
  14. Creates an iterator which returns (previous, current, next) triples,
  15. with ``None`` filling in when there is no previous or next
  16. available.
  17. """
  18. extend = itertools.chain([None], items, [None])
  19. previous, current, next = itertools.tee(extend, 3)
  20. try:
  21. current.next()
  22. next.next()
  23. next.next()
  24. except StopIteration:
  25. pass
  26. return itertools.izip(previous, current, next)
  27. def tree_item_iterator(items, ancestors=False):
  28. """
  29. Given a list of tree items, iterates over the list, generating
  30. two-tuples of the current tree item and a ``dict`` containing
  31. information about the tree structure around the item, with the
  32. following keys:
  33. ``'new_level'``
  34. ``True`` if the current item is the start of a new level in
  35. the tree, ``False`` otherwise.
  36. ``'closed_levels'``
  37. A list of levels which end after the current item. This will
  38. be an empty list if the next item is at the same level as the
  39. current item.
  40. If ``ancestors`` is ``True``, the following key will also be
  41. available:
  42. ``'ancestors'``
  43. A list of unicode representations of the ancestors of the
  44. current node, in descending order (root node first, immediate
  45. parent last).
  46. For example: given the sample tree below, the contents of the
  47. list which would be available under the ``'ancestors'`` key
  48. are given on the right::
  49. Books -> []
  50. Sci-fi -> [u'Books']
  51. Dystopian Futures -> [u'Books', u'Sci-fi']
  52. """
  53. structure = {}
  54. opts = None
  55. first_item_level = 0
  56. for previous, current, next in previous_current_next(items):
  57. if opts is None:
  58. opts = current._mptt_meta
  59. current_level = getattr(current, opts.level_attr)
  60. if previous:
  61. structure['new_level'] = (getattr(previous,
  62. opts.level_attr) < current_level)
  63. if ancestors:
  64. # If the previous node was the end of any number of
  65. # levels, remove the appropriate number of ancestors
  66. # from the list.
  67. if structure['closed_levels']:
  68. structure['ancestors'] = \
  69. structure['ancestors'][:-len(structure['closed_levels'])]
  70. # If the current node is the start of a new level, add its
  71. # parent to the ancestors list.
  72. if structure['new_level']:
  73. structure['ancestors'].append(unicode(previous))
  74. else:
  75. structure['new_level'] = True
  76. if ancestors:
  77. # Set up the ancestors list on the first item
  78. structure['ancestors'] = []
  79. first_item_level = current_level
  80. if next:
  81. structure['closed_levels'] = range(current_level,
  82. getattr(next,
  83. opts.level_attr), -1)
  84. else:
  85. # All remaining levels need to be closed
  86. structure['closed_levels'] = range(current_level, first_item_level - 1, -1)
  87. # Return a deep copy of the structure dict so this function can
  88. # be used in situations where the iterator is consumed
  89. # immediately.
  90. yield current, copy.deepcopy(structure)
  91. def drilldown_tree_for_node(node, rel_cls=None, rel_field=None, count_attr=None,
  92. cumulative=False):
  93. """
  94. Creates a drilldown tree for the given node. A drilldown tree
  95. consists of a node's ancestors, itself and its immediate children,
  96. all in tree order.
  97. Optional arguments may be given to specify a ``Model`` class which
  98. is related to the node's class, for the purpose of adding related
  99. item counts to the node's children:
  100. ``rel_cls``
  101. A ``Model`` class which has a relation to the node's class.
  102. ``rel_field``
  103. The name of the field in ``rel_cls`` which holds the relation
  104. to the node's class.
  105. ``count_attr``
  106. The name of an attribute which should be added to each child in
  107. the drilldown tree, containing a count of how many instances
  108. of ``rel_cls`` are related through ``rel_field``.
  109. ``cumulative``
  110. If ``True``, the count will be for each child and all of its
  111. descendants, otherwise it will be for each child itself.
  112. """
  113. if rel_cls and rel_field and count_attr:
  114. children = node._tree_manager.add_related_count(
  115. node.get_children(), rel_cls, rel_field, count_attr, cumulative)
  116. else:
  117. children = node.get_children()
  118. return itertools.chain(node.get_ancestors(), [node], children)
  119. def print_debug_info(qs):
  120. """
  121. Given an mptt queryset, prints some debug information to stdout.
  122. Use this when things go wrong.
  123. Please include the output from this method when filing bug issues.
  124. """
  125. opts = qs.model._mptt_meta
  126. writer = csv.writer(sys.stdout)
  127. header = (
  128. 'pk',
  129. opts.level_attr,
  130. '%s_id' % opts.parent_attr,
  131. opts.tree_id_attr,
  132. opts.left_attr,
  133. opts.right_attr,
  134. 'pretty',
  135. )
  136. writer.writerow(header)
  137. for n in qs.order_by('tree_id', 'lft'):
  138. level = getattr(n, opts.level_attr)
  139. row = []
  140. for field in header[:-1]:
  141. row.append(getattr(n, field))
  142. row.append('%s%s' % ('- ' * level, unicode(n).encode('utf-8')))
  143. writer.writerow(row)
  144. # NOTE we don't support django 1.1 anymore, so this stuff is likely to get removed soon
  145. def _exists(qs):
  146. """
  147. For Django 1.1 compatibility. (QuerySet.exists() was added in 1.2)
  148. This is not part of the supported mptt API, it may be removed without warning.
  149. """
  150. if hasattr(qs, 'exists'):
  151. return qs.exists()
  152. else:
  153. qs = qs.extra(select={'_exists_check': '1'}).values_list('_exists_check', flat=True)[:1]
  154. return bool(len(qs))