#!/usr/bin/env python import argparse import os import io import sys import xml.etree.ElementTree as ET from io import StringIO from datetime import datetime import markdown import yaml from bs4 import BeautifulSoup from jinja2 import Environment, FunctionLoader DEVMODE_WEBSOCKET_ADDR = "ws://localhost:8888/ws" def _set_dates(entries): for entry in entries: if "date" in entry: entry["date"] = datetime.fromisoformat(entry["date"]) if "others" in entry: return _set_dates(entry["others"]) def _get_dates(entries): dates = [] for entry in entries: if "date" in entry: dates.append(entry["date"]) if "others" in entry: for other in _get_dates(entry["others"]): dates.append(other) return dates def _get_latest(entries): latest = None for other in _get_dates(entries): if latest is None: latest = other else: if other > latest: latest = other return latest def _load_sitemap(path, current_path): by_name = dict() flattened = [] with open(path, "r") as fp: _sitemap = yaml.safe_load(fp.read()) if not _sitemap["enabled"]: return None variables = dict() if "variables" in _sitemap: variables = _sitemap["variables"] def _filter_entries(entries): entries = list( filter( lambda entry: "enabled" not in entry or entry["enabled"] is True, entries, ) ) for entry in entries: if "others" in entry: entry["others"] = _filter_entries(entry["others"]) return entries entries = _filter_entries(_sitemap["entries"]) def _link(entries, parent=None): for entry in entries: flattened.append(entry) entry["parent"] = parent if "others" in entry: _link(entry["others"], parent=entry) def _make_url(link): path = [link["name"]] parent = link["parent"] while parent is not None: path.append(parent["name"]) parent = parent["parent"] path.reverse() return "/".join(path) _link(entries, parent=None) for entry in flattened: target = "/" + _make_url(entry) if target == current_path: entry["active"] = True else: entry["active"] = False entry["url"] = target by_name[target] = entry _set_dates(entries) latest = _get_latest(entries) return dict(by_name=by_name, variables=variables, links=entries, latest=_get_latest(entries)) def _make_tree(links): def _populate(root, links): for link in links: elm = ET.Element("li") if "directory" in link and link["directory"]: elm.text = link["name"] else: lref = ET.Element("a", href=link["url"]) link_text = link["name"] if "date" in link: link_text = link_text + " [" + str(link["date"].strftime("%Y-%m-%d")) + "]" lref.text = link_text if link["active"]: lref.text = lref.text + " <" elm.append(lref) if "others" in link: others = link["others"] ul = ET.Element("ul") elm.append(ul) _populate(root=ul, links=others) root.append(elm) params = {"class": "tree"} root = ET.Element("ul", **params) _populate(root=root, links=links) return ET.tostring(root).decode() def load_content(path): with open(path) as fp: text = fp.read() content = markdown.markdown( text, extensions=["fenced_code", "tables"], ) return content def generate(template, content_path, stylesheet, link, sitemap, devmode=False): tree = _make_tree(sitemap["links"]) with open(template, "r") as fp: template = fp.read() env = Environment(loader=FunctionLoader(lambda name: template)) base = env.get_template(template) with open(stylesheet) as fp: style = fp.read() content = load_content(content_path) env_content = Environment(loader=FunctionLoader(lambda name: content)) out = base.render( { "content": env_content.get_template("").render( {"sitemap": sitemap, "tree": tree, "variables": sitemap["variables"]} ), "variables": sitemap["variables"], "is_index": False, "link": link, "style": style, "sitemap": sitemap, "tree": tree, "devmode": devmode, "devmode_websocket_addr": DEVMODE_WEBSOCKET_ADDR, "current_time": datetime.now(), } ) sys.stdout.write(out) def generate_rss(sitemap): sitemap = _load_sitemap(sitemap, "/") feed = ET.Element("feed", attrib={"xmlns": "http://www.w3.org/2005/Atom"}) feed_id = ET.SubElement(feed, "id") feed_id.text = "https://kevinschoon.com/" title = ET.SubElement(feed, "title") title.text = "Blog of Kevin Schoon" link = ET.SubElement(feed, "link") link.attrib["rel"] = "self" link.attrib["href"] = "https://kevinschoon.com" updated = ET.SubElement(feed, "updated") updated.text = sitemap["latest"].isoformat() author = ET.SubElement(feed, "author") name = ET.SubElement(author, "name") name.text = "Kevin Schoon" for entry in sitemap["by_name"]["/blog"]["others"]: absolute_url = "https://kevinschoon.com/blog/" + entry["name"] atom_entry = ET.SubElement(feed, "entry") link = ET.SubElement(atom_entry, "link") link.attrib["href"] = absolute_url link.attrib["rel"] = "self" _id = ET.SubElement(atom_entry, "id") _id.text = absolute_url title = ET.SubElement(atom_entry, "title") title.text = entry["tagline"] updated = ET.SubElement(atom_entry, "updated") updated.text = entry["date"].isoformat() content_path = "content/blog/" + entry["name"] + "/README.md" content = load_content(content_path) content_element = ET.SubElement(atom_entry, "content") content_element.attrib["type"] = "xhtml" content_element.attrib["xml:lang"] = "en" content_tree = ET.parse(io.StringIO(f"