#!/usr/bin/env python3 import html import logging import os import sys import uuid from datetime import datetime from org_rw import OrgTime, dom, Link from org_rw import dump as dump_org from org_rw import load as load_org from org_rw import token_list_to_raw EXTENSIONS = [ ".org", ".org.txt", ] 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) files_generated = 0 os.makedirs(dest_top, exist_ok=True) for doc in docs: relpath = os.path.relpath(doc.path, src_top) changed = False headlines = list(doc.getAllHeadlines()) related = None i = len(headlines) while i > 0: i -= 1 headline = headlines[i] 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 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: f.write(as_document(render(main_headline, doc))) 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' )) for headline in headlines: endpath = os.path.join(dest_top, headline.id + ".node.html") with open(endpath, "wt") as f: f.write(as_document(render(headline, doc))) 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("") def render_list_item(element, acc): acc.append("
  • ") if element.tag is not None: acc.append("") acc.append(element.tag) acc.append("") acc.append("") render_text_tokens(element.content, acc) acc.append("
  • ") def render_code_block(element, acc): pass def render_text(element, acc): acc.append('') render_text_tokens(element.content, acc) acc.append('') def render_text_tokens(tokens, acc): for chunk in tokens: if isinstance(chunk, str): acc.append('{} '.format(chunk)) elif isinstance(chunk, Link): # @TODO: URLEscape link_target = chunk.value if link_target.startswith('id:'): link_target = './' + link_target[3:] + '.node.html' acc.append('{}'.format(link_target, chunk.description)) else: raise NotImplementedError('TextToken: {}'.format(chunk)) 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): if headline.id != "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95": return "" print("\n===========") dom = headline.as_dom() print_tree(dom) content = [] render_tree(dom, content) for child in headline.children: content.append(render(child, doc)) if headline.state is None: state = "" else: state = f'{headline.state}' if headline.is_todo: todo_state = "todo" else: todo_state = "done" return f"""

    {state} {html.escape(headline.title)}

    {''.join(content)}
    """ def as_document(html): return f""" {html} """ def save_changes(doc): assert doc.path is not None with open(doc.path, "wt") as f: 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])