- Implemented `owl_to_mermaid.py` to convert OWL/Turtle files into Mermaid class diagrams. - Implemented `owl_to_plantuml.py` to convert OWL/Turtle files into PlantUML class diagrams. - Added two new PlantUML files for custodian multi-aspect diagrams.
192 lines
4.6 KiB
TypeScript
192 lines
4.6 KiB
TypeScript
/**
|
|
* React Hook for Graph Data Management
|
|
* Processes parsed RDF data for D3.js visualization
|
|
*/
|
|
|
|
import { useState, useCallback, useMemo } from 'react';
|
|
import {
|
|
extractNodeTypes,
|
|
extractPredicates,
|
|
getLinkCounts,
|
|
getNodeCounts,
|
|
} from '@/lib/rdf/graph-utils';
|
|
import type { GraphData, GraphNode, GraphLink } from '@/types/rdf';
|
|
|
|
export interface GraphFilters {
|
|
nodeTypes: Set<string>;
|
|
predicates: Set<string>;
|
|
searchTerm: string;
|
|
}
|
|
|
|
interface UseGraphDataReturn {
|
|
// Data
|
|
nodes: GraphNode[];
|
|
links: GraphLink[];
|
|
filteredNodes: GraphNode[];
|
|
filteredLinks: GraphLink[];
|
|
|
|
// Metadata
|
|
nodeTypes: string[];
|
|
predicates: string[];
|
|
linkCounts: Record<string, number>;
|
|
nodeCounts: Record<string, number>;
|
|
|
|
// Filters
|
|
filters: GraphFilters;
|
|
setFilters: (filters: Partial<GraphFilters>) => void;
|
|
resetFilters: () => void;
|
|
|
|
// Selection
|
|
selectedNode: GraphNode | null;
|
|
setSelectedNode: (node: GraphNode | null) => void;
|
|
|
|
// Data loading
|
|
loadGraphData: (data: GraphData) => void;
|
|
clearGraphData: () => void;
|
|
|
|
// Stats
|
|
stats: {
|
|
totalNodes: number;
|
|
totalLinks: number;
|
|
filteredNodeCount: number;
|
|
filteredLinkCount: number;
|
|
};
|
|
}
|
|
|
|
const DEFAULT_FILTERS: GraphFilters = {
|
|
nodeTypes: new Set(),
|
|
predicates: new Set(),
|
|
searchTerm: '',
|
|
};
|
|
|
|
export function useGraphData(): UseGraphDataReturn {
|
|
const [graphData, setGraphData] = useState<GraphData | null>(null);
|
|
const [filters, setFiltersState] = useState<GraphFilters>(DEFAULT_FILTERS);
|
|
const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);
|
|
|
|
// Extract metadata from graph data
|
|
const nodeTypes = useMemo(
|
|
() => (graphData ? extractNodeTypes(graphData) : []),
|
|
[graphData]
|
|
);
|
|
|
|
const predicates = useMemo(
|
|
() => (graphData ? extractPredicates(graphData) : []),
|
|
[graphData]
|
|
);
|
|
|
|
const linkCounts = useMemo(
|
|
() => (graphData ? getLinkCounts(graphData) : {}),
|
|
[graphData]
|
|
);
|
|
|
|
const nodeCounts = useMemo(
|
|
() => (graphData ? getNodeCounts(graphData) : {}),
|
|
[graphData]
|
|
);
|
|
|
|
// Get raw nodes and links
|
|
const nodes = graphData?.nodes || [];
|
|
const links = graphData?.links || [];
|
|
|
|
// Apply filters to nodes
|
|
const filteredNodes = useMemo(() => {
|
|
if (!graphData) return [];
|
|
|
|
let result = nodes;
|
|
|
|
// Filter by node type
|
|
if (filters.nodeTypes.size > 0) {
|
|
result = result.filter((node) => filters.nodeTypes.has(node.type));
|
|
}
|
|
|
|
// Filter by search term
|
|
if (filters.searchTerm) {
|
|
const term = filters.searchTerm.toLowerCase();
|
|
result = result.filter(
|
|
(node) =>
|
|
node.id.toLowerCase().includes(term) ||
|
|
node.label?.toLowerCase().includes(term)
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}, [graphData, nodes, filters]);
|
|
|
|
// Apply filters to links
|
|
const filteredLinks = useMemo(() => {
|
|
if (!graphData) return [];
|
|
|
|
const nodeIds = new Set(filteredNodes.map((n) => n.id));
|
|
let result = links.filter((link) => {
|
|
const sourceId = typeof link.source === 'string' ? link.source : link.source.id;
|
|
const targetId = typeof link.target === 'string' ? link.target : link.target.id;
|
|
return nodeIds.has(sourceId) && nodeIds.has(targetId);
|
|
});
|
|
|
|
// Filter by predicate
|
|
if (filters.predicates.size > 0) {
|
|
result = result.filter((link) => filters.predicates.has(link.predicate));
|
|
}
|
|
|
|
return result;
|
|
}, [graphData, links, filteredNodes, filters.predicates]);
|
|
|
|
// Load graph data
|
|
const loadGraphData = useCallback((data: GraphData) => {
|
|
setGraphData(data);
|
|
setSelectedNode(null);
|
|
// Reset filters when loading new data
|
|
setFiltersState(DEFAULT_FILTERS);
|
|
}, []);
|
|
|
|
// Clear graph data
|
|
const clearGraphData = useCallback(() => {
|
|
setGraphData(null);
|
|
setSelectedNode(null);
|
|
setFiltersState(DEFAULT_FILTERS);
|
|
}, []);
|
|
|
|
// Update filters
|
|
const setFilters = useCallback((newFilters: Partial<GraphFilters>) => {
|
|
setFiltersState((prev) => ({
|
|
...prev,
|
|
...newFilters,
|
|
}));
|
|
}, []);
|
|
|
|
// Reset filters
|
|
const resetFilters = useCallback(() => {
|
|
setFiltersState(DEFAULT_FILTERS);
|
|
}, []);
|
|
|
|
// Stats
|
|
const stats = useMemo(
|
|
() => ({
|
|
totalNodes: nodes.length,
|
|
totalLinks: links.length,
|
|
filteredNodeCount: filteredNodes.length,
|
|
filteredLinkCount: filteredLinks.length,
|
|
}),
|
|
[nodes.length, links.length, filteredNodes.length, filteredLinks.length]
|
|
);
|
|
|
|
return {
|
|
nodes,
|
|
links,
|
|
filteredNodes,
|
|
filteredLinks,
|
|
nodeTypes,
|
|
predicates,
|
|
linkCounts,
|
|
nodeCounts,
|
|
filters,
|
|
setFilters,
|
|
resetFilters,
|
|
selectedNode,
|
|
setSelectedNode,
|
|
loadGraphData,
|
|
clearGraphData,
|
|
stats,
|
|
};
|
|
}
|