22#
33# A script to generate markdown documentation from table schemas.
44
5+ from typing import Iterable
56from table2md import MarkdownTable
67import yaml
78from pathlib import Path
9+ from dataclasses import dataclass
10+ from jinja2 import Environment , FileSystemLoader
811
912_DOCS_DIR = Path (__file__ ).parent
1013_SCHEMA_DIR = _DOCS_DIR .parent / "schemas" / "input"
1821}
1922
2023
24+ @dataclass
25+ class Notes :
26+ description : str | None
27+ table : str | None
28+
29+
30+ @dataclass
31+ class File :
32+ name : str
33+ description : str
34+ table : str
35+ notes : Notes | None
36+
37+
38+ @dataclass
39+ class Section :
40+ title : str
41+ files : Iterable [File ]
42+
43+
2144def generate_markdown () -> str :
22- out = (
23- "# Input file format\n "
24- f"<!-- Automatically generated by { Path (__file__ ).name } . Do not edit manually. -->\n "
25- "<!-- markdownlint-disable MD013 -->\n "
26- "<!-- markdownlint-disable MD033 -->\n "
27- )
45+ """Generate markdown from Jinja template using metadata in schemas."""
46+ env = Environment (loader = FileSystemLoader (_DOCS_DIR ))
47+ template = env .get_template ("input_format.md.jinja" )
48+ return template .render (script_name = Path (__file__ ).name , sections = load_sections ())
2849
29- for title , patterns in _FILE_ORDER .items ():
30- out += f"\n ## { title } \n "
3150
51+ def load_sections () -> Iterable [Section ]:
52+ for title , patterns in _FILE_ORDER .items ():
3253 for pattern in patterns :
3354 paths = map (str , _SCHEMA_DIR .glob (f"{ pattern } .yaml" ))
34- for path in map (Path , sorted (paths )):
35- out += process_file (path )
36-
37- return out
55+ files = (load_file (Path (path )) for path in sorted (paths ))
56+ yield Section (title , files )
3857
3958
40- def process_file (path : Path ) -> str :
41- out = f"\n ### `{ path .stem } .csv`\n \n "
59+ def load_file (path : Path ) -> File :
4260 with path .open () as f :
4361 data = yaml .safe_load (f )
4462
45- out += f"{ add_full_stop (data ['title' ])} \n \n "
46-
4763 try :
48- table_str , notes_str = fields2table (data ["fields" ])
49- out += table_str
64+ table , notes_table = fields2table (data ["fields" ])
5065 except KeyError :
5166 print (f"MISSING VALUE IN { path } " )
5267 raise
5368
54- desc = data .get ("description" , "" )
55- if not desc and not notes_str :
56- return out
57-
58- out += "\n #### Notes\n \n "
59-
60- if desc :
61- out += f"{ add_full_stop (desc )} \n \n "
62-
63- if notes_str :
64- out += notes_str
65-
66- return out
69+ name = f"{ path .stem } .csv"
70+ title = add_full_stop (data ["title" ])
71+ if desc := data .get ("description" , None ):
72+ desc = add_full_stop (desc )
73+ notes = Notes (desc , notes_table ) if desc or notes_table else None
74+ return File (name , title , table , notes )
6775
6876
6977def add_full_stop (s : str ) -> str :
@@ -74,7 +82,7 @@ def add_full_stop(s: str) -> str:
7482 return f"{ s } ."
7583
7684
77- def fields2table (fields : list [dict [str , str ]]) -> tuple [str , str ]:
85+ def fields2table (fields : list [dict [str , str ]]) -> tuple [str , str | None ]:
7886 data = []
7987 notes = []
8088 for f in fields :
@@ -95,9 +103,9 @@ def fields2table(fields: list[dict[str, str]]) -> tuple[str, str]:
95103 for f in fields
96104 ]
97105
98- table_str = str (MarkdownTable .from_dicts (data ))
99- notes_str = str (MarkdownTable .from_dicts (notes )) if notes else ""
100- return table_str , notes_str
106+ table = str (MarkdownTable .from_dicts (data ))
107+ notes_table = str (MarkdownTable .from_dicts (notes )) if notes else None
108+ return table , notes_table
101109
102110
103111if __name__ == "__main__" :
0 commit comments