glam/frontend/src/hooks/useGraphData.ts
kempersc 2761857b0d Add scripts for converting OWL/Turtle ontology to Mermaid and PlantUML diagrams
- 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.
2025-11-22 23:01:13 +01:00

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,
};
}