from __future__ import annotations import argparse import json from collections import Counter from datetime import date, datetime from pathlib import Path from typing import Any from neo4j import GraphDatabase from sleep_db.config import Settings, load_settings LABEL_COLORS = { "Metric": "#38bdf8", "ThresholdCondition": "#f59e0b", "ConditionGroup": "#f97316", "State": "#a78bfa", "Effect": "#fb7185", "Advice": "#34d399", "Activity": "#22c55e", "Nutrition": "#84cc16", "Environment": "#06b6d4", "Doc": "#e5e7eb", "RuleVersion": "#94a3b8", } def fetch_neo4j_graph(settings: Settings) -> dict[str, Any]: driver = GraphDatabase.driver( settings.neo4j_uri, auth=(settings.neo4j_user, settings.neo4j_password), ) try: with driver.session() as session: nodes = [ _node_from_record(record) for record in session.run( """ MATCH (n) RETURN elementId(n) AS element_id, labels(n) AS labels, properties(n) AS props ORDER BY labels(n)[0], coalesce(n.name, n.id, n.doc_id, n.rule_id) """ ) ] edges = [ _edge_from_record(record) for record in session.run( """ MATCH (a)-[r]->(b) RETURN elementId(r) AS element_id, elementId(a) AS source_element_id, elementId(b) AS target_element_id, type(r) AS type, properties(r) AS props ORDER BY type(r) """ ) ] finally: driver.close() label_counts = Counter(node["label"] for node in nodes) edge_counts = Counter(edge["type"] for edge in edges) return { "generated_at": datetime.now().isoformat(timespec="seconds"), "nodes": nodes, "edges": edges, "stats": { "node_count": len(nodes), "edge_count": len(edges), "label_counts": dict(label_counts), "edge_type_counts": dict(edge_counts), }, } def write_graph_visualization(graph: dict[str, Any], output_dir: Path) -> dict[str, Path]: output_dir.mkdir(parents=True, exist_ok=True) json_path = output_dir / "neo4j_graph.json" html_path = output_dir / "neo4j_graph.html" json_path.write_text(json.dumps(graph, indent=2, ensure_ascii=False), encoding="utf-8") html_path.write_text(render_graph_html(graph), encoding="utf-8") return {"json": json_path, "html": html_path} def render_graph_html(graph: dict[str, Any]) -> str: graph_json = json.dumps(graph, ensure_ascii=False) label_color_json = json.dumps(LABEL_COLORS) return f"""