subcategory.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # -*- coding: utf-8 -*-
  2. """
  3. @Author: Alistair Magee
  4. Adds support for subcategories on pelican articles
  5. """
  6. import os
  7. from collections import defaultdict
  8. from operator import attrgetter
  9. from functools import partial
  10. from pelican import signals
  11. from pelican.urlwrappers import URLWrapper, Category
  12. from pelican.utils import (slugify, python_2_unicode_compatible)
  13. from six import text_type
  14. class SubCategory(URLWrapper):
  15. def __init__(self, name, parent, settings):
  16. super(SubCategory, self).__init__(name, settings)
  17. self.parent = parent
  18. self.shortname = name.split('/')
  19. self.shortname = self.shortname.pop()
  20. self.slug = slugify(self.shortname, settings.get('SLUG_SUBSTITUIONS', ()))
  21. if isinstance(self.parent, SubCategory):
  22. self.savepath = os.path.join(self.parent.savepath, self.slug)
  23. self.fullurl = '{}/{}'.format(self.parent.fullurl, self.slug)
  24. else: #parent is a category
  25. self.savepath = os.path.join(self.parent.slug, self.slug)
  26. self.fullurl = '{}/{}'.format(self.parent.slug, self.slug)
  27. def as_dict(self):
  28. d = self.__dict__
  29. d['shortname'] = self.shortname
  30. d['savepath'] = self.savepath
  31. d['fullurl'] = self.fullurl
  32. d['parent'] = self.parent
  33. return d
  34. def __hash__(self):
  35. return hash(self.fullurl)
  36. def _key(self):
  37. return self.fullurl
  38. def get_subcategories(generator, metadata):
  39. if 'SUBCATEGORY_SAVE_AS' not in generator.settings:
  40. generator.settings['SUBCATEGORY_SAVE_AS'] = os.path.join(
  41. 'subcategory', '{savepath}.html')
  42. if 'SUBCATEGORY_URL' not in generator.settings:
  43. generator.settings['SUBCATEGORY_URL'] = 'subcategory/{fullurl}.html'
  44. if 'subcategory_path' in metadata:
  45. category_list = text_type(metadata.get('subcategory_path')).split('/')
  46. else:
  47. category_list = text_type(metadata.get('category')).split('/')
  48. category = (category_list.pop(0)).strip()
  49. category = Category(category, generator.settings)
  50. metadata['category'] = category
  51. #generate a list of subcategories with their parents
  52. sub_list = []
  53. parent = category.name
  54. for subcategory in category_list:
  55. subcategory = parent + '/' + subcategory.strip()
  56. sub_list.append(subcategory)
  57. parent = subcategory
  58. metadata['subcategories'] = sub_list
  59. def create_subcategories(generator):
  60. generator.subcategories = []
  61. for article in generator.articles:
  62. parent = article.category
  63. actual_subcategories = []
  64. for subcategory in article.subcategories:
  65. #following line returns a list of items, tuples in this case
  66. sub_cat = [item for item in generator.subcategories
  67. if item[0].name == subcategory]
  68. if sub_cat:
  69. sub_cat[0][1].append(article)
  70. parent = sub_cat[0][0]
  71. actual_subcategories.append(parent)
  72. else:
  73. new_sub = SubCategory(subcategory, parent, generator.settings)
  74. generator.subcategories.append((new_sub, [article,]))
  75. parent = new_sub
  76. actual_subcategories.append(parent)
  77. article.subcategories = actual_subcategories
  78. """Add subpath and suburl to the article metadata. This allows the
  79. the last subcategory's fullurl and savepath to be used when definining
  80. Article URL's. If an article has no subcategories, the Category slug
  81. is used instead
  82. """
  83. try:
  84. last_subcat = article.subcategories[-1]
  85. article.metadata['subpath'] = last_subcat.savepath
  86. article.metadata['suburl'] = last_subcat.fullurl
  87. except IndexError: #No Subcategory
  88. article.metadata['subpath'] = article.category.slug
  89. article.metadata['suburl'] = article.category.slug
  90. def generate_subcategories(generator, writer):
  91. write = partial(writer.write_file,
  92. relative_urls=generator.settings['RELATIVE_URLS'])
  93. subcategory_template = generator.get_template('subcategory')
  94. for subcat, articles in generator.subcategories:
  95. articles.sort(key=attrgetter('date'), reverse=True)
  96. dates = [article for article in generator.dates if article in articles]
  97. write(subcat.save_as, subcategory_template, generator.context,
  98. subcategory=subcat, articles=articles, dates=dates,
  99. paginated={'articles': articles, 'dates': dates},
  100. page_name=subcat.page_name, all_articles=generator.articles)
  101. def generate_subcategory_feeds(generator, writer):
  102. for subcat, articles in generator.subcategories:
  103. articles.sort(key=attrgetter('date'), reverse=True)
  104. if generator.settings.get('SUBCATEGORY_FEED_ATOM'):
  105. writer.write_feed(articles, generator.context,
  106. generator.settings['SUBCATEGORY_FEED_ATOM']
  107. % subcat.fullurl)
  108. if generator.settings.get('SUBCATEGORY_FEED_RSS'):
  109. writer.write_feed(articles, generator.context,
  110. generator.settings['SUBCATEGORY_FEED_RSS']
  111. % subcat.fullurl, feed_type='rss')
  112. def generate(generator, writer):
  113. generate_subcategory_feeds(generator, writer)
  114. generate_subcategories(generator, writer)
  115. def register():
  116. signals.article_generator_context.connect(get_subcategories)
  117. signals.article_generator_finalized.connect(create_subcategories)
  118. signals.article_writer_finalized.connect(generate)