ED_LRR/docs/filters/multifilter.py

158 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()