157 lines
4.5 KiB
Python
157 lines
4.5 KiB
Python
import contextlib
|
|
import csv
|
|
import datetime
|
|
import hashlib
|
|
import io
|
|
import os
|
|
import re
|
|
import subprocess as SP
|
|
import sys
|
|
import tempfile
|
|
from functools import partial
|
|
|
|
import panflute as pf
|
|
from dateutil.parser import parse as dateparse
|
|
from jinja2 import Environment, PackageLoader, Template, select_autoescape
|
|
from panflute import *
|
|
|
|
|
|
def remove_pound(elem, doc):
|
|
if type(elem) == Str:
|
|
return Str(elem.text.lstrip("#"))
|
|
|
|
|
|
def fix_color(elem, doc):
|
|
if type(elem) == MetaMap:
|
|
for k in elem.content:
|
|
if k.endswith("-color"):
|
|
elem[k] = elem[k].walk(remove_pound)
|
|
|
|
|
|
def update_date(elem, doc):
|
|
if type(elem) == MetaMap:
|
|
datefmt = doc.get_metadata("datefmt", "%Y-%m-%d")
|
|
today = datetime.date.today().strftime(datefmt)
|
|
date = dateparse(doc.get_metadata("date", today)).date()
|
|
elem["date"] = MetaInlines(Str(date.strftime(datefmt)))
|
|
return elem
|
|
|
|
|
|
def csv_table(elem, doc):
|
|
if type(elem) == Para and len(elem.content) == 1 and type(elem.content[0]) == Image:
|
|
elem = elem.content[0]
|
|
ext = os.path.splitext(elem.url)[1][1:]
|
|
if ext == "csv":
|
|
caption = elem.content
|
|
has_header = elem.attributes.get("has-header", "false").lower() == "true"
|
|
with open(elem.url) as f:
|
|
reader = csv.reader(f)
|
|
body = []
|
|
for row in reader:
|
|
cells = [TableCell(Plain(Str(x))) for x in row]
|
|
body.append(TableRow(*cells))
|
|
header = body.pop(0) if has_header else None
|
|
ret = Table(*body, header=header, caption=caption)
|
|
return ret
|
|
|
|
|
|
def code_refs(elem, doc):
|
|
if type(elem) == Cite:
|
|
label = elem.content[0]
|
|
if type(label) == Str:
|
|
label = label.text
|
|
filename = re.findall(r"^\[@lst:(.*)\]$", label) or [None]
|
|
if filename[0] in doc.inc_files:
|
|
return [
|
|
RawInline(
|
|
"\\hyperref[{}]{{{}}}".format(filename[0], filename[0]),
|
|
format="tex",
|
|
)
|
|
]
|
|
|
|
|
|
def include_code(elem, doc):
|
|
if type(elem) == CodeBlock:
|
|
if "include" in elem.attributes:
|
|
filepath = elem.attributes.pop("include")
|
|
filename = os.path.split(filepath)[-1]
|
|
try:
|
|
elem.text += elem.text + open(filepath, encoding="utf-8").read()
|
|
elem.attributes["caption"] = filename
|
|
doc.inc_files.append(filename)
|
|
except Exception as e:
|
|
elem.text += "Error: {}".format(e)
|
|
return [RawBlock("\\label{{{}}}".format(filename), format="tex"), elem]
|
|
|
|
|
|
def py_eval(options, data, element, doc):
|
|
out_buffer = io.StringIO()
|
|
with contextlib.redirect_stdout(out_buffer):
|
|
exec(data, doc.pyenv)
|
|
out_buffer.seek(0)
|
|
return convert_text(out_buffer.read())
|
|
|
|
|
|
def jinja_py_filt(doc, file):
|
|
env = {}
|
|
code = open(file, encoding="utf-8").read()
|
|
exec(code, env)
|
|
return env["main"](doc)
|
|
|
|
|
|
def prepare(doc):
|
|
doc.inc_files = []
|
|
doc.env = Environment()
|
|
doc.pyenv = {}
|
|
filters = {"py": partial(jinja_py_filt, doc)}
|
|
doc.env.filters.update(filters)
|
|
|
|
|
|
def process_templates(elem, doc):
|
|
if type(elem) == CodeBlock:
|
|
if elem.classes == ["@"]:
|
|
args = {"meta": doc.get_metadata()}
|
|
return convert_text(doc.env.from_string(elem.text).render(args))
|
|
|
|
|
|
def yaml_filt(elem, doc):
|
|
tags = {"eval": py_eval}
|
|
return yaml_filter(elem, doc, tags=tags, strict_yaml=True)
|
|
|
|
|
|
def checkboxes(elem, doc):
|
|
if type(elem) in [Para, Plain]:
|
|
val = re.findall(r"^\[([xX]|\ )\] (.*)$", stringify(elem))
|
|
if val:
|
|
val = val[0][0].lower() == "x"
|
|
else:
|
|
return elem
|
|
marker = {
|
|
True: RawInline("$\\boxtimes$", format="latex"),
|
|
False: RawInline("$\\square$", format="latex"),
|
|
}[val]
|
|
cont = []
|
|
if val:
|
|
cont += elem.content[2:]
|
|
else:
|
|
cont += elem.content[4:]
|
|
return Plain(marker, Space, *cont)
|
|
return elem
|
|
|
|
|
|
def main(doc=None):
|
|
f = [
|
|
process_templates,
|
|
update_date,
|
|
csv_table,
|
|
include_code,
|
|
fix_color,
|
|
code_refs,
|
|
yaml_filt,
|
|
checkboxes,
|
|
]
|
|
return run_filters(f, prepare=prepare, doc=doc)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|