website/do_blog.py

123 lines
2.8 KiB
Python
Executable file

#!/usr/bin/env python3
import sys
import re
from glob import glob
from ipo import read, write, map, dictmap, starstarmap, join, sort, ipo
from functools import partial
from markdown import markdown
import strictyaml
md_to_html = join("\n") | ipo(markdown)
yaml_to_dict = join("\n") | ipo(strictyaml.load) | ipo(lambda x: x.data)
@ipo
def parted(data, regex):
"""
Two parts of data, delimited by the first element on which regex fullmatches.
If a delimiter is found, the second part is an iterator over the remaining elements. Else it is None.
"""
first = []
it = iter(data)
try:
while True:
item = next(it)
if re.fullmatch(regex, item):
break
first.append(item)
except StopIteration:
return (first, None)
return (first, it)
LIST_ITEM_TEMPLATE = """
<li><a href="{path}">
<div class="title">{title}</div>
<div class="summary">{summary}</div>
</a></li>"""
def blog_page_template():
with open("blog.html") as fh:
return fh.read()
# FIXME what a mess
def blog_page(file, read_body=True):
"""
Keep the file open if you want to be able to read the body.
Good:
>>> with open(…) as file:
>>> metadata, body = blog_page(file)
>>> [smthng(line) for line in body]
>>> print("\n".join(hello))
Bad:
>>> with open(…) as file:
>>> metadata, body = blog_page(file)
>>> hello = (smthng(line) for line in body) # Lazy generator, the body wasn't read from file yet
>>> print("\n".join(hello))
"""
if isinstance(file, str):
assert not read_body, "Can't read body when giving a filename, need a file for that."
filename = file
file = open(filename)
else:
filename = None
try:
metadata_yaml, body_md = read(file) | parted("---+")
metadata = {
**(metadata_yaml | yaml_to_dict),
}
if not read_body:
metadata["path"] = re.sub(r".md$", "", filename)
body = (
body_md | md_to_html(extensions=["abbr", "toc", "smarty", "fenced_code", "codehilite"])
if read_body else None
)
return (metadata, body)
finally:
if filename:
file.close()
def safe_metadata(metadata):
return {
k: (
v
if k in ("published", "path")
else markdown(v, extensions=["smarty"]).replace("<p>", "").replace("</p>", "")
)
for k, v in metadata.items()
}
if sys.argv[1] == "--index":
blog_post_list = (
glob("blog/*.md") |
map(lambda filename: {
**blog_page(filename, read_body=False)[0],
}) |
sort(key=lambda x: x["published"]) |
map(safe_metadata) |
map(lambda metadata: LIST_ITEM_TEMPLATE.format(**metadata)) |
join("")
)
with open(sys.argv[2]) as file_in, open(sys.argv[3], "w") as file_out:
file_in.read().format(blog_posts=blog_post_list) | write(file=file_out)
else:
with open(sys.argv[1]) as file_in, open(sys.argv[2], "w") as file_out:
metadata, body = blog_page(file_in)
blog_page_template().format(**safe_metadata(metadata), body=body) | write(file=file_out)