From cc4e380c113926e93fa80c6f7b5950762ea1e398 Mon Sep 17 00:00:00 2001 From: haider Date: Tue, 23 Apr 2024 14:06:42 +0100 Subject: [PATCH 1/4] - Add specifiedRootUri in EntityTree.tsx --- .../pages/ontologies/entities/EntityTree.tsx | 42 ++++++++++++++++--- frontend/src/widgets/EntityTreeWidget.tsx | 3 ++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/ontologies/entities/EntityTree.tsx b/frontend/src/pages/ontologies/entities/EntityTree.tsx index 0c0790580..9ce88d416 100644 --- a/frontend/src/pages/ontologies/entities/EntityTree.tsx +++ b/frontend/src/pages/ontologies/entities/EntityTree.tsx @@ -31,11 +31,14 @@ import { showCounts, showObsolete, showSiblings, + setSpecificRootIri, + getEntity, } from "../ontologiesSlice"; export default function EntityTree({ ontology, entityType, + specifiedRootIri, selectedEntity, lang, onNavigateToEntity, @@ -45,6 +48,7 @@ export default function EntityTree({ ontology: Ontology; selectedEntity?: Entity; entityType: "entities" | "classes" | "properties" | "individuals"; + specifiedRootIri?: string; lang: string; onNavigateToEntity: (ontology: Ontology, entity: Entity) => void; onNavigateToOntology: (ontologyId: string, entity: Entity) => void; @@ -136,6 +140,19 @@ export default function EntityTree({ }) ); return () => promise.abort(); // component was unmounted + } else if (specifiedRootIri) { + let promise = dispatch( + getAncestors({ + ontologyId: ontology.getOntologyId(), + entityType, + entityIri: specifiedRootIri, + lang, + showObsoleteEnabled, + showSiblingsEnabled, + apiUrl, + }) + ); + return () => promise.abort(); // component was unmounted } else { let promise = dispatch( getRootEntities({ @@ -157,8 +174,27 @@ export default function EntityTree({ preferredRoots, lang, showObsoleteEnabled, + specifiedRootIri, ]); + useEffect(() => { + if (specifiedRootIri) { + dispatch( + getEntity({ + ontologyId: ontology.getOntologyId(), + entityIri: specifiedRootIri, + apiUrl: apiUrl, + }) + ); + } + }, [dispatch, specifiedRootIri, ontology.getOntologyId(),]); + + useEffect(() => { + if (specifiedRootIri) { + dispatch(setSpecificRootIri(specifiedRootIri)); + } + }, [dispatch, specifiedRootIri]); + useEffect(() => { let nodesMissingChildren: string[] = manuallyExpandedNodes.filter( (absoluteIdentity) => @@ -174,10 +210,6 @@ export default function EntityTree({ ), ]; } - // console.log( - // "!!!! Getting missing node children: " + - // JSON.stringify(nodesMissingChildren) - // ); let promises: any = []; for (let absId of nodesMissingChildren) { @@ -337,7 +369,7 @@ export default function EntityTree({ ) : null}
{entityType === "classes" && - ontology.getPreferredRoots().length > 0 && ( + ontology.getPreferredRoots().length > 0 && !specifiedRootIri && (
void; @@ -33,6 +34,7 @@ function EntityTreeWidget(props: EntityTreeWidgetProps) { ontologyId={props.ontologyId} iri={props.iri} apiUrl={props.apiUrl} + specifiedRootIri={props.specifiedRootIri} /> ); @@ -64,6 +66,7 @@ function EntityTreeWidgetInner(props: EntityTreeWidgetProps) { Date: Tue, 23 Apr 2024 14:07:47 +0100 Subject: [PATCH 2/4] - Add specifiedRootUri in createTreeFromEntities.ts --- .../ontologies/entities/createTreeFromEntities.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/ontologies/entities/createTreeFromEntities.ts b/frontend/src/pages/ontologies/entities/createTreeFromEntities.ts index 5d2c8a26d..46a5fbc3a 100644 --- a/frontend/src/pages/ontologies/entities/createTreeFromEntities.ts +++ b/frontend/src/pages/ontologies/entities/createTreeFromEntities.ts @@ -6,16 +6,19 @@ import { TreeNode } from "../ontologiesSlice"; export default function createTreeFromEntities( entities: Entity[], preferredRoots: boolean, - ontology: Ontology + ontology: Ontology, + specificRootIri: string ): { allNodes:TreeNode[], rootNodes: TreeNode[]; automaticallyExpandedNodes:Set, nodeChildren: any } { let { rootEntities, parentToChildRelations } = extractEntityHierarchy(entities); - console.log('rootEntities') - console.dir(rootEntities) - console.log('parentToChildRelations') - console.dir(parentToChildRelations) + if(specificRootIri) { + let specificRootEntity = entities.find(entity => entity.getIri() === specificRootIri) + if (specificRootEntity) { + rootEntities = [specificRootEntity]; + } + } - if (preferredRoots) { + if (preferredRoots && !specificRootIri) { let preferred = ontology.getPreferredRoots(); if (preferred.length > 0) { let preferredRootEntities = preferred.map( From 910d5d6ece2508170187be778f5dee8e17591097 Mon Sep 17 00:00:00 2001 From: haider Date: Tue, 23 Apr 2024 14:09:57 +0100 Subject: [PATCH 3/4] - Add specifiedRootUri ontology slice state --- frontend/src/pages/ontologies/ontologiesSlice.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/ontologies/ontologiesSlice.ts b/frontend/src/pages/ontologies/ontologiesSlice.ts index 9314b7fe4..91baabbf5 100644 --- a/frontend/src/pages/ontologies/ontologiesSlice.ts +++ b/frontend/src/pages/ontologies/ontologiesSlice.ts @@ -34,6 +34,7 @@ export interface OntologiesState { displaySiblings: boolean; displayCounts: boolean; errorMessage: string; + specificRootIri: string; } export interface TreeNode { absoluteIdentity: string; // the IRIs of this node and its ancestors delimited by a ; @@ -70,6 +71,7 @@ const initialState: OntologiesState = { displaySiblings: false, displayCounts: true, errorMessage: "", + specificRootIri: "", }; export const resetTreeContent = createAction("ontologies_tree_reset_content"); @@ -95,6 +97,7 @@ export const hideSiblings = createAction("ontologies_hide_siblings"); export const showCounts = createAction("ontologies_show_counts"); export const hideCounts = createAction("ontologies_hide_counts"); +export const setSpecificRootIri = createAction("ontologies_set_specific_root_iri"); export const getOntology = createAsyncThunk( "ontologies_ontology", @@ -171,9 +174,11 @@ export const getEntity = createAsyncThunk( { ontologyId, entityIri, + apiUrl, }: { ontologyId: string; entityIri: string; + apiUrl?: string; }, { rejectWithValue } ) => { @@ -183,7 +188,7 @@ export const getEntity = createAsyncThunk( let path = ""; try { path = `api/v2/ontologies/${ontologyId}/entities/${doubleEncodedTermUri}`; - const entityJsonProperties = await get(path); + const entityJsonProperties = await get(path, undefined, apiUrl); return thingFromJsonProperties(entityJsonProperties); } catch (error: any) { return rejectWithValue(`Error accessing: ${path}; ${error.message}`); @@ -550,7 +555,8 @@ const ontologiesSlice = createSlice({ createTreeFromEntities( [state.entity!, ...action.payload], state.preferredRoots, - state.ontology! + state.ontology!, + state.specificRootIri ); state.rootNodes = rootNodes; state.nodeChildren = nodeChildren; @@ -607,7 +613,8 @@ const ontologiesSlice = createSlice({ ...(orphanedIndividuals || []), ], state.preferredRoots, - state.ontology! + state.ontology!, + state.specificRootIri ); state.rootNodes = rootNodes; @@ -726,6 +733,9 @@ const ontologiesSlice = createSlice({ builder.addCase(hideCounts, (state: OntologiesState) => { state.displayCounts = false; }); + builder.addCase(setSpecificRootIri, (state: OntologiesState, action: PayloadAction) => { + state.specificRootIri = action.payload; + }); builder.addCase( openNode, (state: OntologiesState, action: PayloadAction) => { From 823820d5b38ef2485963f96a7c5a7218c62de244 Mon Sep 17 00:00:00 2001 From: haider Date: Tue, 23 Apr 2024 14:10:53 +0100 Subject: [PATCH 4/4] - Update entity tree widget version and README.md --- frontend/dist_widgets/README.md | 30 ++++++++++++++++++++++++++---- frontend/dist_widgets/package.json | 4 ++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/frontend/dist_widgets/README.md b/frontend/dist_widgets/README.md index c89f6f756..dec0d7e92 100644 --- a/frontend/dist_widgets/README.md +++ b/frontend/dist_widgets/README.md @@ -19,7 +19,7 @@ After installation, you can use OLS4 Widgets in your project by including the ne initializing the widget with a simple javaScript command. Here's a quick example of how to display the chebi tree in a **React** application: -1. Import the dependencies in the js file you want to render the component: +## 1. Import the dependencies in the js file you want to render the component: ```javascript import '@ebi-ols/ols4-widgets/treestyles.css' @@ -27,7 +27,7 @@ import { useEffect, useRef } from 'react'; import { createEntityTree } from '@ebi-ols/ols4-widgets/ols4_widgets'; ``` -2. Create the function to render the tree: +## 2. Create the function to render the tree: ```javascript function EntityTree() { @@ -49,7 +49,24 @@ function EntityTree() { ``` **NOTE:** The main point to notice here is the ontologyId and the apiUrl. The ontologyId is the id of the ontology you want to display and the apiUrl is the base url of the OLS4 API. -3. Add the element where you want the tree to appear in your HTML file: +### Partial rendering of the tree + +You can specify a term uri e.g. water (http://purl.obolibrary.org/obo/CHEBI_15377) as a prop to the `createEntityTree` function to render a partial tree. If you specify this prop i.e. `specifiedRootIri`, +the tree will render starting from the specified term. + +```javascript +useEffect(() => { + if(div.current) { + createEntityTree({ + ontologyId: "chebi", + specifiedRootIri: "http://purl.obolibrary.org/obo/CHEBI_15377", + apiUrl: "https://www.ebi.ac.uk/ols4/" + }, div.current); + } + }, [div]) +``` + +## 3. Add the element where you want the tree to appear in your HTML file: ```javascript function App() { @@ -60,12 +77,17 @@ return ( ); } ``` -### Example of the rendered tree +### Example of the complete ontology rendered tree ![chebi-tree-render](https://github.com/EBISPOT/ols4/assets/13108541/b9e14c70-6be0-4007-8311-91605087d5ad) +### Example of the partial ontology rendered tree by providing a specified uri of water (http://purl.obolibrary.org/obo/CHEBI_15377) + +![Screenshot 2024-04-23 at 13 25 11](https://github.com/EBISPOT/ols4/assets/13108541/78a09a8b-8b25-49ff-8b2e-7469c811eaee) + ## Features - Easy to integrate with any web application. +- Full and partial ontology tree rendering. - Lightweight and fast. \ No newline at end of file diff --git a/frontend/dist_widgets/package.json b/frontend/dist_widgets/package.json index f394e9fda..ed3d3a93c 100644 --- a/frontend/dist_widgets/package.json +++ b/frontend/dist_widgets/package.json @@ -1,7 +1,7 @@ { "name": "@ebi-ols/ols4-widgets", - "version": "1.0.2", + "version": "1.1.0", "type": "module", "main": "ols4_widgets.js", "types": "manually_maintained_types.d.ts" -} +} \ No newline at end of file