11#! /usr/bin/env python3
22
3- from __future__ import print_function
4- from os .path import join , basename
5- from urllib .parse import quote_plus
6- from glob import glob
3+ from __future__ import annotations
4+
5+ from pathlib import Path
76import re
7+ from dataclasses import dataclass
8+ from glob import glob
9+ from os .path import basename , join
10+ from typing import TYPE_CHECKING , NotRequired , TypedDict
11+ from urllib .parse import quote_plus
12+
13+
14+ if TYPE_CHECKING :
15+ from collections .abc import Mapping , Sequence
816
917
1018section_nr = [1 ]
1119
1220
13- def rewrite_org_element (args , org_el ):
14- el_type = org_el [0 ]
15- org_props = org_el [1 ]
16- assert org_props is None or isinstance (org_props , dict )
17- contents = org_el [2 :]
21+ class Node (TypedDict ):
22+ text : str
23+ link : str
24+ icon : str
25+ section : NotRequired [str ]
26+ section_nr : NotRequired [int ]
27+ nodes : NotRequired [Sequence [Node ]]
28+ opened : NotRequired [bool ]
29+
30+
31+ # Assumes org json export from https://github.com/jlumpe/ox-json
32+ def rewrite_org_element (
33+ args , org_el
34+ ) -> tuple [Node , Sequence [Node ], bool ]:
35+ if isinstance (org_el , str ):
36+ return {}, [], False
37+
38+ el_type = org_el ["$$data_type" ]
39+ el_subtype = org_el .get ("type" )
40+ org_props = org_el ["properties" ]
41+ assert isinstance (org_props , dict )
42+ contents = org_el ["contents" ]
1843
1944 had_relate_properties = False
2045 promote_to_parent_level = False
21- node_props = {}
22- if el_type == "org-data" :
23- title , = [
24- subel [1 ]["key" ]
25- for el in contents
26- if el [0 ] == "section"
27- for subel in el [2 :]
28- if subel [0 ] == "keyword"
29- if subel [1 ]["key" ] == "TITLE" ]
46+ node_props : Node = {}
47+ if el_type == "org-document" :
48+ title = org_props ["title" ]
3049 node_props ["text" ] = title
3150
32- elif el_type == "headline" :
51+ elif el_type == "org-node" and el_subtype == " headline" :
3352 node_props ["text" ] = org_props ["raw-value" ]
3453
35- prop_container = [
36- subel [2 :]
37- for el in contents
38- if el [0 ] == "section"
39- for subel in el [2 :]
40- if subel [0 ] == "property-drawer" ]
41-
42- if prop_container :
43- prop_kws = prop_container [0 ]
44- else :
45- prop_kws = []
46-
47- properties = {
48- ch [1 ]["key" ]: ch [1 ]["value" ]
49- for ch in prop_kws
50- if ch [0 ] == "node-property"
51- }
54+ properties = org_el .get ("drawer" , {})
5255
5356 had_relate_properties = had_relate_properties or any (
5457 key .startswith ("RELATE_" ) for key in properties )
@@ -74,21 +77,20 @@ def rewrite_org_element(args, org_el):
7477 and level < len (args .org_level_icon )):
7578 node_props ["icon" ] = args .org_level_icon [level ]
7679
77- siblings = []
78- children = []
80+ siblings : list [ Node ] = []
81+ children : list [ Node ] = []
7982 node_props ["nodes" ] = children
8083
8184 for ch in contents :
82- if isinstance (ch , list ):
83- child_props , child_siblings , promote_child_to_parent_level = \
84- rewrite_org_element (args , ch )
85- if "text" in child_props :
86- if promote_child_to_parent_level :
87- siblings .append (child_props )
88- else :
89- children .append (child_props )
85+ child_props , child_siblings , promote_child_to_parent_level = \
86+ rewrite_org_element (args , ch )
87+ if "text" in child_props :
88+ if promote_child_to_parent_level :
89+ siblings .append (child_props )
90+ else :
91+ children .append (child_props )
9092
91- children .extend (child_siblings )
93+ children .extend (child_siblings )
9294
9395 if args .org_stop_level :
9496 if (level is not None
@@ -106,12 +108,11 @@ def rewrite_org_element(args, org_el):
106108 return node_props , siblings , promote_to_parent_level
107109
108110
109- def load_org_json (args , infile_name ):
111+ def load_org_json (args , infile_name : str ):
110112 # https://github.com/ludios/org-to-json
111113
112114 from json import loads
113- with open (infile_name , "rb" ) as inf :
114- json = loads (inf .read ())
115+ json = loads (Path (infile_name ).read_bytes ())
115116
116117 node_props , siblings , promote_to_parent_level = \
117118 rewrite_org_element (args , json )
@@ -156,20 +157,25 @@ def find_section_nodes(section_dict, node):
156157 find_section_nodes (section_dict , subnode )
157158
158159
160+ @dataclass (frozen = True )
159161class RenderSettings :
160- def __init__ (self , default_icon , number_sections , tree_replacements ):
161- self .default_icon = default_icon
162- self .number_sections = number_sections
163- self .tree_replacements = tree_replacements
162+ default_icon : str
163+ number_sections : bool
164+ tree_replacements : Mapping [str , str ]
164165
165- def apply_replacements (self , s ):
166+ def apply_replacements (self , s : str ):
166167 for key , val in self .tree_replacements .items ():
167168 s = s .replace (key , val )
168169
169170 return s
170171
171172
172- def render (settings , outf , node , indent = 0 , skip = 1 ):
173+ def render (
174+ settings : RenderSettings ,
175+ outf : str ,
176+ node : Node ,
177+ indent = 0 ,
178+ skip = 1 ):
173179 attrs = {}
174180
175181 icon = settings .default_icon
@@ -249,7 +255,10 @@ def get_section_id_and_display_name(trunk, include_extension):
249255 return section_id , display_name
250256
251257
252- def blacklisted_glob (basedir , pattern , blacklist_regexps ):
258+ def blacklisted_glob (
259+ basedir : str ,
260+ pattern : str ,
261+ blacklist_regexps : Sequence [re .Pattern [str ]]):
253262 return sorted (
254263 name
255264 for name in glob (join (basedir , pattern ))
@@ -348,7 +357,7 @@ def main():
348357 link_html = link_ipynb .replace (".ipynb" , ".html" )
349358 main_link = link_html
350359
351- sub_nodes = [{
360+ sub_nodes : list [ Node ] = [{
352361 "text" : "View on the web" ,
353362 "link" : link_html ,
354363 "icon" : "bi bi-newspaper" ,
@@ -392,7 +401,7 @@ def main():
392401 "icon" : "bi bi-download" ,
393402 })
394403
395- demo_node = {
404+ demo_node : Node = {
396405 "text" : "Demo: " + display_name ,
397406 "link" : main_link ,
398407 "icon" : "bi bi-keyboard" ,
0 commit comments