From d2f9d6ae5103bdd7ebfca363fd0c6de9c33985fd Mon Sep 17 00:00:00 2001 From: romina1601 Date: Thu, 22 Aug 2024 17:47:01 +0300 Subject: [PATCH] VT-23. Add functionality to expand nodes in the ontology --- src/components/GaphView.tsx | 67 +++++++++++-------- src/components/InfoBox.tsx | 1 + .../interfaces/GraphViewInterfaces.tsx | 7 +- .../interfaces/InfoBoxInterfaces.tsx | 5 +- .../interfaces/TreeViewInterfaces.tsx | 2 +- src/handler.ts | 17 ++++- tvb_ext_ontology/handlers.py | 34 +++++++++- 7 files changed, 94 insertions(+), 39 deletions(-) diff --git a/src/components/GaphView.tsx b/src/components/GaphView.tsx index debb2d2..a7fb110 100644 --- a/src/components/GaphView.tsx +++ b/src/components/GaphView.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import ForceGraph2D from 'react-force-graph-2d'; -import { fetchNodeByLabel, fetchNodeConnections } from '../handler'; +import { fetchNodeByLabel, fetchNodeChildren } from '../handler'; import { ISelectedNodeType } from './interfaces/InfoBoxInterfaces'; import { ILinkType, INodeType } from './interfaces/GraphViewInterfaces'; import { ITreeNode } from './interfaces/TreeViewInterfaces'; @@ -33,7 +33,7 @@ export const GraphViewComponent: React.FC = ({ }, [searchLabel]); const buildTree = (currentNode: INodeType): ITreeNode => { - const nodeMap = new Map(); + const nodeMap = new Map(); // Initialize parents and children for all nodes in graph data.nodes.forEach(node => { @@ -45,17 +45,13 @@ export const GraphViewComponent: React.FC = ({ parents: [] }); }); - console.log(nodeMap); const currentTreeNode = nodeMap.get(currentNode.id)!; // Get parents and children data.links.forEach(link => { - console.log('Link: ', link); const sourceNode = nodeMap.get(link.source); const targetNode = nodeMap.get(link.target); - console.log('Source Node: ', sourceNode); - console.log('Target Node: ', targetNode); if (sourceNode && targetNode) { if (link.target === currentNode.id) { @@ -81,38 +77,55 @@ export const GraphViewComponent: React.FC = ({ type: node.type, definition: node.definition, iri: node.iri, + childNodes: node.childNodes, childLinks: node.childLinks, collapsed: false }); - console.log('Node clicked'); + console.log('Node clicked: ', node); + node.collapsed = !node.collapsed; // Build the tree view for the clicked node const tree = buildTree(node); setTreeData(tree); - const connections = await fetchNodeConnections(node.label); - - const nodesById = Object.fromEntries( - data.nodes.map(node => [node.id, node]) - ); - - // link parent/children - data.nodes.forEach(n => { - n.collapsed = n.id !== node.id; - n.childLinks = []; - }); + const connections = await fetchNodeChildren(node.label, node.id); + console.log('GraphView connections: ', connections); + node.childNodes = connections.nodes; + node.childLinks = connections.links; + console.log(node.childNodes); + console.log(node.childLinks); + + const visibleNodes: INodeType[] = []; + const visibleLinks: ILinkType[] = []; + let newNodes: INodeType[] = []; + let newLinks: ILinkType[] = []; + const visitedIds: string[] = []; + + for (const n of data.nodes) { + visitedIds.push(n.id); + } - connections!.links.forEach(link => { - const sourceNode = nodesById[link.source]; - if (sourceNode) { - sourceNode.childLinks!.push(link); + const processNode = (n: INodeType) => { + console.log('PROCESSING NODE: ', n); + if (!visitedIds.includes(n.id)) { + visitedIds.push(n.id); + visibleNodes.push(n); + } + if (!n.collapsed) { + visibleLinks.push(...n.childLinks!); + for (const childNode of n.childNodes!) { + processNode(childNode); + } } else { - console.error( - `Node with id ${link.source} does not exist in nodesById` - ); + return; } - }); - data.nodes = Object.values(nodesById); + }; + + processNode(node); + newNodes = [...data.nodes, ...visibleNodes]; + newLinks = [...data.links, ...visibleLinks]; + console.log(newNodes); + setData({ nodes: newNodes, links: newLinks }); }; // Handle search diff --git a/src/components/InfoBox.tsx b/src/components/InfoBox.tsx index 5b08dbe..93eec77 100644 --- a/src/components/InfoBox.tsx +++ b/src/components/InfoBox.tsx @@ -27,6 +27,7 @@ const InfoBoxComponent: React.FC = ({ return (
+

Concept Details

{selectedNode ? (

Node Information

diff --git a/src/components/interfaces/GraphViewInterfaces.tsx b/src/components/interfaces/GraphViewInterfaces.tsx index 2cd5470..0158da0 100644 --- a/src/components/interfaces/GraphViewInterfaces.tsx +++ b/src/components/interfaces/GraphViewInterfaces.tsx @@ -1,5 +1,5 @@ export interface INodeType { - id: number; + id: string; label: string; type: string; definition: string; @@ -7,11 +7,12 @@ export interface INodeType { x?: number; y?: number; collapsed?: boolean; + childNodes?: INodeType[]; childLinks?: ILinkType[]; } export interface ILinkType { - source: number; - target: number; + source: string; + target: string; type: string; } diff --git a/src/components/interfaces/InfoBoxInterfaces.tsx b/src/components/interfaces/InfoBoxInterfaces.tsx index 4b9ef8d..240e982 100644 --- a/src/components/interfaces/InfoBoxInterfaces.tsx +++ b/src/components/interfaces/InfoBoxInterfaces.tsx @@ -1,10 +1,11 @@ -import { ILinkType } from './GraphViewInterfaces'; +import { ILinkType, INodeType } from './GraphViewInterfaces'; export interface ISelectedNodeType { label: string; type: string; definition: string; iri: string; - childLinks?: ILinkType[], + childNodes?: INodeType[]; + childLinks?: ILinkType[]; collapsed?: boolean; } diff --git a/src/components/interfaces/TreeViewInterfaces.tsx b/src/components/interfaces/TreeViewInterfaces.tsx index aee1d90..e451d77 100644 --- a/src/components/interfaces/TreeViewInterfaces.tsx +++ b/src/components/interfaces/TreeViewInterfaces.tsx @@ -1,5 +1,5 @@ export interface ITreeNode { - id: number; + id: string; label: string; type: string; children: ITreeNode[]; diff --git a/src/handler.ts b/src/handler.ts index a29ccd8..edb3bd1 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -46,21 +46,32 @@ export async function requestAPI( return data; } -export async function fetchNodeByLabel(label: string): Promise { +export async function fetchNodeByLabel(label: string): Promise<{ nodes: INodeType[]; links: ILinkType[] }> { try { const response = await requestAPI(`node?label=${label}`); return response; } catch (error) { console.error(`Error fetching node data: ${error}`); + return { nodes: [], links: []}; } } -export async function fetchNodeConnections(label: string): Promise<{ nodes: INodeType[]; links: ILinkType[] } | null> { +export async function fetchNodeConnections(label: string): Promise<{ nodes: INodeType[]; links: ILinkType[] }> { try { const response = await requestAPI<{ nodes: INodeType[]; links: ILinkType[] }>(`node-connections?label=${label}`); return response; } catch (error) { console.error(`Error fetching node data: ${error}`); - return null; + return { nodes: [], links: [] }; + } +} + +export async function fetchNodeChildren(label: string, id: string): Promise<{ nodes: INodeType[]; links: ILinkType[] }> { + try { + const response = await requestAPI<{ nodes: INodeType[]; links: ILinkType[] }>(`node-children-connections?label=${label}&id=${id}`); + return response; + } catch (error) { + console.error(`Error fetching node data: ${error}`); + return { nodes: [], links: [] }; } } diff --git a/tvb_ext_ontology/handlers.py b/tvb_ext_ontology/handlers.py index 9a1b3b9..236d918 100644 --- a/tvb_ext_ontology/handlers.py +++ b/tvb_ext_ontology/handlers.py @@ -29,7 +29,6 @@ def get(self): return node_data = onto_api.get_node_by_label(label) - print(f'Links: {node_data["links"]}') if not node_data: self.set_status(404) self.finish(json.dumps({"error": f"No data found for label: {label}"})) @@ -52,7 +51,34 @@ def get(self): nodes = onto_api.nodes links = onto_api.edges node_data = {'nodes': nodes, 'links': links} - print(f'Connections links: {node_data["links"]}') + if not node_data: + self.set_status(404) + self.finish(json.dumps({"error": f"No data found for label: {label}"})) + return + + self.set_header("Content-Type", "application/json") + self.finish(json.dumps(node_data)) + + +class NodeChildrenConnectionsHandler(APIHandler): + @tornado.web.authenticated + def get(self): + label = self.get_argument('label', None) + id = self.get_argument('id', None) + if not label: + self.set_status(400) + self.finish(json.dumps({"error": "Missing 'label' parameter"})) + return + if not id: + self.set_status(400) + self.finish(json.dumps({"error": "Missing 'ID' parameter"})) + return + print(f'Label: {label}') + print(f'ID: {id}') + onto_api.expand_node_relationships(label) + # print(onto_api.nodes) + node_data = onto_api.get_child_connections(id) + print(f'node_data: {node_data}') if not node_data: self.set_status(404) self.finish(json.dumps({"error": f"No data found for label: {label}"})) @@ -69,10 +95,12 @@ def setup_handlers(web_app): route_pattern = url_path_join(base_url, "tvb-ext-ontology", "get-example") node_pattern = url_path_join(base_url, "tvb-ext-ontology", "node") node_connections_pattern = url_path_join(base_url, "tvb-ext-ontology", "node-connections") + node_children_connections_pattern = url_path_join(base_url, "tvb-ext-ontology", "node-children-connections") handlers = [ (route_pattern, RouteHandler), (node_pattern, NodeHandler), - (node_connections_pattern, NodeConnectionsHandler) + (node_connections_pattern, NodeConnectionsHandler), + (node_children_connections_pattern, NodeChildrenConnectionsHandler) ] web_app.add_handlers(host_pattern, handlers)