import os import re import glob import html import email import logging import datetime from multiprocessing import Pool from string import Template import markdown from markdown.extensions.toc import TocExtension destpath_re = re.compile(r"\.md$") logging.basicConfig(encoding="utf-8", level=logging.INFO) cpu_count = os.cpu_count() def convert(text): md = markdown.Markdown(extensions=["extra", "meta", TocExtension(anchorlink=True)]) res = md.convert(text) return res, md.Meta def render_post(fpath): destpath = destpath_re.sub(".html", fpath) logging.info("opening %s for parsing, dest %s", fpath, destpath) # from: https://python-markdown.github.io/reference/ with open(fpath, "r", encoding="utf-8") as input_file: logging.info("reading %s", fpath) text = input_file.read() logging.info("parsing %s", fpath) out, meta = convert(text) title = meta.get("title")[0] date = meta.get("date")[0] draft = False if meta.get("draft"): draft = True title_out, _ = convert("# " + title) out = title_out + out logging.info("writing to %s", destpath) render_template( "index.html.tmpl", destpath, {"content": out, "more_title": " - " + title} ) return { "title": title, "date": date, "fpath": fpath, "destpath": destpath, "draft": draft, } def render_posts(): files = glob.glob("posts/*.md") logging.info("found post files %s", files) posts = [] logging.info("starting render posts with cpu_count: %d", cpu_count) with Pool(processes=cpu_count) as pool: posts = pool.map(render_post, files) logging.info("render_posts result: %s", posts) return posts def posts_list_html(posts): post_tpl = """
  • {title}
  • """ out = '" def render_template(tpl_fname, out_fname, subs): with open(tpl_fname, "r", encoding="utf-8") as inf: tmpl = Template(inf.read()) out = tmpl.substitute(subs) out_fname = os.path.join("public/", out_fname) with open(out_fname, "w", encoding="utf-8") as outf: outf.write(out) def render_index(posts): content_html = posts_list_html(posts) render_template( "index.html.tmpl", "index.html", {"content": content_html, "more_title": ""} ) def rss_post_xml(post): tpl = """ {title} {link} {pubdate} {link} {description} """ link = "https://cfebs.com/" + post["destpath"] with open(post["fpath"], "r", encoding="utf-8") as inf: text = inf.read() converted, _ = convert(text) pubdate = email.utils.format_datetime(datetime.datetime.fromisoformat(post["date"])) subs = { "title": post["title"], "link": link, "pubdate": pubdate, "description": converted, } for k, v in subs.items(): subs[k] = html.escape(v) return tpl.format(**subs) def render_rss_index(posts): items = "" for post in posts[:5]: items += rss_post_xml(post) subs = { "site_title": "cfebs.com", "site_link": "https://cfebs.com", "self_full_link": "https://cfebs.com/index.xml", "description": "Recent content from cfebs.com", "last_build_date": email.utils.format_datetime(datetime.datetime.now()), } for k, v in subs.items(): subs[k] = html.escape(v) subs["items"] = items render_template("index.xml.tmpl", "index.xml", subs) def main(): os.makedirs("public/posts/", exist_ok=True) posts = render_posts() logging.info("rendered posts: %s", posts) posts = filter(lambda p: not p["draft"], posts) sorted_posts = sorted( posts, key=lambda p: datetime.datetime.fromisoformat(p["date"]), reverse=True ) render_index(sorted_posts) render_rss_index(sorted_posts) if __name__ == "__main__": main()