Coverage for src / graphable / views / cytoscape.py: 100%
39 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
1from dataclasses import dataclass
2from json import dumps
3from logging import getLogger
4from pathlib import Path
5from typing import Any, Callable
7from ..graph import Graph
8from ..graphable import Graphable
9from ..registry import register_view
11logger = getLogger(__name__)
14@dataclass
15class CytoscapeStylingConfig:
16 """
17 Configuration for Cytoscape.js JSON serialization.
19 Attributes:
20 node_data_fnc: Optional function to add extra data to each node's 'data' object.
21 edge_data_fnc: Optional function to add extra data to each edge's 'data' object.
22 reference_fnc: Function to generate the string identifier for each node.
23 indent: JSON indentation level.
24 """
26 node_data_fnc: Callable[[Graphable[Any]], dict[str, Any]] | None = None
27 edge_data_fnc: Callable[[Graphable[Any], Graphable[Any]], dict[str, Any]] | None = (
28 None
29 )
30 reference_fnc: Callable[[Graphable[Any]], str] = lambda n: str(n.reference)
31 indent: int | str | None = 2
34def create_topology_cytoscape(
35 graph: Graph, config: CytoscapeStylingConfig | None = None
36) -> str:
37 """
38 Generate a Cytoscape.js compatible JSON representation of the graph.
40 Args:
41 graph (Graph): The graph to convert.
42 config (CytoscapeStylingConfig | None): Serialization configuration.
44 Returns:
45 str: A JSON string in Cytoscape.js elements format.
46 """
47 logger.debug("Creating Cytoscape JSON representation.")
48 config = config or CytoscapeStylingConfig()
50 elements = []
52 for node in graph.topological_order():
53 # Node
54 node_id = config.reference_fnc(node)
55 node_data = {
56 "id": node_id,
57 "label": str(node.reference),
58 "tags": list(node.tags),
59 }
60 if config.node_data_fnc:
61 node_data.update(config.node_data_fnc(node))
63 elements.append({"data": node_data})
65 # Edges
66 for dependent, attrs in graph.internal_dependents(node):
67 dep_id = config.reference_fnc(dependent)
68 edge_id = f"{node_id}_{dep_id}"
69 edge_data = {
70 "id": edge_id,
71 "source": node_id,
72 "target": dep_id,
73 }
74 if config.edge_data_fnc:
75 edge_data.update(config.edge_data_fnc(node, dependent))
77 # Add existing attributes
78 edge_data.update(attrs)
80 elements.append({"data": edge_data})
82 return dumps(elements, indent=config.indent)
85@register_view(".cy.json", creator_fnc=create_topology_cytoscape)
86def export_topology_cytoscape(
87 graph: Graph,
88 output: Path,
89 config: CytoscapeStylingConfig | None = None,
90) -> None:
91 """
92 Export the graph to a Cytoscape JSON file.
94 Args:
95 graph (Graph): The graph to export.
96 output (Path): The output file path.
97 config (CytoscapeStylingConfig | None): Serialization configuration.
98 """
99 logger.info(f"Exporting Cytoscape JSON to: {output}")
100 with open(output, "w+") as f:
101 f.write(create_topology_cytoscape(graph, config))