|
@@ -0,0 +1,203 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+
|
|
|
+from dataclasses import dataclass
|
|
|
+import datetime
|
|
|
+import os
|
|
|
+import markdown
|
|
|
+import pystache
|
|
|
+import shutil
|
|
|
+import yaml
|
|
|
+
|
|
|
+class Datum:
|
|
|
+ @classmethod
|
|
|
+ def from_yaml(cls, data):
|
|
|
+ return cls(**data)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def from_file(cls, path):
|
|
|
+ with open(path) as f:
|
|
|
+ data = yaml.safe_load(f)
|
|
|
+ return cls.from_yaml(data)
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class Quote(Datum):
|
|
|
+ id: str
|
|
|
+ content: str
|
|
|
+ author: str
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class Quip(Datum):
|
|
|
+ id: str
|
|
|
+ content: str
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class Work:
|
|
|
+ slug: str
|
|
|
+ category: str
|
|
|
+ title: str
|
|
|
+ date: str
|
|
|
+ contents: str
|
|
|
+
|
|
|
+class Path:
|
|
|
+ DATADIR=os.getenv('DATADIR', '/home/gdritter/projects/lib-data')
|
|
|
+ OUTDIR=os.getenv('OUTDIR', '/tmp/lib')
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def data(cls, *paths):
|
|
|
+ return os.path.join(cls.DATADIR, *paths)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def out(cls, *paths):
|
|
|
+ return os.path.join(cls.OUTDIR, *paths)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def write(cls, *paths):
|
|
|
+ if len(paths) > 1:
|
|
|
+ os.makedirs(cls.out(*paths[:-1]), exist_ok=True)
|
|
|
+ return open(cls.out(*paths), 'w')
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def read(cls, *paths):
|
|
|
+ with open(cls.data(*paths)) as f:
|
|
|
+ return f.read()
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def list(cls, *paths):
|
|
|
+ return os.listdir(cls.data(*paths))
|
|
|
+
|
|
|
+class Template:
|
|
|
+ renderer = pystache.Renderer(search_dirs="templates")
|
|
|
+
|
|
|
+ def load_template(name):
|
|
|
+ with open(f"templates/{name}.mustache") as f:
|
|
|
+ parsed = pystache.parse(f.read())
|
|
|
+ return lambda stuff: Template.renderer.render(parsed, stuff)
|
|
|
+
|
|
|
+ main = load_template("main")
|
|
|
+ quote = load_template("quote")
|
|
|
+ list = load_template("list")
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ year = datetime.datetime.now().year
|
|
|
+ std_copy = f'© Getty Ritter {year}'
|
|
|
+ no_copy = 'all rights reversed'
|
|
|
+
|
|
|
+ # gather the quips and make their individual pages
|
|
|
+ quips = []
|
|
|
+ for uuid in Path.list('quips'):
|
|
|
+ q = Quip.from_file(Path.data('quips', uuid))
|
|
|
+ quips.append(q)
|
|
|
+ with Path.write('quips', uuid, 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': f"Quip {uuid}",
|
|
|
+ 'contents': Template.quote({'quotelist': [q]}),
|
|
|
+ 'copy': no_copy,
|
|
|
+ }))
|
|
|
+
|
|
|
+ # sort 'em and make the combined page
|
|
|
+ quips.sort(key=lambda q: q.id)
|
|
|
+ with Path.write('quips', 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': "Quips",
|
|
|
+ 'contents': Template.quote({'quotelist': quips}),
|
|
|
+ 'copy': no_copy,
|
|
|
+ }))
|
|
|
+
|
|
|
+ # gather the quotes and make their individual pages
|
|
|
+ quotes = []
|
|
|
+ for uuid in Path.list('quotes'):
|
|
|
+ q = Quote.from_file(Path.data('quotes', uuid))
|
|
|
+ quotes.append(q)
|
|
|
+ with Path.write('quotes', uuid, 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': f"Quote {uuid}",
|
|
|
+ 'contents': Template.quote({'quotelist': [q]}),
|
|
|
+ 'copy': no_copy,
|
|
|
+ }))
|
|
|
+
|
|
|
+ # sort 'em and make their combined page
|
|
|
+ quotes.sort(key=lambda q: q.id)
|
|
|
+ with Path.write('quotes', 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': "Quotes",
|
|
|
+ 'contents': Template.quote({'quotelist': quotes}),
|
|
|
+ 'copy': no_copy,
|
|
|
+ }))
|
|
|
+
|
|
|
+ # figure out what categories we've got
|
|
|
+ with open(Path.data('works.json')) as f:
|
|
|
+ categories = yaml.safe_load(f)
|
|
|
+
|
|
|
+ # make an index page for each category
|
|
|
+ with Path.write('category', 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': 'Categories',
|
|
|
+ 'contents': Template.list({
|
|
|
+ 'works': [
|
|
|
+ {'slug': f'category/{c["slug"]}', 'title': c['category']}
|
|
|
+ for c in categories
|
|
|
+ ]
|
|
|
+ }),
|
|
|
+ 'copy': 'whatever',
|
|
|
+ }))
|
|
|
+
|
|
|
+ # create each category page
|
|
|
+ for slug in os.listdir(Path.data('works')):
|
|
|
+ # we need to know what works exist in the category
|
|
|
+ works = []
|
|
|
+ for work in os.listdir(Path.data('works', slug)):
|
|
|
+ # grab the metadata for this work
|
|
|
+ with open(Path.data('works', slug, work, 'metadata.yaml')) as f:
|
|
|
+ meta = yaml.safe_load(f)
|
|
|
+ with open(Path.data('works', slug, work, 'text')) as f:
|
|
|
+ text = markdown.markdown(f.read())
|
|
|
+ w = Work(
|
|
|
+ slug=meta.get('slug', work),
|
|
|
+ category=meta.get('category', slug),
|
|
|
+ title=meta['name'],
|
|
|
+ date=meta['date'],
|
|
|
+ contents=text,
|
|
|
+ )
|
|
|
+
|
|
|
+ if slug == 'pages':
|
|
|
+ # always keep index/about up-to-date
|
|
|
+ copy = std_copy
|
|
|
+ else:
|
|
|
+ # report other works in their own year
|
|
|
+ copy = f'© Getty Ritter {w.date}'
|
|
|
+
|
|
|
+ with Path.write(w.slug, 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': w.title,
|
|
|
+ 'contents': text,
|
|
|
+ 'copy': copy,
|
|
|
+ }))
|
|
|
+ works.append(w)
|
|
|
+ works.sort(key=lambda w: w.slug)
|
|
|
+
|
|
|
+ # not every on-disk category should be shown: we should find
|
|
|
+ # it in the categories list first
|
|
|
+ category_metadata = [c for c in categories if c['slug'] == slug]
|
|
|
+ if not category_metadata:
|
|
|
+ print(f'skipping {slug}')
|
|
|
+ continue
|
|
|
+
|
|
|
+ with Path.write('category', slug, 'index.html') as f:
|
|
|
+ f.write(Template.main({
|
|
|
+ 'title': category_metadata[0]['category'],
|
|
|
+ 'contents': Template.list({
|
|
|
+ 'works': works,
|
|
|
+ }),
|
|
|
+ 'copy': std_copy,
|
|
|
+ }))
|
|
|
+
|
|
|
+ shutil.copy(Path.out('index', 'index.html'), Path.out('index.html'))
|
|
|
+
|
|
|
+ os.makedirs(Path.out('static'), exist_ok=True)
|
|
|
+ shutil.copy('static/main.css', Path.out('static', 'main.css'))
|
|
|
+ shutil.copy('static/icon.png', Path.out('static', 'icon.png'))
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ main()
|