#! /usr/bin/env python3 from __future__ import print_function from os.path import join, basename from glob import glob import re def find_section_nodes(section_dict, node): if "section" in node: section_dict[node["section"]] = node for subnode in node.get("nodes", []): find_section_nodes(section_dict, subnode) class RenderSettings: def __init__(self, default_icon): self.default_icon = default_icon def render(settings, outf, node, indent=0, skip=1): attrs = {} icon = settings.default_icon if "icon" in node: icon = node["icon"] attrs["icon"] = icon text = node["text"] if "section" in node: text = "%s" % text if not skip: print( indent * " ", "
  • " % repr(attrs).replace("'", '"'), file=outf, sep="") indent += 2 if "link" in node: print( indent * " ", "%s" % ( node["link"], text), file=outf, sep="") else: print(indent * " ", text, file=outf, sep="") subnodes = node.get("nodes", []) if subnodes: print(indent * " ", "", file=outf, sep="") if not skip: indent -= 2 print(indent * " ", "
  • ", file=outf, sep="") FN_REGEX = re.compile(r"^([0-9][0-9])-(.*)(\.[a-z]+)$") def main(): import argparse parser = argparse.ArgumentParser( description='Turn a YAML file into a jsTree-compatible data file') parser.add_argument("-o", "--output-file", metavar="FILE", required=True) parser.add_argument("--ipynb-dir", metavar="DIRECTORY") parser.add_argument("--ipynb-urlroot", metavar="URL", help="(without the trailing slash)") parser.add_argument("--ipynb-as-py", action="store_true") parser.add_argument("--ipynb-as-ipynb", action="store_true") parser.add_argument("--py-dir", metavar="DIRECTORY") parser.add_argument("--py-urlroot", metavar="URL", help="(without the trailing slash)") parser.add_argument("--pdf-dir", metavar="DIRECTORY") parser.add_argument("--pdf-urlroot", metavar="URL", help="(without the trailing slash)") parser.add_argument("--default-icon", metavar="ICON_STR", default="fa fa-file-o") parser.add_argument("input_file", metavar="FILE") args = parser.parse_args() from yaml import load with open(args.input_file, "rb") as inf: root_node = load(inf) section_dict = {} find_section_nodes(section_dict, root_node) # {{{ demos if args.ipynb_dir is not None: for fn in sorted(glob(join(args.ipynb_dir, "[0-9]*", "*.ipynb"))): trunk = fn[len(args.ipynb_dir)+1:] fn_match = FN_REGEX.match(trunk) sec_nr = int(fn_match.group(1)) link_ipynb = args.ipynb_urlroot + "/" + trunk link_html = link_ipynb.replace(".ipynb", ".html") sub_nodes = [{ "text": "View on the web", "link": link_html, "icon": "fa fa-newspaper-o", }] if args.ipynb_as_py: link_py = link_ipynb.replace(".ipynb", ".py") sub_nodes.append({ "text": "Download Python script", "link": link_py, "icon": "fa fa-terminal", }) if args.ipynb_as_ipynb: sub_nodes.append({ "text": "Download IPython notebook", "link": link_ipynb, "icon": "fa fa-download", }) demo_node = { "text": "Demo: " + basename(fn_match.group(2)), "link": link_html, "icon": "fa fa-keyboard-o", "nodes": sub_nodes, } section_dict[sec_nr]["nodes"].append(demo_node) # }}} # {{{ python source if args.py_dir is not None: for fn in sorted(glob(join(args.py_dir, "[0-9]*", "*.py"))): trunk = fn[len(args.py_dir)+1:] fn_match = FN_REGEX.match(trunk) sec_nr = int(fn_match.group(1)) src_node = { "text": "Code: " + basename( fn_match.group(2)+fn_match.group(3)), "link": args.py_urlroot + "/" + trunk, "icon": "fa fa-file-text-o", } section_dict[sec_nr]["nodes"].append(src_node) # }}} # {{{ notes if args.pdf_dir is not None: for fn in sorted(glob(join(args.pdf_dir, "[0-9]*.pdf"))): if "autosave" in fn: continue trunk = fn[len(args.pdf_dir)+1:] fn_match = FN_REGEX.match(trunk) sec_nr = int(fn_match.group(1)) notes_node = { "text": "PDF: " + basename( fn_match.group(2)+fn_match.group(3)), "link": args.pdf_urlroot + "/" + trunk, "icon": "fa fa-book", } section_dict[sec_nr]["nodes"].insert(0, notes_node) # }}} with open(args.output_file, "wt") as outf: render(RenderSettings(default_icon=args.default_icon), outf, root_node) if __name__ == "__main__": main() # vim: foldmethod=marker