new-codigoparallevar/_scripts/generate.py

283 lines
7.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
2021-08-26 22:22:48 +00:00
import html
import logging
import os
import sys
import uuid
from datetime import datetime
2022-05-07 21:03:26 +00:00
import org_rw
2022-05-06 13:58:28 +00:00
from org_rw import OrgTime, dom, Link
from org_rw import dump as dump_org
from org_rw import load as load_org
2021-08-26 22:22:48 +00:00
from org_rw import token_list_to_raw
EXTENSIONS = [
".org",
".org.txt",
]
2022-05-07 18:38:12 +00:00
MIN_HIDDEN_HEADLINE_LEVEL = 2
def load_all(top_dir_relative):
top = os.path.abspath(top_dir_relative)
docs = []
for root, dirs, files in os.walk(top):
for name in files:
if ".org" not in name:
continue
path = os.path.join(root, name)
try:
doc = load_org(open(path), extra_cautious=True)
docs.append(doc)
except Exception as err:
import traceback
traceback.print_exc()
print(f"== On {path}")
sys.exit(1)
logging.info("Collected {} files".format(len(docs)))
return docs
def main(src_top, dest_top):
docs = load_all(src_top)
2021-08-26 22:22:48 +00:00
files_generated = 0
os.makedirs(dest_top, exist_ok=True)
for doc in docs:
relpath = os.path.relpath(doc.path, src_top)
changed = False
2021-09-03 18:19:45 +00:00
headlines = list(doc.getAllHeadlines())
related = None
i = len(headlines)
while i > 0:
i -= 1
headline = headlines[i]
2021-09-03 18:19:45 +00:00
if headline.title.strip().lower() == "related" and headline.depth == 1:
if related is not None:
print(
"Found duplicated related: {} vs {}".format(
related.id, headline.id
)
)
assert related is None
related = headline
headlines.pop(i)
for headline in headlines:
if headline.id is None:
headline.id = str(uuid.uuid4())
changed = True
if changed:
print("Updated", relpath)
save_changes(doc)
if not relpath.startswith("public/"):
# print("Skip:", relpath)
continue
2021-08-26 22:22:48 +00:00
if doc.id is not None:
topHeadlines = doc.getTopHeadlines()
if ((len(topHeadlines) == 1 and related is None)
or (len(topHeadlines) == 2 and related is not None)):
endpath = os.path.join(dest_top, doc.id + ".node.html")
main_headline = [h for h in topHeadlines if h != related][0]
with open(endpath, "wt") as f:
2022-05-07 18:38:12 +00:00
f.write(as_document(render(main_headline, doc, headlineLevel=0)))
files_generated += 1
else:
logging.error("Cannot render document from id: {}. {} headlines {} related".format(
relpath,
len(topHeadlines),
'with' if related is not None else 'without'
))
2021-08-26 22:22:48 +00:00
for headline in headlines:
endpath = os.path.join(dest_top, headline.id + ".node.html")
with open(endpath, "wt") as f:
2022-05-07 18:38:12 +00:00
f.write(as_document(render(headline, doc, headlineLevel=0)))
2021-08-26 22:22:48 +00:00
files_generated += 1
logging.info("Generated {} files".format(files_generated))
def print_tree(tree, indentation=0):
for element in tree:
print(" " * indentation + "- " + str(type(element)))
if "children" in dir(element):
if len(element.children) > 0:
print_tree(element.children, indentation + 1)
print()
def render_property_drawer(element, acc):
pass
def render_logbook_drawer(element, acc):
pass
def render_property_node(element, acc):
pass
def render_list_group(element, acc):
acc.append("<ul>")
render_tree(element.children, acc)
acc.append("</ul>")
def render_list_item(element, acc):
acc.append("<li>")
2022-05-06 18:19:11 +00:00
if element.tag is not None:
2021-08-26 22:22:48 +00:00
acc.append("<span class='tag'>")
2022-05-07 21:44:37 +00:00
acc.append(html.escape(element.tag))
2021-08-26 22:22:48 +00:00
acc.append("</span>")
acc.append("<span class='item'>")
2022-05-06 18:19:11 +00:00
render_text_tokens(element.content, acc)
2021-08-26 22:22:48 +00:00
acc.append("</span></li>")
def render_code_block(element, acc):
2022-05-07 21:03:26 +00:00
acc.append('<pre><code>')
2022-05-07 21:44:37 +00:00
acc.append(html.escape(element.lines))
2022-05-07 21:03:26 +00:00
acc.append('</code></pre>')
2021-08-26 22:22:48 +00:00
def render_text(element, acc):
2022-05-06 18:19:11 +00:00
acc.append('<span class="text">')
render_text_tokens(element.content, acc)
acc.append('</span>')
def render_text_tokens(tokens, acc):
for chunk in tokens:
2022-05-06 13:58:28 +00:00
if isinstance(chunk, str):
2022-05-06 18:19:11 +00:00
acc.append('{}</span> '.format(chunk))
2022-05-06 13:58:28 +00:00
elif isinstance(chunk, Link):
2022-05-06 19:18:16 +00:00
link_target = chunk.value
if link_target.startswith('id:'):
link_target = './' + link_target[3:] + '.node.html'
2022-05-07 21:44:37 +00:00
description = chunk.description
if description is None:
description = chunk.value
acc.append('<a href="{}">{}</a>'.format(
html.escape(link_target),
html.escape(description),
))
2022-05-06 13:58:28 +00:00
else:
2022-05-06 18:19:11 +00:00
raise NotImplementedError('TextToken: {}'.format(chunk))
2021-08-26 22:22:48 +00:00
def render_tag(element, acc):
return {
dom.PropertyDrawerNode: render_property_drawer,
dom.LogbookDrawerNode: render_logbook_drawer,
dom.PropertyNode: render_property_node,
dom.ListGroupNode: render_list_group,
dom.ListItem: render_list_item,
dom.CodeBlock: render_code_block,
dom.Text: render_text,
}[type(element)](element, acc)
def render_tree(tree, acc):
for element in tree:
render_tag(element, acc)
def render(headline, doc):
2022-05-06 18:19:11 +00:00
if headline.id != "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95":
return ""
2021-08-26 22:22:48 +00:00
print("\n===========")
dom = headline.as_dom()
print_tree(dom)
content = []
render_tree(dom, content)
2021-09-03 22:26:28 +00:00
for child in headline.children:
2022-05-07 18:38:12 +00:00
content.append(render(child, doc, headlineLevel=headlineLevel+1))
2021-08-26 22:22:48 +00:00
2021-09-03 18:19:45 +00:00
if headline.state is None:
state = ""
else:
state = f'<span class="state todo-{headline.is_todo} state-{headline.state}">{headline.state}</span>'
if headline.is_todo:
todo_state = "todo"
else:
todo_state = "done"
2022-05-07 18:38:12 +00:00
display_state = 'collapsed'
if headlineLevel < MIN_HIDDEN_HEADLINE_LEVEL:
display_state = 'expanded'
2021-08-26 22:22:48 +00:00
return f"""
2022-05-07 18:38:12 +00:00
<div id="{html.escape(headline.id)}" class="node {todo_state} {display_state}">
2021-08-26 22:22:48 +00:00
<h1 class="title">
2021-09-03 18:19:45 +00:00
{state}
2022-05-07 18:38:12 +00:00
<a href=\"javascript:toggle_expand('{html.escape(headline.id)}')\">
2021-08-26 22:22:48 +00:00
{html.escape(headline.title)}
</a>
</h1>
2022-05-07 18:38:12 +00:00
<div class='contents'>
{''.join(content)}
</div>
2021-09-03 22:26:10 +00:00
</div>
2021-08-26 22:22:48 +00:00
"""
2021-09-03 22:26:10 +00:00
def as_document(html):
2022-05-07 16:35:18 +00:00
return f"""<!DOCTYPE html>
<html>
<head>
2021-09-03 22:26:10 +00:00
<link href="../static/style.css" rel="stylesheet"/>
2022-05-07 18:38:12 +00:00
<script type="text/javascript">
function toggle_expand(header_id) {{
var e = document.getElementById(header_id);
if (e.classList.contains('expanded')) {{
e.classList.add('collapsed');
e.classList.remove('expanded');
}}
else {{
e.classList.add('expanded');
e.classList.remove('collapsed');
}}
}}
</script>
2022-05-07 16:35:18 +00:00
</head>
<body>
2021-09-03 22:26:10 +00:00
{html}
2022-05-07 16:35:18 +00:00
</body>
</html>
2021-09-03 22:26:10 +00:00
"""
def save_changes(doc):
assert doc.path is not None
with open(doc.path, "wt") as f:
2021-09-03 18:19:45 +00:00
dump_org(doc, f)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: {} SOURCE_TOP DEST_TOP".format(sys.argv[0]))
exit(0)
logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
main(sys.argv[1], sys.argv[2])