PageRenderTime 24ms CodeModel.GetById 16ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/hyde/ext/plugins/sorter.py

http://github.com/hyde/hyde
Python | 131 lines | 69 code | 17 blank | 45 comment | 18 complexity | a4809d6860a1a23387eacdac177eb9d7 MD5 | raw file
  1# -*- coding: utf-8 -*-
  2"""
  3Contains classes and utilities related to sorting
  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, pairwalk
 11
 12from itertools import ifilter
 13from functools import partial
 14from operator import attrgetter
 15
 16def filter_method(item, settings=None):
 17    """
 18    Returns true if all the filters in the
 19    given settings evaluate to True.
 20    """
 21    all_match = True
 22    default_filters = {}
 23    filters = {}
 24    if hasattr(settings, 'filters'):
 25        filters.update(default_filters)
 26        filters.update(settings.filters.__dict__)
 27
 28    for field, value in filters.items():
 29        try:
 30            res = attrgetter(field)(item)
 31        except:
 32            res = None
 33        if res != value:
 34            all_match = False
 35            break
 36    return all_match
 37
 38def attributes_checker(item, attributes=None):
 39    """
 40    Checks if the given list of attributes exist.
 41    """
 42    try:
 43      x = attrgetter(*attributes)(item)
 44      return True
 45    except AttributeError:
 46      return False
 47
 48def sort_method(node, settings=None):
 49    """
 50    Sorts the resources in the given node based on the
 51    given settings.
 52    """
 53    attr = 'name'
 54    if settings and hasattr(settings, 'attr') and settings.attr:
 55        attr = settings.attr
 56    reverse = False
 57    if settings and hasattr(settings, 'reverse'):
 58        reverse = settings.reverse
 59    if not isinstance(attr, list):
 60        attr = [attr]
 61    filter_ = partial(filter_method, settings=settings)
 62
 63    excluder_ = partial(attributes_checker, attributes=attr)
 64
 65    resources = ifilter(lambda x: excluder_(x) and filter_(x),
 66                        node.walk_resources())
 67    return sorted(resources,
 68                    key=attrgetter(*attr),
 69                    reverse=reverse)
 70
 71
 72class SorterPlugin(Plugin):
 73    """
 74    Sorter plugin for hyde. Adds the ability to do
 75    sophisticated sorting by expanding the site objects
 76    to support prebuilt sorting methods. These methods
 77    can be used in the templates directly.
 78
 79    Configuration example
 80    ---------------------
 81    #yaml
 82
 83    sorter:
 84        kind:
 85            # Sorts by this attribute name
 86            # Uses `attrgetter` on the resource object
 87            attr: source_file.kind
 88
 89            # The filters to be used before sorting
 90            # This can be used to remove all the items
 91            # that do not apply. For example,
 92            # filtering non html content
 93            filters:
 94                source_file.kind: html
 95                is_processable: True
 96                meta.is_listable: True
 97    """
 98
 99    def __init__(self, site):
100        super(SorterPlugin, self).__init__(site)
101
102    def begin_site(self):
103        """
104        Initialize plugin. Add a sort and match method
105        for every configuration mentioned in site settings
106        """
107
108        config = self.site.config
109        if not hasattr(config, 'sorter'):
110            return
111
112        for name, settings in config.sorter.__dict__.items():
113            sort_method_name = 'walk_resources_sorted_by_%s' % name
114            self.logger.debug("Adding sort methods for [%s]" % name)
115            add_method(Node, sort_method_name, sort_method, settings=settings)
116            match_method_name = 'is_%s' % name
117            add_method(Resource, match_method_name, filter_method, settings)
118
119            prev_att = 'prev_by_%s' % name
120            next_att = 'next_by_%s' % name
121
122            setattr(Resource, prev_att, None)
123            setattr(Resource, next_att, None)
124
125            walker = getattr(self.site.content,
126                                sort_method_name,
127                                self.site.content.walk_resources)
128            for prev, next in pairwalk(walker()):
129                setattr(prev, next_att, next)
130                setattr(next, prev_att, prev)
131