From 342eff116a367eb9620dd3902f069cabd4f5dabb Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 17 May 2024 10:45:06 +0530 Subject: [PATCH 1/4] ESCKAN-43 minor types update --- src/components/FiltersDropdowns.tsx | 3 +-- src/components/common/Types.ts | 4 +++- src/constants.tsx | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 src/constants.tsx diff --git a/src/components/FiltersDropdowns.tsx b/src/components/FiltersDropdowns.tsx index 5c26229..a877f19 100644 --- a/src/components/FiltersDropdowns.tsx +++ b/src/components/FiltersDropdowns.tsx @@ -2,7 +2,7 @@ import {Box} from "@mui/material"; import CustomFilterDropdown from "./common/CustomFilterDropdown.tsx"; import React, {useMemo} from "react"; import {Filters, useDataContext} from "../context/DataContext.ts"; -import {Option} from "./common/Types.ts"; +import { Option } from "./common/Types.ts"; import { getUniqueApinatomies, getUniqueOrgans, getUniqueOrigins, @@ -17,7 +17,6 @@ import { searchSpecies, searchVias } from "../services/searchService.ts"; - interface FilterConfig { id: keyof Filters; placeholder: string; diff --git a/src/components/common/Types.ts b/src/components/common/Types.ts index 038b93f..a293cc5 100644 --- a/src/components/common/Types.ts +++ b/src/components/common/Types.ts @@ -1,4 +1,5 @@ import { KnowledgeStatement } from "../../models/explorer"; +import { Filters } from "../../context/DataContext.ts"; export type OptionDetail = { title: string; // What to display as the title/label for the property. @@ -31,4 +32,5 @@ export interface HierarchicalItem { export type PhenotypeDetail = { label: string; color: string; -}; \ No newline at end of file + ksId: string; +}; diff --git a/src/constants.tsx b/src/constants.tsx new file mode 100644 index 0000000..2c7591b --- /dev/null +++ b/src/constants.tsx @@ -0,0 +1 @@ +export const OTHER_LABEL = 'other' \ No newline at end of file From 583165881961ac4c03be59de25b9ee5c27884e52 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 17 May 2024 10:46:04 +0530 Subject: [PATCH 2/4] ESCKAN-43 - integrate summary filters --- src/components/Connections.tsx | 74 ++------------------ src/components/SummaryFiltersDropdown.tsx | 85 +++++++++++++++++++++++ src/services/searchService.ts | 11 ++- src/services/summaryHeatmapService.ts | 16 +++-- 4 files changed, 112 insertions(+), 74 deletions(-) create mode 100644 src/components/SummaryFiltersDropdown.tsx diff --git a/src/components/Connections.tsx b/src/components/Connections.tsx index 4e2c20d..db388b4 100644 --- a/src/components/Connections.tsx +++ b/src/components/Connections.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from "react"; import { Box, Chip, TextField, Typography } from "@mui/material"; import { ArrowRightIcon } from "./icons"; import { vars } from "../theme/variables"; -import { HierarchicalItem, ISubConnections, Option, SummaryType, ksMapType } from "./common/Types"; +import { HierarchicalItem, ISubConnections, PhenotypeDetail, SummaryType, ksMapType } from "./common/Types"; import { useDataContext } from "../context/DataContext.ts"; import { calculateSecondaryConnections, @@ -12,13 +12,13 @@ import { getYAxisNode } from "../services/summaryHeatmapService.ts"; import { getYAxis, getKnowledgeStatementAndCount } from "../services/heatmapService.ts"; -import CustomFilterDropdown from "./common/CustomFilterDropdown"; import SummaryHeader from "./connections/SummaryHeader"; import Details from "./connections/Details.tsx"; import SummaryInstructions from "./connections/SummaryInstructions.tsx"; import PhenotypeLegend from "./connections/PhenotypeLegend.tsx"; import HeatmapGrid from "./common/Heatmap.tsx"; import { Organ } from "../models/explorer.ts"; +import SummaryFiltersDropdown from "./SummaryFiltersDropdown.tsx"; const { gray700, gray600A, gray100 } = vars; @@ -38,28 +38,6 @@ const styles = { } } -type PhenotypeDetail = { - label: string; - color: string; -}; -const phenotype: PhenotypeDetail[] = [ - { - label: 'Sympathetic', - color: '#9B18D8' - }, - { - label: 'Parasympathetic', - color: '#2C2CCE' - }, - { - label: 'Sensory', - color: '#DC6803' - }, - { - label: 'Motor', - color: '#EAAA08' - } -] function Connections() { @@ -81,8 +59,7 @@ function Connections() { const viasConnection = getAllViasFromConnections(selectedConnectionSummary.connections); const viasStatement = convertViaToString(Object.values(viasConnection)) const totalConnectionCount = Object.keys(selectedConnectionSummary.connections).length; - const phenotypes = getAllPhenotypes(selectedConnectionSummary.connections); - const [phenotypeFilters, setPhenotypeFilters] = useState(phenotype); + const [phenotypeFilters, setPhenotypeFilters] = useState([]); useEffect(() => { if (!checkIfConnectionSummaryIsEmpty(selectedConnectionSummary)) { @@ -90,7 +67,8 @@ function Connections() { const phenotypeColors: string[] = generatePhenotypeColors(phenotypes.length) setPhenotypeFilters(phenotypes.map((phenotype, index) => ({ label: phenotype, - color: phenotypeColors[index] + color: phenotypeColors[index], + ksId: '' }))) } @@ -98,27 +76,6 @@ function Connections() { const nerves = getNerveFilters(viasConnection, majorNerves); - const searchPhenotypeFilter = (searchValue: string): Option[] => { - console.log(searchValue) - const searchedPhenotype = phenotypes - return searchedPhenotype.map((phenotype) => ({ - id: phenotype, - label: phenotype, - group: 'Phenotype', - content: [] - })); - } - - const searchNerveFilter = (searchValue: string): Option[] => { - console.log(searchValue) - const searchedNerve = Object.keys(nerves) - return searchedNerve.map((nerve) => ({ - id: nerve, - label: nerves[nerve], - group: 'Nerve', - content: [] - })); - } useEffect(() => { if (!checkIfConnectionSummaryIsEmpty(selectedConnectionSummary) && phenotypeFilters) { @@ -230,26 +187,7 @@ function Connections() { Summary map shows the connections of the selected connection origin and end organ with phenotypes. Select individual squares to view the details of each connections. - - searchPhenotypeFilter(searchValue)} - onSelect={() => {}} - /> - searchNerveFilter(searchValue)} - onSelect={() => {}} - /> - + { + const { summaryFilters, setSummaryFilters, knowledgeStatements, organs } = useDataContext(); + + const convertNervesToOptions = (nerves: { [key: string]: string }): Option[] => { + return Object.keys(nerves).map(nerve => ({ + id: nerve, + label: nerves[nerve], + group: 'Nerve', + content: [] + })); + } + const convertPhenotypesToOptions = (phenotypes: PhenotypeDetail[]): Option[] => { + // filter the phenotype where label is other + return phenotypes.map(phenotype => ({ + id: phenotype.label, + label: phenotype.label, + group: 'Phenotype', + content: [] + })).filter(phenotype => phenotype.label !== OTHER_LABEL); + } + + const phenotypeOptions = useMemo(() => convertPhenotypesToOptions(phenotypes), [phenotypes]); + const nerveOptions = useMemo(() => convertNervesToOptions(nerves), [nerves]); + + const handleSelect = (filterKey: keyof typeof summaryFilters, selectedOptions: Option[]) => { + setSummaryFilters(prevFilters => ({ + ...prevFilters, + [filterKey]: selectedOptions + })); + }; + + const searchFunctions = { + Phenotype: (value: string) => searchPhenotypeFilter(value, phenotypeOptions), + Nerve: (value: string) => searchNerveFilter(value, nerveOptions) + }; + + return ( + + {filterConfig.map(filter => ( + searchFunctions[filter.id](searchValue)} + onSelect={(options: Option[]) => handleSelect(filter.id as keyof SummaryFilters, options)} + /> + ))} + + ) + +} + +export default SummaryFiltersDropdown; \ No newline at end of file diff --git a/src/services/searchService.ts b/src/services/searchService.ts index 89276ec..85c098b 100644 --- a/src/services/searchService.ts +++ b/src/services/searchService.ts @@ -1,6 +1,6 @@ // Search origins -import {Option} from "../components/common/Types.ts"; -import {SYNONYMS_TITLE} from "../settings.ts"; +import { Option } from "../components/common/Types.ts"; +import { SYNONYMS_TITLE } from "../settings.ts"; export const searchOrigins = (searchValue: string, options: Option[]): Option[] => { return searchAnatomicalEntities(searchValue, options); @@ -26,6 +26,13 @@ export const searchVias = (searchValue: string, options: Option[]): Option[] => return searchAnatomicalEntities(searchValue, options); }; +export const searchNerveFilter = (value: string, nerveOptions: Option[]): Option[] => { + return searchByLabel(value, nerveOptions); +} + +export const searchPhenotypeFilter = (value: string, phenotypeOptions: Option[]): Option[] => { + return searchByLabel(value, phenotypeOptions); +} const searchByLabel = (searchValue: string, options: Option[]): Option[] => { const lowerSearchValue = searchValue.toLowerCase(); diff --git a/src/services/summaryHeatmapService.ts b/src/services/summaryHeatmapService.ts index 14e39be..f806f20 100644 --- a/src/services/summaryHeatmapService.ts +++ b/src/services/summaryHeatmapService.ts @@ -3,6 +3,7 @@ import { HierarchicalItem, ISubConnections, ksMapType } from "../components/comm import { ConnectionSummary, SummaryFilters } from "../context/DataContext.ts"; import { HierarchicalNode, KnowledgeStatement, Organ } from "../models/explorer.ts"; import { PhenotypeDetail } from "../components/common/Types.ts"; +import { OTHER_LABEL } from "../constants.tsx"; export const checkIfConnectionSummaryIsEmpty = (connectionSummary: ConnectionSummary): boolean => { @@ -58,7 +59,7 @@ export function getAllPhenotypes(connections: ksMapType): string[] { if (connection.ks?.phenotype) { phenotypeNames.add(connection.ks.phenotype); } else { - phenotypeNames.add('other'); + phenotypeNames.add(OTHER_LABEL); } }); return Array.from(phenotypeNames) @@ -129,8 +130,14 @@ export function getSecondaryHeatmapData(yAxis: HierarchicalItem[], connections: export function summaryFilterKnowledgeStatements(knowledgeStatements: Record, summaryFilters: SummaryFilters): Record { const phenotypeIds = summaryFilters.Phenotype.map(option => option.id); const nerveIds = summaryFilters.Nerve.map(option => option.id); - console.log(phenotypeIds, nerveIds); - return knowledgeStatements; + return Object.entries(knowledgeStatements).reduce((filtered, [id, ks]) => { + const phenotypeMatch = !phenotypeIds.length || phenotypeIds.includes(ks.phenotype); + const nerveMatch = !nerveIds.length || ks.via?.some(via => via.anatomical_entities.map(entity => entity.id).some(id => nerveIds.includes(id))); + if (phenotypeMatch && nerveMatch) { + filtered[id] = ks; + } + return filtered; + }, {} as Record); } export function calculateSecondaryConnections( @@ -187,7 +194,7 @@ export function calculateSecondaryConnections( const ksPhenotypes = knowledgeStatementIds.map(ksId => knowledgeStatements[ksId].phenotype).filter(phenotype => phenotype !== ''); const phenotypeColorsSet = new Set(); - const unknownFilter = phenotypes.find(p => p.label === 'other'); + const unknownFilter = phenotypes.find(p => p.label === OTHER_LABEL); ksPhenotypes.length === 0 ? phenotypeColorsSet.add(unknownFilter?.color || '') : ksPhenotypes.map(phenotype => { const phn = phenotypes.find(p => p.label === phenotype); @@ -220,5 +227,6 @@ export const getNormalizedValueForMinMax = (value: number, min: number, max: num // keep the min 0 always... // Ex. for situations where min is 4... the value 4 will not be shown... min = 0; + if (max === 0) return 0; return max !== min ? (value - min) / (max - min) : 1; } \ No newline at end of file From 07b9f46a5e29bab0be0b4c51fc63ab6f0465201d Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Fri, 17 May 2024 10:50:13 +0530 Subject: [PATCH 3/4] ESCKAN-43 cleanup --- src/components/SummaryFiltersDropdown.tsx | 2 +- src/components/common/Types.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/SummaryFiltersDropdown.tsx b/src/components/SummaryFiltersDropdown.tsx index 19884cc..5f9f701 100644 --- a/src/components/SummaryFiltersDropdown.tsx +++ b/src/components/SummaryFiltersDropdown.tsx @@ -29,7 +29,7 @@ const SummaryFiltersDropdown = ({ nerves, phenotypes }: { nerves: { [key: string]: string }, phenotypes: PhenotypeDetail[] }) => { - const { summaryFilters, setSummaryFilters, knowledgeStatements, organs } = useDataContext(); + const { summaryFilters, setSummaryFilters } = useDataContext(); const convertNervesToOptions = (nerves: { [key: string]: string }): Option[] => { return Object.keys(nerves).map(nerve => ({ diff --git a/src/components/common/Types.ts b/src/components/common/Types.ts index a293cc5..845ed9b 100644 --- a/src/components/common/Types.ts +++ b/src/components/common/Types.ts @@ -1,5 +1,4 @@ import { KnowledgeStatement } from "../../models/explorer"; -import { Filters } from "../../context/DataContext.ts"; export type OptionDetail = { title: string; // What to display as the title/label for the property. From 22ba408c0ce50b207dc8e3197c12cb704dc53768 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Mon, 20 May 2024 12:39:40 +0530 Subject: [PATCH 4/4] ESCKAN-43 shift "other" constant to settings.ts --- src/components/SummaryFiltersDropdown.tsx | 2 +- src/constants.tsx | 1 - src/services/summaryHeatmapService.ts | 2 +- src/settings.ts | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 src/constants.tsx diff --git a/src/components/SummaryFiltersDropdown.tsx b/src/components/SummaryFiltersDropdown.tsx index 5f9f701..99b7953 100644 --- a/src/components/SummaryFiltersDropdown.tsx +++ b/src/components/SummaryFiltersDropdown.tsx @@ -4,7 +4,7 @@ import CustomFilterDropdown from "./common/CustomFilterDropdown"; import { Option, PhenotypeDetail } from "./common/Types"; import { Box } from "@mui/material"; import { searchNerveFilter, searchPhenotypeFilter } from "../services/searchService"; -import { OTHER_LABEL } from "../constants"; +import { OTHER_LABEL } from "../settings"; interface FilterConfig { id: keyof SummaryFilters; diff --git a/src/constants.tsx b/src/constants.tsx deleted file mode 100644 index 2c7591b..0000000 --- a/src/constants.tsx +++ /dev/null @@ -1 +0,0 @@ -export const OTHER_LABEL = 'other' \ No newline at end of file diff --git a/src/services/summaryHeatmapService.ts b/src/services/summaryHeatmapService.ts index f806f20..456c2a6 100644 --- a/src/services/summaryHeatmapService.ts +++ b/src/services/summaryHeatmapService.ts @@ -3,7 +3,7 @@ import { HierarchicalItem, ISubConnections, ksMapType } from "../components/comm import { ConnectionSummary, SummaryFilters } from "../context/DataContext.ts"; import { HierarchicalNode, KnowledgeStatement, Organ } from "../models/explorer.ts"; import { PhenotypeDetail } from "../components/common/Types.ts"; -import { OTHER_LABEL } from "../constants.tsx"; +import { OTHER_LABEL } from "../settings.ts"; export const checkIfConnectionSummaryIsEmpty = (connectionSummary: ConnectionSummary): boolean => { diff --git a/src/settings.ts b/src/settings.ts index a63d4fd..be235e5 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -8,4 +8,5 @@ export const OTHER_X_AXIS_ID = 'OTHER_X' export const OTHER_X_AXIS_LABEL = 'Other' export const SYNONYMS_TITLE = "synonyms" +export const OTHER_LABEL = 'other'