""" graph_view.py - Generiert interaktive Graph-Visualisierung (Cytoscape.js). """ import json from pathlib import Path from typing import Optional from .store import EngramStore _HTML_TEMPLATE = """ Second Brain Graph

🧠 Second Brain Graph

Knoten: Engramme (Farbe = Confidence)

Grün=hoch, Gelb=mittel, Rot=niedrig

Links: Verknüpfungen

Klicke für Details

""" def _confidence_color(conf: float) -> str: if conf >= 0.8: return "#27ae60" # Green elif conf >= 0.5: return "#f39c12" # Yellow else: return "#e74c3c" # Red def _node_size(access_count: int) -> float: return max(20, min(60, 20 + access_count * 5)) def generate_graph_html(store: EngramStore, output_path: str) -> str: """Generiert interaktive HTML-Graph-Visualisierung.""" # store.get_all() defaults to 1000; paginate so the graph can include all nodes. engrams = [] offset = 0 while True: batch = store.get_all(limit=2000, offset=offset) if not batch: break engrams.extend(batch) offset += len(batch) nodes = [] edges = [] node_ids = set(str(e.id) for e in engrams) for eg in engrams: eid = str(eg.id) conf = eg.compute_confidence() color = _confidence_color(conf) size = _node_size(eg.metadata.get("access_count", 0)) tags = ", ".join(eg.metadata.get("tags", [])) nodes.append({ "data": { "id": eid, "label": eg.content[:40] + ("..." if len(eg.content) > 40 else ""), "title": eg.content, "color": color, "size": size, "confidence": conf, "confirmed": eg.correctness.confirmed, "verdict": getattr(eg.correctness, "verdict", "unknown"), "source": eg.metadata.get("source", "?"), "tags": tags, } }) # Add edges after all nodes are known (otherwise early nodes miss links). for eg in engrams: eid = str(eg.id) for lid in eg.links: lid_s = str(lid) if lid_s in node_ids: edges.append({"data": {"id": f"{eid}_{lid_s}", "source": eid, "target": lid_s}}) elements = {"nodes": nodes, "edges": edges} html = _HTML_TEMPLATE.format(elements_json=json.dumps(elements, ensure_ascii=False)) out = Path(output_path) out.parent.mkdir(parents=True, exist_ok=True) with open(out, "w", encoding="utf-8") as f: f.write(html) return str(out)