"""Regenerate graphify-out/graph.html with a real 3D viewer, falling back to 2D vis-network if Three.js renders nothing. Run: . venv/bin/activate && python3 regen_graph_html_3d.py """ from pathlib import Path import json ROOT = Path(__file__).resolve().parent GRAPH_JSON = ROOT / "graphify-out" / "graph.json" OUT = ROOT / "graphify-out" / "graph.html" data = json.loads(GRAPH_JSON.read_text(encoding="utf-8")) nodes = data.get("nodes", []) links = data.get("links", []) def esc(s: str) -> str: return ( str(s) .replace("&", "&") .replace("<", "<") .replace(">", ">") .replace('"', """) .replace("'", "'") ) degree = {} for link in links: for key in ("source", "from", "target", "to"): if key in link and link[key] is not None: degree[link[key]] = degree.get(link[key], 0) + 1 break for n in nodes: nid = n.get("id") or n.get("name") n["__degree__"] = degree.get(nid, 0) RAW_NODES = json.dumps(nodes, ensure_ascii=False) RAW_LINKS = json.dumps(links, ensure_ascii=False) PALETTE = [ "#E15759", "#4E79A7", "#F28E2B", "#76B7B2", "#59A14F", "#EDC948", "#B07AA1", "#FF9DA7", "#9C755F", "#BAB0AC", "#86BCB6", "#8CD17D", "#B6992D", "#499894", "#D37295", "#F1CE63", "#D4A6C8", ] seen_communities = [] for n in nodes: cn = (n.get("community_name") or n.get("community") or "").strip() if cn and cn not in seen_communities: seen_communities.append(cn) def community_color(name: str, index: int) -> str: if not name: return PALETTE[index % len(PALETTE)] import hashlib h = hashlib.md5(name.encode("utf-8")).hexdigest() return PALETTE[int(h, 16) % len(PALETTE)] legend_items = [] for idx, cn in enumerate(seen_communities): color = community_color(cn, idx) legend_items.append( '' '%s' % (color, esc(cn)) ) legend_html = "\n".join( '