Newer
Older
#! /usr/bin/env python3
from __future__ import print_function
from os.path import join, basename
from glob import glob
import re
section_nr = [1]
def normalize_nodes(nodes, text_icon):
for i in range(len(nodes)):
node = nodes[i]
if isinstance(node, str):
node = {"text": node, "icon": text_icon}
if "section_nr" in node and node["section_nr"] is not False:
section_nr[0] = node["section_nr"]
if "section" in node:
if node.get("section_nr") is not False:
node["section_nr"] = section_nr[0]
section_nr[0] += 1
nodes[i] = node
if "nodes" in node:
normalize_nodes(node["nodes"], text_icon)
def find_section_nodes(section_dict, node):
if "section" in node:
section_dict[node["section"]] = node
if node.get("section_nr") is not False:
node["section_nr"] = section_nr[0]
for subnode in node.get("nodes", []):
find_section_nodes(section_dict, subnode)
def __init__(self, default_icon, number_sections):
self.number_sections = number_sections
def render(settings, outf, node, indent=0, skip=1):
icon = node["icon"]
attrs["icon"] = icon
if "opened" in node and int(bool(node["opened"])):
attrs["opened"] = True
if (settings.number_sections
and "section_nr" in node
and node["section_nr"] is not False):
text = "%d. %s" % (node["section_nr"], text)
if "section" in node:
text = "<b>%s</b>" % text
if not skip:
"<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]+)-(.*)(\.[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("--text-icon", metavar="ICON_STR",
default="fa fa-file-o")
parser.add_argument("--number-sections", action="store_true")
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)
normalize_nodes([root_node], args.text_icon)
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, "*", "*.ipynb"))):
if fn_match is not None:
section_id = fn_match.group(1)
display_name = basename(fn_match.group(2))
else:
section_id, display_name = trunk.split("/")
try:
section_id = int(section_id)
except ValueError:
pass
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({
"link": link_ipynb,
"icon": "fa fa-download",
})
demo_node = {
"text": "Demo: " + display_name,
"link": link_html,
"icon": "fa fa-keyboard-o",
"nodes": sub_nodes,
}
section_dict[section_id]["nodes"].append(demo_node)
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# }}}
# {{{ 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,
number_sections=args.number_sections),
outf, root_node)
if __name__ == "__main__":
main()
# vim: foldmethod=marker