feat: GraphML export (--graphml flag) for Gephi and yEd

This commit is contained in:
Safi
2026-04-04 23:18:53 +01:00
parent cf7f42d3e7
commit a7f910667d
3 changed files with 48 additions and 3 deletions
+2 -1
View File
@@ -107,6 +107,7 @@ All commands are typed inside Claude Code:
/graphify ./raw --html # also export graph.html (browser, no Obsidian needed)
/graphify ./raw --svg # also export graph.svg (embeds in Notion, GitHub)
/graphify ./raw --graphml # also export graph.graphml (Gephi, yEd, any GraphML tool)
/graphify ./raw --neo4j # generate cypher.txt for Neo4j import
/graphify ./raw --mcp # start MCP stdio server for agent access
```
@@ -218,7 +219,7 @@ graphify/
├── cluster.py Leiden community detection, cohesion scoring
├── analyze.py god nodes, bridge nodes, surprising connections, suggested questions, graph diff
├── report.py render GRAPH_REPORT.md
├── export.py Obsidian vault, graph.json, graph.html, graph.svg, Neo4j Cypher, Canvas
├── export.py Obsidian vault, graph.json, graph.html, graph.svg, graph.graphml, Neo4j Cypher, Canvas
├── ingest.py fetch URLs (arXiv, Twitter/X, PDF, any webpage); save Q&A to .graphify/memory/
├── cache.py SHA256-based per-file extraction cache; check_semantic_cache / save_semantic_cache
├── security.py URL validation (http/https only), safe fetch with size cap, path guards, label sanitisation
+18 -1
View File
@@ -1,4 +1,4 @@
# write graph to HTML, JSON, SVG, Obsidian vault, and Neo4j Cypher
# write graph to HTML, JSON, SVG, GraphML, Obsidian vault, and Neo4j Cypher
from __future__ import annotations
import json
import math
@@ -586,6 +586,23 @@ def push_to_neo4j(
return {"nodes": nodes_pushed, "edges": edges_pushed}
def to_graphml(
G: nx.Graph,
communities: dict[int, list[str]],
output_path: str,
) -> None:
"""Export graph as GraphML — opens in Gephi, yEd, and any GraphML-compatible tool.
Community IDs are written as a node attribute so Gephi can colour by community.
Edge confidence (EXTRACTED/INFERRED/AMBIGUOUS) is preserved as an edge attribute.
"""
H = G.copy()
node_community = {n: cid for cid, nodes in communities.items() for n in nodes}
for node_id in H.nodes():
H.nodes[node_id]["community"] = node_community.get(node_id, -1)
nx.write_graphml(H, output_path)
def to_svg(
G: nx.Graph,
communities: dict[int, list[str]],
+28 -1
View File
@@ -3,7 +3,7 @@ import tempfile
from pathlib import Path
from graphify.build import build_from_json
from graphify.cluster import cluster
from graphify.export import to_json, to_cypher
from graphify.export import to_json, to_cypher, to_graphml
FIXTURES = Path(__file__).parent / "fixtures"
@@ -52,3 +52,30 @@ def test_to_cypher_contains_merge_statements():
to_cypher(G, str(out))
content = out.read_text()
assert "MERGE" in content
def test_to_graphml_creates_file():
G = make_graph()
communities = cluster(G)
with tempfile.TemporaryDirectory() as tmp:
out = Path(tmp) / "graph.graphml"
to_graphml(G, communities, str(out))
assert out.exists()
def test_to_graphml_valid_xml():
G = make_graph()
communities = cluster(G)
with tempfile.TemporaryDirectory() as tmp:
out = Path(tmp) / "graph.graphml"
to_graphml(G, communities, str(out))
content = out.read_text()
assert "<graphml" in content
assert "<node" in content
def test_to_graphml_has_community_attribute():
G = make_graph()
communities = cluster(G)
with tempfile.TemporaryDirectory() as tmp:
out = Path(tmp) / "graph.graphml"
to_graphml(G, communities, str(out))
content = out.read_text()
assert "community" in content