subcategory.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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.strip()
  56. subcategory = parent + '/' + subcategory
  57. sub_list.append(subcategory)
  58. parent = subcategory
  59. metadata['subcategories'] = sub_list
  60. def create_subcategories(generator):
  61. generator.subcategories = []
  62. for article in generator.articles:
  63. parent = article.category
  64. actual_subcategories = []
  65. for subcategory in article.subcategories:
  66. #following line returns a list of items, tuples in this case
  67. sub_cat = [item for item in generator.subcategories
  68. if item[0].name == subcategory]
  69. if sub_cat:
  70. sub_cat[0][1].append(article)
  71. parent = sub_cat[0][0]
  72. actual_subcategories.append(parent)
  73. else:
  74. new_sub = SubCategory(subcategory, parent, generator.settings)
  75. generator.subcategories.append((new_sub, [article,]))
  76. parent = new_sub
  77. actual_subcategories.append(parent)
  78. article.subcategories = actual_subcategories
  79. """Add subpath and suburl to the article metadata. This allows the
  80. the last subcategory's fullurl and savepath to be used when definining
  81. Article URL's. If an article has no subcategories, the Category slug
  82. is used instead
  83. """
  84. try:
  85. last_subcat = article.subcategories[-1]
  86. article.metadata['subpath'] = last_subcat.savepath
  87. article.metadata['suburl'] = last_subcat.fullurl
  88. except IndexError: #No Subcategory
  89. article.metadata['subpath'] = article.category.slug
  90. article.metadata['suburl'] = article.category.slug
  91. def generate_subcategories(generator, writer):
  92. write = partial(writer.write_file,
  93. relative_urls=generator.settings['RELATIVE_URLS'])
  94. subcategory_template = generator.get_template('subcategory')
  95. for subcat, articles in generator.subcategories:
  96. articles.sort(key=attrgetter('date'), reverse=True)
  97. dates = [article for article in generator.dates if article in articles]
  98. write(subcat.save_as, subcategory_template, generator.context,
  99. subcategory=subcat, articles=articles, dates=dates,
  100. paginated={'articles': articles, 'dates': dates},
  101. page_name=subcat.page_name, all_articles=generator.articles)
  102. def generate_subcategory_feeds(generator, writer):
  103. for subcat, articles in generator.subcategories:
  104. articles.sort(key=attrgetter('date'), reverse=True)
  105. if generator.settings.get('SUBCATEGORY_FEED_ATOM'):
  106. writer.write_feed(articles, generator.context,
  107. generator.settings['SUBCATEGORY_FEED_ATOM']
  108. % subcat.fullurl)
  109. if generator.settings.get('SUBCATEGORY_FEED_RSS'):
  110. writer.write_feed(articles, generator.context,
  111. generator.settings['SUBCATEGORY_FEED_RSS']
  112. % subcat.fullurl, feed_type='rss')
  113. def generate(generator, writer):
  114. generate_subcategory_feeds(generator, writer)
  115. generate_subcategories(generator, writer)
  116. def register():
  117. signals.article_generator_context.connect(get_subcategories)
  118. signals.article_generator_finalized.connect(create_subcategories)
  119. signals.article_writer_finalized.connect(generate)