Collect nodes (pages), directed edges (wikilinks), and the set of types.
(wiki_dir: Path)
| 20 | |
| 21 | |
| 22 | def build_graph(wiki_dir: Path) -> dict: |
| 23 | """Collect nodes (pages), directed edges (wikilinks), and the set of types.""" |
| 24 | nodes: dict[str, dict] = {} |
| 25 | texts: dict[str, str] = {} # nid -> file text, read once and reused for edges |
| 26 | for sub in PAGE_CONTENT_DIRS: |
| 27 | d = wiki_dir / sub |
| 28 | if not d.exists(): |
| 29 | continue |
| 30 | for p in sorted(d.glob("*.md")): |
| 31 | nid = f"{sub}/{p.stem}" |
| 32 | text = p.read_text(encoding="utf-8") |
| 33 | texts[nid] = text |
| 34 | fm = frontmatter.parse(text) |
| 35 | t = fm.get("type") |
| 36 | t = t.strip() if isinstance(t, str) and t.strip() else _type_for_dir(sub) |
| 37 | desc = fm.get("description") |
| 38 | desc = desc.strip() if isinstance(desc, str) else "" |
| 39 | srcs = fm.get("sources") |
| 40 | srcs = [str(s) for s in srcs] if isinstance(srcs, list) else [] |
| 41 | ft = fm.get( |
| 42 | "full_text" |
| 43 | ) # summaries record their origin document here, not in `sources` |
| 44 | if isinstance(ft, str) and ft.strip(): |
| 45 | srcs.insert(0, ft.strip()) |
| 46 | nodes[nid] = { |
| 47 | "id": nid, |
| 48 | "label": p.stem, |
| 49 | "type": t, |
| 50 | "description": desc, |
| 51 | "sources": srcs, |
| 52 | "out": 0, |
| 53 | "in": 0, |
| 54 | } |
| 55 | |
| 56 | norm = {_normalize_target(nid): nid for nid in nodes} |
| 57 | edges: list[dict] = [] |
| 58 | seen: set[tuple[str, str]] = set() |
| 59 | for src, text in texts.items(): |
| 60 | for raw in _extract_wikilinks(text): |
| 61 | tgt = norm.get(_normalize_target(raw)) |
| 62 | if not tgt or tgt == src or (src, tgt) in seen: |
| 63 | continue |
| 64 | seen.add((src, tgt)) |
| 65 | edges.append({"source": src, "target": tgt}) |
| 66 | nodes[src]["out"] += 1 |
| 67 | nodes[tgt]["in"] += 1 |
| 68 | |
| 69 | types = sorted({n["type"] for n in nodes.values()}) |
| 70 | return {"nodes": list(nodes.values()), "edges": edges, "types": types} |
| 71 | |
| 72 | |
| 73 | def render_html(graph: dict) -> str: |