Skip to content
make-tree-html 5.64 KiB
Newer Older
#! /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
    if "opened" in node and int(bool(node["opened"])):
        attrs["opened"] = True

    text = node["text"]
    if "section" in node:
        text = "<b>%s</b>" % text

    if not skip:
        from json import dumps
        print(
            indent * " ",
            "<li data-jstree='%s'>" % dumps(attrs).replace("'", '"'),
            file=outf, sep="")
        indent += 2
        if "link" in node:
            print(
                indent * " ",
                "<a href=\"%s\">%s</a>" % (
                    node["link"],
                    text),
                file=outf, sep="")
        else:
            print(indent * " ", text, file=outf, sep="")

    subnodes = node.get("nodes", [])
    if subnodes:
        print(indent * " ", "<ul>", file=outf, sep="")
        indent += 2
        for subnode in subnodes:
            render(settings, outf, subnode, indent, skip=max(0, skip - 1))
        indent -= 2
        print(indent * " ", "</ul>", file=outf, sep="")

    if not skip:
        indent -= 2
        print(indent * " ", "</li>", 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