File size: 1,878 Bytes
42fb3af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
"""Save and load the ALS knowledge graph (pickle for speed, JSON for inspection)."""
from __future__ import annotations

import json
import pickle
from pathlib import Path

import networkx as nx

from config import GRAPH_JSON_PATH, GRAPH_PICKLE_PATH
from logging_config import get_logger

_logger = get_logger("graph.serializer")


def save_graph(
    G: nx.DiGraph,
    pickle_path: Path = GRAPH_PICKLE_PATH,
    json_path: Path = GRAPH_JSON_PATH,
) -> None:
    for path in (pickle_path, json_path):
        path.parent.mkdir(parents=True, exist_ok=True)

    with open(pickle_path, "wb") as f:
        pickle.dump(G, f, protocol=pickle.HIGHEST_PROTOCOL)

    data = {
        "nodes": [
            {"id": n, **{k: _json_safe(v) for k, v in G.nodes[n].items()}}
            for n in G.nodes
        ],
        "edges": [
            {"source": u, "target": v, **{k: _json_safe(dv) for k, dv in d.items()}}
            for u, v, d in G.edges(data=True)
        ],
        "stats": {
            "nodes": G.number_of_nodes(),
            "edges": G.number_of_edges(),
        },
    }
    json_path.write_text(json.dumps(data, indent=2))
    _logger.info(f"Saved graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges")


def load_graph(pickle_path: Path = GRAPH_PICKLE_PATH) -> nx.DiGraph:
    if not pickle_path.exists():
        raise FileNotFoundError(
            f"Graph not found at {pickle_path}. "
            "Run: uv run python scripts/build_graph.py"
        )
    with open(pickle_path, "rb") as f:
        G = pickle.load(f)
    _logger.info(f"Loaded graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges")
    return G


def _json_safe(value: object) -> object:
    if isinstance(value, set):
        return list(value)
    if isinstance(value, (list, dict, str, int, float, bool)) or value is None:
        return value
    return str(value)