/hyde/ext/plugins/grouper.py

https://github.com/spidaboy7/hyde · Python · 217 lines · 118 code · 19 blank · 80 comment · 33 complexity · 7c66aa56215995b52965ec942dbf543e MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. """
  3. Contains classes and utilities related to grouping
  4. resources and nodes in hyde.
  5. """
  6. import re
  7. from hyde.model import Expando
  8. from hyde.plugin import Plugin
  9. from hyde.site import Node, Resource
  10. from hyde.util import add_method, add_property, pairwalk
  11. from collections import namedtuple
  12. from functools import partial
  13. from itertools import ifilter, izip, tee, product
  14. from operator import attrgetter
  15. Grouper = namedtuple('Grouper', 'group resources')
  16. class Group(Expando):
  17. """
  18. A wrapper class for groups. Adds methods for
  19. grouping resources.
  20. """
  21. def __init__(self, grouping, parent=None):
  22. self.name = 'groups'
  23. self.parent = parent
  24. self.root = self
  25. self.root = parent.root if parent else self
  26. self.groups = []
  27. self.sorter = getattr(grouping, 'sorter', None)
  28. if hasattr(parent, 'sorter'):
  29. self.sorter = parent.sorter
  30. super(Group, self).__init__(grouping)
  31. add_method(Node,
  32. 'walk_%s_groups' % self.name,
  33. Group.walk_groups_in_node,
  34. group=self)
  35. add_method(Node,
  36. 'walk_resources_grouped_by_%s' % self.name,
  37. Group.walk_resources,
  38. group=self)
  39. add_property(Resource,
  40. '%s_group' % self.name,
  41. Group.get_resource_group,
  42. group=self)
  43. add_method(Resource,
  44. 'walk_%s_groups' % self.name,
  45. Group.walk_resource_groups,
  46. group=self)
  47. def set_expando(self, key, value):
  48. """
  49. If the key is groups, creates group objects instead of
  50. regular expando objects.
  51. """
  52. if key == "groups":
  53. self.groups = [Group(group, parent=self) for group in value]
  54. else:
  55. return super(Group, self).set_expando(key, value)
  56. @staticmethod
  57. def get_resource_group(resource, group):
  58. """
  59. This method gets attached to the resource object.
  60. Returns group and its ancestors that the resource
  61. belongs to, in that order.
  62. """
  63. try:
  64. group_name = getattr(resource.meta, group.root.name)
  65. except AttributeError:
  66. group_name = None
  67. return next((g for g in group.walk_groups()
  68. if g.name == group_name), None) \
  69. if group_name \
  70. else None
  71. @staticmethod
  72. def walk_resource_groups(resource, group):
  73. """
  74. This method gets attached to the resource object.
  75. Returns group and its ancestors that the resource
  76. belongs to, in that order.
  77. """
  78. try:
  79. group_name = getattr(resource.meta, group.root.name)
  80. except AttributeError:
  81. group_name = None
  82. if group_name:
  83. for g in group.walk_groups():
  84. if g.name == group_name:
  85. return reversed(list(g.walk_hierarchy()))
  86. return []
  87. @staticmethod
  88. def walk_resources(node, group):
  89. """
  90. The method that gets attached to the node
  91. object for walking the resources in the node
  92. that belong to this group.
  93. """
  94. for group in group.walk_groups():
  95. for resource in group.walk_resources_in_node(node):
  96. yield resource
  97. @staticmethod
  98. def walk_groups_in_node(node, group):
  99. """
  100. The method that gets attached to the node
  101. object for walking the groups in the node.
  102. """
  103. walker = group.walk_groups()
  104. for g in walker:
  105. lister = g.walk_resources_in_node(node)
  106. yield Grouper(group=g, resources=lister)
  107. def walk_hierarchy(self):
  108. """
  109. Walks the group hierarchy starting from
  110. this group.
  111. """
  112. g = self
  113. yield g
  114. while g.parent:
  115. yield g.parent
  116. g = g.parent
  117. def walk_groups(self):
  118. """
  119. Walks the groups in the current group
  120. """
  121. yield self
  122. for group in self.groups:
  123. for child in group.walk_groups():
  124. yield child
  125. def walk_resources_in_node(self, node):
  126. """
  127. Walks the resources in the given node
  128. sorted based on sorter configuration in this
  129. group.
  130. """
  131. walker = 'walk_resources'
  132. if hasattr(self, 'sorter') and self.sorter:
  133. walker = 'walk_resources_sorted_by_' + self.sorter
  134. walker = getattr(node, walker, getattr(node, 'walk_resources'))
  135. for resource in walker():
  136. try:
  137. group_value = getattr(resource.meta, self.root.name)
  138. except AttributeError:
  139. continue
  140. if group_value == self.name:
  141. yield resource
  142. class GrouperPlugin(Plugin):
  143. """
  144. Grouper plugin for hyde. Adds the ability to do
  145. group resources and nodes in an arbitrary
  146. hierarchy.
  147. Configuration example
  148. ---------------------
  149. #yaml
  150. sorter:
  151. kind:
  152. atts: source.kind
  153. grouper:
  154. hyde:
  155. # Categorizes the nodes and resources
  156. # based on the groups specified here.
  157. # The node and resource should be tagged
  158. # with the categories in their metadata
  159. sorter: kind # A reference to the sorter
  160. description: Articles about hyde
  161. groups:
  162. -
  163. name: announcements
  164. description: Hyde release announcements
  165. -
  166. name: making of
  167. description: Articles about hyde design decisions
  168. -
  169. name: tips and tricks
  170. description: >
  171. Helpful snippets and tweaks to
  172. make hyde more awesome.
  173. """
  174. def __init__(self, site):
  175. super(GrouperPlugin, self).__init__(site)
  176. def begin_site(self):
  177. """
  178. Initialize plugin. Add the specified groups to the
  179. site context variable.
  180. """
  181. config = self.site.config
  182. if not hasattr(config, 'grouper'):
  183. return
  184. if not hasattr(self.site, 'grouper'):
  185. self.site.grouper = {}
  186. for name, grouping in self.site.config.grouper.__dict__.items():
  187. grouping.name = name
  188. prev_att = 'prev_in_%s' % name
  189. next_att = 'next_in_%s' % name
  190. setattr(Resource, prev_att, None)
  191. setattr(Resource, next_att, None)
  192. self.site.grouper[name] = Group(grouping)
  193. walker = Group.walk_resources(
  194. self.site.content, self.site.grouper[name])
  195. for prev, next in pairwalk(walker):
  196. setattr(next, prev_att, prev)
  197. setattr(prev, next_att, next)