/hyde/ext/plugins/sorter.py

http://github.com/hyde/hyde · Python · 131 lines · 69 code · 17 blank · 45 comment · 16 complexity · a4809d6860a1a23387eacdac177eb9d7 MD5 · raw file

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