pboProperty Ontology
python75 lines4.2 KB
RawDownload
1#!/usr/bin/env python3
2"""Build a self-contained HTML rendering of an Ontology Semantic Atlas.
3
4Usage: build_atlas_html.py <project-dir> <mermaid-bundle.js> [--atlas documentation/semantic-atlas.md] [--out documentation/semantic-atlas.html]
5
6The bundle is Mermaid + @mermaid-js/layout-elk (registered) compiled to a single IIFE that sets
7window.mermaid (see scripts/build_mermaid_bundle.sh) — so every ```mermaid block, all of which carry
8the `config: layout: elk` frontmatter, is laid out by ELK with no CDN, plugin or viewer support needed.
9Blocks that fail to render are replaced in-page by a visible RENDER ERROR box, and the page exposes
10window.__report = {total, ok, errors:[{index, message}]} for headless checks.
11
12Requires the `markdown` package (pip install markdown).
13"""
14from __future__ import annotations
15import argparse, re
16from pathlib import Path
17import markdown
18
19ap = argparse.ArgumentParser()
20ap.add_argument("project_dir", type=Path); ap.add_argument("bundle", type=Path)
21ap.add_argument("--atlas", type=Path, default=Path("documentation/semantic-atlas.md"))
22ap.add_argument("--out", type=Path, default=Path("documentation/semantic-atlas.html"))
23a = ap.parse_args()
24proj = a.project_dir.resolve()
25atlas = a.atlas if a.atlas.is_absolute() else proj / a.atlas
26out = a.out if a.out.is_absolute() else proj / a.out
27md_src = atlas.read_text(encoding="utf-8")
28body = markdown.markdown(md_src, extensions=["tables", "fenced_code", "toc"])
29# python-markdown emits <pre><code class="language-mermaid">…escaped…</code></pre>; keep the escaped text,
30# Mermaid decodes entities from a <pre> element's textContent.
31pat = re.compile(r'<pre><code class="language-mermaid">(.*?)</code></pre>', re.S)
32n = 0
33def repl(m):
34 global n; n += 1
35 return f'<pre class="mermaid" data-idx="{n-1}">{m.group(1)}</pre>'
36body = pat.sub(repl, body)
37assert n == md_src.count("```mermaid"), (n, md_src.count("```mermaid"))
38title_m = re.search(r"^# (.+)$", md_src, re.M)
39title = title_m.group(1) if title_m else "Ontology Semantic Atlas"
40css = """
41body{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;max-width:1400px;margin:0 auto;padding:1rem 2rem;color:#1a1a1a;background:#fff;line-height:1.45}
42table{border-collapse:collapse;font-size:.85rem;margin:1rem 0;display:block;overflow-x:auto}
43th,td{border:1px solid #ccc;padding:.3rem .5rem;text-align:left;vertical-align:top} th{background:#f3f3f3}
44pre.mermaid{background:transparent;overflow-x:auto;margin:1rem 0} pre.mermaid svg{max-width:100%;height:auto}
45pre.render-error{background:#fff0f0;border:2px solid #c00;padding:1rem;white-space:pre-wrap;font-size:.8rem}
46code{background:#f4f4f4;padding:0 .2em;border-radius:3px;font-size:.9em} pre:not(.mermaid) code{background:none}
47h1,h2,h3{margin-top:2rem}
48#status{position:fixed;top:0;right:0;background:#222;color:#fff;font:12px monospace;padding:.3rem .6rem;z-index:9}
49"""
50js = """
51(async()=>{
52 const st=document.getElementById('status');
53 mermaid.initialize({startOnLoad:false, securityLevel:'strict', maxTextSize:900000, maxEdges:5000});
54 const nodes=[...document.querySelectorAll('pre.mermaid')];
55 const report={total:nodes.length, ok:0, errors:[]}; window.__report=report;
56 for(const el of nodes){
57 const idx=el.dataset.idx, src=el.textContent;
58 try{ const {svg}=await mermaid.render('atlas-svg-'+idx, src); el.innerHTML=svg; report.ok++; }
59 catch(e){ const p=document.createElement('pre'); p.className='render-error';
60 p.textContent='RENDER ERROR (block '+idx+'): '+(e&&e.message||e)+'\\n\\n'+src;
61 el.replaceWith(p); report.errors.push({index:+idx, message:String(e&&e.message||e)}); }
62 st.textContent='rendered '+(report.ok+report.errors.length)+'/'+report.total+(report.errors.length?' errors: '+report.errors.length:'');
63 }
64 window.__done=true;
65})();
66"""
67doc = f"""<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
68<title>{title}</title><style>{css}</style>
69<script>{a.bundle.read_text(encoding="utf-8")}</script>
70</head><body><div id="status">rendering…</div>
71{body}
72<script>{js}</script></body></html>"""
73out.parent.mkdir(parents=True, exist_ok=True)
74out.write_text(doc, encoding="utf-8")
75print(f"Wrote {out}{n} mermaid blocks, {out.stat().st_size/1e6:.1f} MB")