blog/main.py
2024-11-22 15:39:52 -05:00

173 lines
3.9 KiB
Python

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 = """<li>
<a href="{href}">{title}</a>
<time datetime="{date}">{disp_date}</time>
</li>"""
out = '<ul class="blog-posts-list">'
for post in posts:
disp_date = datetime.datetime.fromisoformat(post.get("date")).strftime(
"%Y-%m-%d"
)
out += post_tpl.format(
href=post.get("destpath"),
title=post.get("title"),
date=post.get("date"),
disp_date=disp_date,
)
return out + "</ul>"
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 = """
<item>
<title>{title}</title>
<link>{link}</link>
<pubDate>{pubdate}</pubDate>
<guid>{link}</guid>
<description>{description}</description>
</item>
"""
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()