mirror of
https://github.com/safishamsi/graphify.git
synced 2026-08-28 09:17:02 +00:00
fix(export): stabilize graph JSON collection order
This commit is contained in:
+10
-1
@@ -292,6 +292,10 @@ def to_json(G: nx.Graph, communities: dict[int, list[str]], output_path: str, *,
|
||||
data = json_graph.node_link_data(G, edges="links")
|
||||
except TypeError:
|
||||
data = json_graph.node_link_data(G)
|
||||
|
||||
def _json_sort_key(item: dict) -> str:
|
||||
return json.dumps(item, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||
|
||||
for node in data["nodes"]:
|
||||
cid = node_community.get(node["id"])
|
||||
node["community"] = cid
|
||||
@@ -311,6 +315,8 @@ def to_json(G: nx.Graph, communities: dict[int, list[str]], output_path: str, *,
|
||||
if true_src is not None and true_tgt is not None:
|
||||
link["source"] = true_src
|
||||
link["target"] = true_tgt
|
||||
data["nodes"].sort(key=_json_sort_key)
|
||||
data["links"].sort(key=_json_sort_key)
|
||||
if "hyperedges" not in getattr(G, "graph", {}):
|
||||
# Hardening (#2485): a graph with NO hyperedges key at all was built by
|
||||
# a path that never engaged hyperedge metadata — distinct from an
|
||||
@@ -339,7 +345,10 @@ def to_json(G: nx.Graph, communities: dict[int, list[str]], output_path: str, *,
|
||||
f"extraction if this is unexpected.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
data["hyperedges"] = getattr(G, "graph", {}).get("hyperedges", [])
|
||||
hyperedges = sorted(getattr(G, "graph", {}).get("hyperedges", []), key=_json_sort_key)
|
||||
if isinstance(data.get("graph"), dict) and "hyperedges" in data["graph"]:
|
||||
data["graph"]["hyperedges"] = hyperedges
|
||||
data["hyperedges"] = hyperedges
|
||||
commit = built_at_commit if built_at_commit is not None else _git_head()
|
||||
if commit:
|
||||
data["built_at_commit"] = commit
|
||||
|
||||
@@ -40,6 +40,38 @@ def test_to_json_nodes_have_community():
|
||||
for node in data["nodes"]:
|
||||
assert "community" in node
|
||||
|
||||
|
||||
def test_to_json_sorts_graph_collections_across_insertion_order(tmp_path):
|
||||
import networkx as nx
|
||||
|
||||
nodes = [("b", {"label": "Beta"}), ("a", {"label": "Alpha"}), ("c", {"label": "Gamma"})]
|
||||
links = [
|
||||
("b", "c", {"relation": "uses", "_src": "b", "_tgt": "c"}),
|
||||
("a", "b", {"relation": "calls", "_src": "a", "_tgt": "b"}),
|
||||
]
|
||||
hyperedges = [
|
||||
{"id": "h2", "nodes": ["b", "c"]},
|
||||
{"id": "h1", "nodes": ["a", "b"]},
|
||||
]
|
||||
|
||||
def make_graph(reverse=False):
|
||||
graph = nx.Graph()
|
||||
graph.add_nodes_from(reversed(nodes) if reverse else nodes)
|
||||
graph.add_edges_from(reversed(links) if reverse else links)
|
||||
graph.graph["hyperedges"] = list(reversed(hyperedges)) if reverse else hyperedges
|
||||
return graph
|
||||
|
||||
outputs = [tmp_path / "first.json", tmp_path / "second.json"]
|
||||
for output, reverse in zip(outputs, (False, True)):
|
||||
assert to_json(
|
||||
make_graph(reverse),
|
||||
{0: ["a", "b"], 1: ["c"]},
|
||||
str(output),
|
||||
built_at_commit="fixed",
|
||||
)
|
||||
|
||||
assert outputs[0].read_bytes() == outputs[1].read_bytes()
|
||||
|
||||
def test_to_cypher_creates_file():
|
||||
G = make_graph()
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
|
||||
Reference in New Issue
Block a user