PageRenderTime 66ms CodeModel.GetById 29ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 1ms

/hyde/ext/plugins/grouper.py

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