1
0
Fork 0
forked from github/pelican
pelican-theme/pelican/generator.py
2010-08-17 15:04:12 +02:00

156 lines
5.1 KiB
Python

# -*- coding: utf-8 -*-
import os
import re
from codecs import open
from datetime import datetime
from docutils import core
from functools import partial
from jinja2 import Environment, FileSystemLoader
import rstdirectives # import the directives to have pygments support
_TEMPLATES = ('index', 'tag', 'tags', 'article', 'category', 'categories',
'archives')
_DIRECT_TEMPLATES = ('index', 'tags', 'categories', 'archives')
_DEFAULT_TEMPLATE_PATH =\
os.sep.join([os.path.dirname(os.path.abspath(__file__)), "templates"])
def generate_output(files, templates_path=None, output_path=None, markup=None):
"""Given a list of files, a template and a destination,
output the static files.
:param files: the list of files to parse
:param templates_path: where to search for templates
:param output_path: where to output the generated files
:param markup: the markup language to use while parsing
"""
if not templates_path:
templates_path = _DEFAULT_TEMPLATE_PATH
if not output_path:
output_path = './output'
output_path = os.path.realpath(output_path)
articles, months, years, tags, categories = [], {}, {}, {}, {}
# for each file, get the informations.
for f in files:
f = os.path.abspath(f)
article = Article(open(f, encoding='utf-8').read(), markup)
articles.append(article)
if hasattr(article, 'date'):
update_dict(months, article.date.month, article)
update_dict(years, article.date.year, article)
if hasattr(article, 'tags'):
for tag in article.tags:
update_dict(tags, tag, article)
if hasattr(article, 'category'):
update_dict(categories, article.category, article)
templates = get_templates(templates_path)
context = {}
for item in ('articles', 'months', 'years', 'tags', 'categories'):
context[item] = locals()[item]
generate = partial(generate_file, output_path)
for template in _DIRECT_TEMPLATES:
generate(template, templates[template], context)
for tag in tags:
generate('tag/%s' % tag, templates['tag'], context, tag=tag)
for cat in categories:
generate('category/%s' % cat, templates['category'], context,
category=cat)
for article in articles:
generate('%s' % article.url,
templates['article'], context, article=article)
def generate_file(path, name, template, context, **kwargs):
context.update(kwargs)
output = template.render(context)
filename = os.sep.join((path, '%s.html' % name))
try:
os.makedirs(os.path.dirname(filename))
except Exception:
pass
with open(filename, 'w', encoding='utf-8') as f:
f.write(output)
print filename
def get_templates(path=None):
env = Environment(loader=FileSystemLoader(path))
templates = {}
for template in _TEMPLATES:
templates[template] = env.get_template('%s.html' % template)
return templates
_METADATA = re.compile('.. ([a-z]+): (.*)', re.M)
_METADATAS_FIELDS = {'tags': lambda x: x.split(', '),
'date': lambda x: datetime.strptime(x, '%Y/%m/%d %H:%M'),
'category': lambda x: x}
def update_dict(mapping, key, value):
if key not in mapping:
mapping[key] = []
mapping[key].append(value)
def parse_metadatas(string):
"""Return a dict, containing a list of metadatas informations, found
whithin the given string.
:param string: the string to search the metadata in
"""
output = {}
for m in _METADATA.finditer(string):
name = m.group(1)
value = m.group(2)
if name in _METADATAS_FIELDS:
output[name] = _METADATAS_FIELDS[name](value)
return output
def slugify(value):
"""
Normalizes string, converts to lowercase, removes non-alpha characters,
and converts spaces to hyphens.
Took from django sources.
"""
import unicodedata
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
return re.sub('[-\s]+', '-', value)
class Article(object):
"""Represents an article.
Given a string, complete it's properties from here.
:param string: the string to parse, containing the original content.
:param markup: the markup language to use while parsing.
"""
def __init__(self, string, markup=None):
if markup == None:
markup = 'rest'
for key, value in parse_metadatas(string).items():
setattr(self, key, value)
if markup == 'rest':
extra_params = {'input_encoding': 'unicode',
'initial_header_level': '2'}
rendered_content = core.publish_parts(string, writer_name='html',
settings_overrides=extra_params)
self.title = rendered_content.get('title')
self.content = rendered_content.get('body')
@property
def url(self):
return slugify(self.title)
def __repr__(self):
return '<%s "%s">' % (self.__class__.__name__, self.title)