# -*- coding: utf-8 -*- """ @Author: Alistair Magee Adds support for subcategories on pelican articles """ import os from collections import defaultdict from operator import attrgetter from functools import partial from pelican import signals from pelican.urlwrappers import URLWrapper, Category from pelican.utils import (slugify, python_2_unicode_compatible) from six import text_type class SubCategory(URLWrapper): def __init__(self, name, parent, settings): super(SubCategory, self).__init__(name, settings) self.parent = parent self.shortname = name.split('/') self.shortname = self.shortname.pop() self.slug = slugify(self.shortname, settings.get('SLUG_SUBSTITUIONS', ())) if isinstance(self.parent, SubCategory): self.savepath = os.path.join(self.parent.savepath, self.slug) self.fullurl = '{}/{}'.format(self.parent.fullurl, self.slug) else: #parent is a category self.savepath = os.path.join(self.parent.slug, self.slug) self.fullurl = '{}/{}'.format(self.parent.slug, self.slug) def as_dict(self): d = self.__dict__ d['shortname'] = self.shortname d['savepath'] = self.savepath d['fullurl'] = self.fullurl d['parent'] = self.parent return d def __hash__(self): return hash(self.fullurl) def _key(self): return self.fullurl def get_subcategories(generator, metadata): if 'SUBCATEGORY_SAVE_AS' not in generator.settings: generator.settings['SUBCATEGORY_SAVE_AS'] = os.path.join( 'subcategory', '{savepath}.html') if 'SUBCATEGORY_URL' not in generator.settings: generator.settings['SUBCATEGORY_URL'] = 'subcategory/{fullurl}.html' if 'subcategory_path' in metadata: category_list = text_type(metadata.get('subcategory_path')).split('/') else: category_list = text_type(metadata.get('category')).split('/') category = (category_list.pop(0)).strip() category = Category(category, generator.settings) metadata['category'] = category #generate a list of subcategories with their parents sub_list = [] parent = category.name for subcategory in category_list: subcategory.strip() subcategory = parent + '/' + subcategory sub_list.append(subcategory) parent = subcategory metadata['subcategories'] = sub_list def create_subcategories(generator): generator.subcategories = [] for article in generator.articles: parent = article.category actual_subcategories = [] for subcategory in article.subcategories: #following line returns a list of items, tuples in this case sub_cat = [item for item in generator.subcategories if item[0].name == subcategory] if sub_cat: sub_cat[0][1].append(article) parent = sub_cat[0][0] actual_subcategories.append(parent) else: new_sub = SubCategory(subcategory, parent, generator.settings) generator.subcategories.append((new_sub, [article,])) parent = new_sub actual_subcategories.append(parent) article.subcategories = actual_subcategories """Add subpath and suburl to the article metadata. This allows the the last subcategory's fullurl and savepath to be used when definining Article URL's. If an article has no subcategories, the Category slug is used instead """ try: last_subcat = article.subcategories[-1] article.metadata['subpath'] = last_subcat.savepath article.metadata['suburl'] = last_subcat.fullurl except IndexError: #No Subcategory article.metadata['subpath'] = article.category.slug article.metadata['suburl'] = article.category.slug def generate_subcategories(generator, writer): write = partial(writer.write_file, relative_urls=generator.settings['RELATIVE_URLS']) subcategory_template = generator.get_template('subcategory') for subcat, articles in generator.subcategories: articles.sort(key=attrgetter('date'), reverse=True) dates = [article for article in generator.dates if article in articles] write(subcat.save_as, subcategory_template, generator.context, subcategory=subcat, articles=articles, dates=dates, paginated={'articles': articles, 'dates': dates}, page_name=subcat.page_name, all_articles=generator.articles) def generate_subcategory_feeds(generator, writer): for subcat, articles in generator.subcategories: articles.sort(key=attrgetter('date'), reverse=True) if generator.settings.get('SUBCATEGORY_FEED_ATOM'): writer.write_feed(articles, generator.context, generator.settings['SUBCATEGORY_FEED_ATOM'] % subcat.fullurl) if generator.settings.get('SUBCATEGORY_FEED_RSS'): writer.write_feed(articles, generator.context, generator.settings['SUBCATEGORY_FEED_RSS'] % subcat.fullurl, feed_type='rss') def generate(generator, writer): generate_subcategory_feeds(generator, writer) generate_subcategories(generator, writer) def register(): signals.article_generator_context.connect(get_subcategories) signals.article_generator_finalized.connect(create_subcategories) signals.article_writer_finalized.connect(generate)