summaryrefslogtreecommitdiffstats
path: root/generate.py
diff options
context:
space:
mode:
Diffstat (limited to 'generate.py')
-rwxr-xr-xgenerate.py178
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)