Skip to content

Commit

Permalink
Merge pull request #651 from EBISPOT/feature/628-update-ols-widget
Browse files Browse the repository at this point in the history
update ols widget to take a specified root iri prop
  • Loading branch information
henrietteharmse authored Jun 4, 2024
2 parents 907cddc + 17df046 commit 0ebadb3
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 16 deletions.
30 changes: 26 additions & 4 deletions frontend/dist_widgets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ 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'
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() {
Expand All @@ -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() {
Expand All @@ -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.
4 changes: 2 additions & 2 deletions frontend/dist_widgets/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
38 changes: 37 additions & 1 deletion frontend/src/pages/ontologies/entities/EntityTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ import {
showCounts,
showObsolete,
showSiblings,
setSpecificRootIri,
getEntity,
} from "../ontologiesSlice";

export default function EntityTree({
ontology,
entityType,
specifiedRootIri,
selectedEntity,
lang,
onNavigateToEntity,
Expand All @@ -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;
Expand Down Expand Up @@ -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({
Expand All @@ -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]);

const prevNodeChildrenRef = useRef(nodeChildren);

const haveRelevantPartsChanged = (prev, next) => {
Expand Down Expand Up @@ -350,7 +386,7 @@ export default function EntityTree({
) : null}
<div className="lg:col-span-1 flex flex-col bg-white px-2 mb-2 rounded-lg">
{entityType === "classes" &&
ontology.getPreferredRoots().length > 0 && (
ontology.getPreferredRoots().length > 0 && !specifiedRootIri && (
<div className="mb-2">
<FormControl>
<RadioGroup
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/pages/ontologies/entities/createTreeFromEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>, 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(
Expand Down
16 changes: 13 additions & 3 deletions frontend/src/pages/ontologies/ontologiesSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ;
Expand Down Expand Up @@ -70,6 +71,7 @@ const initialState: OntologiesState = {
displaySiblings: false,
displayCounts: true,
errorMessage: "",
specificRootIri: "",
};

export const resetTreeContent = createAction("ontologies_tree_reset_content");
Expand All @@ -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<string>("ontologies_set_specific_root_iri");

export const getOntology = createAsyncThunk(
"ontologies_ontology",
Expand Down Expand Up @@ -171,9 +174,11 @@ export const getEntity = createAsyncThunk(
{
ontologyId,
entityIri,
apiUrl,
}: {
ontologyId: string;
entityIri: string;
apiUrl?: string;
},
{ rejectWithValue }
) => {
Expand All @@ -183,7 +188,7 @@ export const getEntity = createAsyncThunk(
let path = "";
try {
path = `api/v2/ontologies/${ontologyId}/entities/${doubleEncodedTermUri}`;
const entityJsonProperties = await get<any>(path);
const entityJsonProperties = await get<any>(path, undefined, apiUrl);
return thingFromJsonProperties(entityJsonProperties);
} catch (error: any) {
return rejectWithValue(`Error accessing: ${path}; ${error.message}`);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -607,7 +613,8 @@ const ontologiesSlice = createSlice({
...(orphanedIndividuals || []),
],
state.preferredRoots,
state.ontology!
state.ontology!,
state.specificRootIri
);

state.rootNodes = rootNodes;
Expand Down Expand Up @@ -726,6 +733,9 @@ const ontologiesSlice = createSlice({
builder.addCase(hideCounts, (state: OntologiesState) => {
state.displayCounts = false;
});
builder.addCase(setSpecificRootIri, (state: OntologiesState, action: PayloadAction<string>) => {
state.specificRootIri = action.payload;
});
builder.addCase(
openNode,
(state: OntologiesState, action: PayloadAction<TreeNode>) => {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/widgets/EntityTreeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface EntityTreeWidgetProps {
iri?: string;
ontologyId: string;
apiUrl: string;
specifiedRootIri?: string;
entityType?: "entities" | "classes" | "properties" | "individuals";
lang?: string;
onNavigateToEntity?: (ontology: Ontology, entity: Entity) => void;
Expand All @@ -33,6 +34,7 @@ function EntityTreeWidget(props: EntityTreeWidgetProps) {
ontologyId={props.ontologyId}
iri={props.iri}
apiUrl={props.apiUrl}
specifiedRootIri={props.specifiedRootIri}
/>
</Provider>
);
Expand Down Expand Up @@ -64,6 +66,7 @@ function EntityTreeWidgetInner(props: EntityTreeWidgetProps) {
<EntityTree
ontology={ontology}
entityType={entityType}
specifiedRootIri={props.specifiedRootIri}
lang={lang}
onNavigateToEntity={onNavigateToEntity}
onNavigateToOntology={onNavigateToOntology}
Expand Down

0 comments on commit 0ebadb3

Please sign in to comment.