visualize.py•8.66 kB
#!/usr/bin/env python3
# MEMORY_ANCHOR: knowledge_graph_visualization
"""Knowledge Graph Visualization
This script visualizes the knowledge graph generated by generator.py.
Maturity: beta
Why:
Visualization helps developers understand the relationships between
components, making it easier to navigate the codebase and identify
dependencies or potential issues.
"""
import json
import argparse
import networkx as nx
import matplotlib.pyplot as plt
from pathlib import Path
import matplotlib.patches as mpatches
# Define paths
BASE_DIR = Path(__file__).parent.parent
GRAPH_FILE = BASE_DIR / "knowledge_graph" / "graph.json"
OUTPUT_DIR = BASE_DIR / "knowledge_graph" / "visualizations"
# Define node colors by type
NODE_COLORS = {
"component": "#4285F4", # Blue
"file": "#34A853", # Green
"function": "#FBBC05", # Yellow
"class": "#EA4335", # Red
"error": "#FF6D00", # Orange
"solution": "#00C853", # Green
"pattern": "#AA00FF", # Purple
"concept": "#6200EA" # Deep Purple
}
# Define edge colors by type
EDGE_COLORS = {
"depends_on": "#4285F4", # Blue
"imports": "#34A853", # Green
"calls": "#FBBC05", # Yellow
"inherits_from": "#EA4335", # Red
"contains": "#9E9E9E", # Grey
"causes": "#FF6D00", # Orange
"solves": "#00C853", # Green
"implements": "#AA00FF", # Purple
"references": "#6200EA" # Deep Purple
}
def load_knowledge_graph(file_path):
"""Load the knowledge graph from a file."""
try:
with open(file_path, 'r') as f:
return json.load(f)
except Exception as e:
print(f"Error loading knowledge graph: {e}")
return None
def create_graph(knowledge_graph):
"""Create a NetworkX graph from the knowledge graph."""
G = nx.DiGraph()
# Add nodes
for node in knowledge_graph.get("nodes", []):
node_id = node.get("id")
node_type = node.get("type")
node_name = node.get("name", "")
if node_id:
G.add_node(node_id,
type=node_type,
name=node_name,
maturity=node.get("maturity"),
description=node.get("description", ""))
# Add edges
for edge in knowledge_graph.get("edges", []):
source = edge.get("source")
target = edge.get("target")
edge_type = edge.get("type")
if source and target:
G.add_edge(source, target, type=edge_type)
return G
def visualize_full_graph(G, output_file):
"""Visualize the full knowledge graph."""
plt.figure(figsize=(20, 20))
# Set node colors based on type
node_colors = [NODE_COLORS.get(G.nodes[node].get("type"), "#9E9E9E") for node in G.nodes]
# Set edge colors based on type
edge_colors = [EDGE_COLORS.get(G.edges[edge].get("type"), "#9E9E9E") for edge in G.edges]
# Create layout
pos = nx.spring_layout(G, k=0.15, iterations=50)
# Draw nodes
nx.draw_networkx_nodes(G, pos, node_size=300, node_color=node_colors, alpha=0.8)
# Draw edges
nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, edge_color=edge_colors, arrows=True, arrowsize=10)
# Draw labels
nx.draw_networkx_labels(G, pos, font_size=8, font_family="sans-serif")
# Create legend for node types
node_patches = [mpatches.Patch(color=color, label=node_type)
for node_type, color in NODE_COLORS.items()]
plt.legend(handles=node_patches, title="Node Types", loc="upper left", bbox_to_anchor=(1, 1))
plt.axis("off")
plt.title("Knowledge Graph Visualization")
plt.tight_layout()
plt.savefig(output_file, dpi=300, bbox_inches="tight")
plt.close()
print(f"Full graph visualization saved to {output_file}")
def visualize_component_dependencies(G, output_file):
"""Visualize component dependencies."""
# Create a subgraph with only component nodes and depends_on edges
component_nodes = [node for node in G.nodes if G.nodes[node].get("type") == "component"]
SG = G.subgraph(component_nodes)
# Filter edges to only include depends_on
depends_on_edges = [(u, v) for u, v, data in SG.edges(data=True) if data.get("type") == "depends_on"]
# Create a new graph with only the filtered edges
DG = nx.DiGraph()
for node in component_nodes:
DG.add_node(node, **G.nodes[node])
for u, v in depends_on_edges:
DG.add_edge(u, v, type="depends_on")
plt.figure(figsize=(15, 15))
# Set node colors
node_colors = [NODE_COLORS.get("component") for _ in DG.nodes]
# Create layout
pos = nx.spring_layout(DG, k=0.3, iterations=50)
# Draw nodes
nx.draw_networkx_nodes(DG, pos, node_size=500, node_color=node_colors, alpha=0.8)
# Draw edges
nx.draw_networkx_edges(DG, pos, width=2, alpha=0.7, edge_color=EDGE_COLORS.get("depends_on"),
arrows=True, arrowsize=15)
# Draw labels with just the component name (without the "component:" prefix)
labels = {node: G.nodes[node].get("name", "") for node in DG.nodes}
nx.draw_networkx_labels(DG, pos, labels=labels, font_size=10, font_family="sans-serif")
plt.axis("off")
plt.title("Component Dependencies")
plt.tight_layout()
plt.savefig(output_file, dpi=300, bbox_inches="tight")
plt.close()
print(f"Component dependencies visualization saved to {output_file}")
def visualize_error_solutions(G, output_file):
"""Visualize errors and their solutions."""
# Create a subgraph with only error and solution nodes
error_nodes = [node for node in G.nodes if G.nodes[node].get("type") == "error"]
solution_nodes = [node for node in G.nodes if G.nodes[node].get("type") == "solution"]
# Also include components that cause errors
component_nodes = []
for error in error_nodes:
for u, v in G.in_edges(error):
if G.nodes[u].get("type") == "component":
component_nodes.append(u)
nodes = error_nodes + solution_nodes + component_nodes
SG = G.subgraph(nodes)
plt.figure(figsize=(15, 15))
# Set node colors based on type
node_colors = [NODE_COLORS.get(SG.nodes[node].get("type"), "#9E9E9E") for node in SG.nodes]
# Set edge colors based on type
edge_colors = [EDGE_COLORS.get(SG.edges[edge].get("type"), "#9E9E9E") for edge in SG.edges]
# Create layout
pos = nx.spring_layout(SG, k=0.3, iterations=50)
# Draw nodes
nx.draw_networkx_nodes(SG, pos, node_size=400, node_color=node_colors, alpha=0.8)
# Draw edges
nx.draw_networkx_edges(SG, pos, width=1.5, alpha=0.7, edge_color=edge_colors,
arrows=True, arrowsize=12)
# Draw labels
labels = {node: SG.nodes[node].get("name", "").split(":")[0] for node in SG.nodes}
nx.draw_networkx_labels(SG, pos, labels=labels, font_size=9, font_family="sans-serif")
# Create legend for node types
node_patches = [mpatches.Patch(color=NODE_COLORS.get(t), label=t)
for t in ["component", "error", "solution"]]
plt.legend(handles=node_patches, title="Node Types", loc="upper left", bbox_to_anchor=(1, 1))
plt.axis("off")
plt.title("Errors and Solutions")
plt.tight_layout()
plt.savefig(output_file, dpi=300, bbox_inches="tight")
plt.close()
print(f"Error-solution visualization saved to {output_file}")
def main():
parser = argparse.ArgumentParser(description="Visualize the knowledge graph")
parser.add_argument("--input", help="Input graph file", default=str(GRAPH_FILE))
parser.add_argument("--output-dir", help="Output directory", default=str(OUTPUT_DIR))
args = parser.parse_args()
graph_file = Path(args.input)
output_dir = Path(args.output_dir)
# Create output directory if it doesn't exist
output_dir.mkdir(parents=True, exist_ok=True)
# Load knowledge graph
knowledge_graph = load_knowledge_graph(graph_file)
if not knowledge_graph:
print("Failed to load knowledge graph.")
return
# Create NetworkX graph
G = create_graph(knowledge_graph)
# Generate visualizations
visualize_full_graph(G, output_dir / "full_graph.png")
visualize_component_dependencies(G, output_dir / "component_dependencies.png")
visualize_error_solutions(G, output_dir / "error_solutions.png")
print("All visualizations generated successfully.")
if __name__ == "__main__":
main()