diff options
Diffstat (limited to 'generate.py')
-rwxr-xr-x | generate.py | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/generate.py b/generate.py new file mode 100755 index 0000000..3c7f1f3 --- /dev/null +++ b/generate.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 + +from collections import namedtuple, Counter +from datetime import date +from functools import partial +from glob import glob +from itertools import chain +from jinja2 import Environment, FileSystemLoader, Markup +from markdown import markdown +from operator import attrgetter +from subprocess import call as run +from sys import argv +import meta +import os + +CONTENT = './content' +OUTPUT = './output' +KWORDS = { 'Tomasz Kramkowski', 'the-tk', 'blog', 'projects', 'TK', 'Arch-TK' } +ERRORS = { + 403: "Forbidden", + 404: "File Not Found", + 500: "Internal Server Error", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", +} + +md = lambda s: Markup(markdown(s, extensions=['markdown.extensions.fenced_code', + 'markdown.extensions.codehilite'])) + +File = namedtuple('File', 'content date slug ext') +def parse(path, has_date=False): + with open(path) as f: + content = meta.parse(f.read()) + slug, ext = os.path.splitext(os.path.split(path)[1]) + if has_date: + y, m, d, slug = slug.split('-', 3) + dat = date(int(y), int(m), int(d)) + else: + dat = None + return File(content, dat, slug, ext) + +Post = namedtuple('Post', 'title date tags pre content location') +def readpost(path): + pf = parse(path, has_date=True) + location = os.path.join('post', + '{:04}'.format(pf.date.year), + '{:02}'.format(pf.date.month), + '{:02}'.format(pf.date.day), + pf.slug + '.html') + tags = set(pf.content.meta.get('tags', [])) + return Post(' '.join(pf.content.meta['title']), pf.date, tags, + pf.content.sects['pre'], + '\n'.join(pf.content.sects.values()), location) + +Project = namedtuple('Project', 'name description source ml aur content location') +def readproject(path): + pf = parse(path) + source = pf.content.meta.get('source', [None])[0] + ml = pf.content.meta.get('ml', [None])[0] + aur = pf.content.meta.get('aur', [None])[0] + content = '\n'.join(pf.content.sects.values()) + location = os.path.join('project', pf.slug + '.html') + return Project(' '.join(pf.content.meta['name']), + pf.content.sects.get('desc', content), source, ml, aur, content, + location); + +def output(dest, cont): + dest = os.path.join(OUTPUT, dest) + destdir = os.path.dirname(dest) + if not os.path.exists(destdir): + os.makedirs(destdir) + with open(dest, 'w') as f: + f.write(cont) + +def outputpost(env, post, tags): + page = { + 'title': post.title, + 'description': md(post.pre), + 'keywords': ', '.join(KWORDS | post.tags), + 'date': post.date, + 'content': md(post.content), + 'tags': sorted(post.tags & set(tags)), + } + output(post.location, env.get_template('post.html').render(page=page)) + +def outputtag(env, posts, tag): + page = { + 'title': 'Posts tagged - {}'.format(tag), + 'description': "List of posts tagged '{}'.".format(tag), + 'keywords': ', '.join(KWORDS | {tag}), + 'tag': tag, + 'posts': [ p for p in posts if tag in p.tags ], + } + output('tag/{}.html'.format(tag), env.get_template('tag.html').render(page=page)) + +def outputproject(env, project): + page = { + 'title': project.name, + 'description': project.description.replace('\n', ' '), + 'keywords': ', '.join(KWORDS | {project.name}), + 'content': md(project.content), + 'source': project.source, + 'ml': project.ml, + 'aur': project.aur, + } + output(project.location, env.get_template('project.html').render(page=page)) + +def main(argv=['generate.py']): + from getopt import getopt + global OUTPUT + + opts = dict(getopt(argv[1:], "o:b:")[0]) + OUTPUT = opts.get('-o', OUTPUT) + baseurl = opts.get('-b', 'https://the-tk.com') + + run(['rm', '-rf', os.path.join(OUTPUT, '*')]) + + env = Environment(loader=FileSystemLoader('./templates'), autoescape=True) + env.globals['site'] = {'baseurl': baseurl} + env.filters['datefmt'] = lambda d, f='%Y-%m-%d': d.strftime(f) + + posts = glob(os.path.join(CONTENT, 'posts', '*')) + posts = [p for p in posts if not os.path.split(p)[1].startswith('_')] + posts = sorted(map(readpost, posts), key=attrgetter('date'), reverse=True) + tags = Counter(chain(*(p.tags for p in posts))) + tags = sorted(k for k, v in tags.items() if v > 1) + + page = { + 'title': 'Blog', + 'description': 'New posts will appear chronologically on this page.', + 'keywords': ', '.join(KWORDS), + 'tags': tags, + 'posts': posts, + } + output('blog.html', env.get_template('blog.html').render(page=page)) + + projects = glob(os.path.join(CONTENT, 'projects', '*')) + projects = [p for p in projects if not os.path.split(p)[1].startswith('_')] + projects = sorted(map(readproject, projects), key=lambda e: e.name.lower()) + + page = { + 'title': 'Projects', + 'description': 'This is a listing of all of my notable projects.', + 'keywords': ', '.join(KWORDS), + 'projects': projects, + } + output('projects.html', env.get_template('projects.html').render(page=page)) + + page = { + 'title': 'the-tk.com', + 'description': 'This is my website. Here you can find some blog posts and descriptions for notable projects.', + 'keywords': ', '.join(KWORDS), + 'posts': posts[:3], + } + output('index.html', env.get_template('index.html').render(page=page)) + + for post in posts: + outputpost(env, post, tags) + for tag in tags: + outputtag(env, posts, tag) + for project in projects: + outputproject(env, project) + + for code, error in ERRORS.items(): + page = { + 'title': 'Error: {} - {}'.format(code, error), + 'code': code, + 'error': error, + } + output('error/{}.html'.format(code), env.get_template('error.html').render(page=page)) + + run(['scss', os.path.join(CONTENT, 'style.scss'), os.path.join(OUTPUT, 'style.css')]) + run(['cp', '-r', os.path.join(CONTENT, 'images'), OUTPUT]) + +if __name__ == '__main__': + from sys import argv + main(argv) |