122 lines
2.8 KiB
Python
Executable file
122 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("templates/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)
|