From 3395aaa0eaed8d33f1af5ee069519d91a42aea72 Mon Sep 17 00:00:00 2001 From: Amy Lehman Date: Tue, 25 Aug 2026 11:43:10 -0500 Subject: [PATCH 1/2] PEAR-2560 - consolidate queries for continuous data --- .../clinicalContinuousStatsSlice.ts | 76 ++++++++++++++----- .../cDave/CDaveCard/CDaveCard.unit.test.tsx | 29 ++----- .../cDave/CDaveCard/ContinuousData.tsx | 25 +++--- 3 files changed, 74 insertions(+), 56 deletions(-) diff --git a/packages/core/src/features/clinicalDataAnalysis/clinicalContinuousStatsSlice.ts b/packages/core/src/features/clinicalDataAnalysis/clinicalContinuousStatsSlice.ts index 2177f37748..77df618bc1 100644 --- a/packages/core/src/features/clinicalDataAnalysis/clinicalContinuousStatsSlice.ts +++ b/packages/core/src/features/clinicalDataAnalysis/clinicalContinuousStatsSlice.ts @@ -1,6 +1,37 @@ -import { graphqlAPISlice } from "../gdcapi/gdcgraphql"; +import { GraphQLApiResponse, graphqlAPISlice } from "../gdcapi/gdcgraphql"; import { GqlOperation, GqlRange } from "../gdcapi/filters"; +interface ClinicalContinuousStatsResponse { + viewer: { + explore: { + cases: { + aggregations: { + [field: string]: { + stats: { + Min: number; + Max: number; + Mean: number; + SD: number; + }; + percentiles: { + Median: number; + IQR: number; + q1: number; + q3: number; + }; + range: { + buckets: Array<{ + doc_count: number; + key: string; + }>; + }; + }; + }; + }; + }; + }; +} + export interface ClinicalContinuousStatsData { readonly min: number; readonly max: number; @@ -10,6 +41,7 @@ export interface ClinicalContinuousStatsData { readonly median: number; readonly q1: number; readonly q3: number; + readonly buckets: Record; } interface ClinicalContinuousStatsInputs { @@ -29,7 +61,7 @@ const continuousDataStatsApi = graphqlAPISlice.injectEndpoints({ viewer { explore { cases { - aggregations(filters: $queryFilters) { + aggregations(case_filters: $queryFilters) { ${field} { stats { Min : min @@ -60,25 +92,29 @@ const continuousDataStatsApi = graphqlAPISlice.injectEndpoints({ rangeFilters, }, }), - transformResponse: (response, _, arg) => { + transformResponse: ( + response: GraphQLApiResponse, + _, + arg, + ) => { + const aggregation = + response.data.viewer.explore.cases.aggregations[arg.field]; return { - min: response.data.viewer.explore.cases.aggregations[arg.field].stats - .Min, - max: response.data.viewer.explore.cases.aggregations[arg.field].stats - .Max, - mean: response.data.viewer.explore.cases.aggregations[arg.field].stats - .Mean, - std_dev: - response.data.viewer.explore.cases.aggregations[arg.field].stats.SD, - iqr: response.data.viewer.explore.cases.aggregations[arg.field] - .percentiles.IQR, - median: - response.data.viewer.explore.cases.aggregations[arg.field] - .percentiles.Median, - q1: response.data.viewer.explore.cases.aggregations[arg.field] - .percentiles.q1, - q3: response.data.viewer.explore.cases.aggregations[arg.field] - .percentiles.q3, + min: aggregation.stats.Min, + max: aggregation.stats.Max, + mean: aggregation.stats.Mean, + std_dev: aggregation.stats.SD, + iqr: aggregation.percentiles.IQR, + median: aggregation.percentiles.Median, + q1: aggregation.percentiles.q1, + q3: aggregation.percentiles.q3, + buckets: aggregation.range.buckets.reduce( + (facetBuckets, apiBucket) => { + facetBuckets[apiBucket.key] = apiBucket.doc_count; + return facetBuckets; + }, + {} as Record, + ), }; }, }), diff --git a/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx b/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx index 89ad3679e7..933f4b0dc2 100644 --- a/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx +++ b/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx @@ -153,12 +153,8 @@ describe("CDaveCard", () => { type: "long", }) as any, ); - jest.spyOn(facetHooks, "useRangeFacet").mockReturnValue({ - data: { "0.0-12.0": 10, "12.0-24.0": 90 }, - isFetching: false, - isSuccess: true, - } as any); jest.mocked(useGetContinuousDataStatsQuery).mockReturnValue({ + data: { buckets: { "0.0-12.0": 10, "12.0-24.0": 90 } }, isFetching: false, isSuccess: true, } as any); @@ -194,12 +190,9 @@ describe("CDaveCard", () => { type: "long", }) as any, ); - jest.spyOn(facetHooks, "useRangeFacet").mockReturnValue({ - data: { "-28.0-166.8000001": 38 }, - isFetching: false, - isSuccess: true, - } as any); jest.mocked(useGetContinuousDataStatsQuery).mockReturnValue({ + data: { buckets: { "-28.0-166.8000001": 38 } }, + isFetching: false, isSuccess: true, } as any); @@ -226,12 +219,9 @@ describe("CDaveCard", () => { type: "long", }) as any, ); - jest.spyOn(facetHooks, "useRangeFacet").mockReturnValue({ - data: { "7201.0-12255.8": 10, "12255.8-17310.6": 90 }, - isFetching: false, - isSuccess: true, - } as any); jest.mocked(useGetContinuousDataStatsQuery).mockReturnValue({ + data: { buckets: { "7201.0-12255.8": 10, "12255.8-17310.6": 90 } }, + isFetching: false, isSuccess: true, } as any); @@ -274,7 +264,7 @@ describe("CDaveCard", () => { }) as any, ); jest - .spyOn(facetHooks, "useRangeFacet") + .mocked(useGetContinuousDataStatsQuery) .mockReturnValue({ data: {}, isFetching: false } as any); const stats = { @@ -309,12 +299,9 @@ describe("CDaveCard", () => { type: "long", }) as any, ); - jest.spyOn(facetHooks, "useRangeFacet").mockReturnValue({ - data: { "0.0-12.0": 20, "12.0-24.0": 90 }, - isFetching: false, - isSuccess: true, - } as any); jest.mocked(useGetContinuousDataStatsQuery).mockReturnValue({ + data: { buckets: { "0.0-12.0": 20, "12.0-24.0": 90 } }, + isFetching: false, isSuccess: true, } as any); diff --git a/packages/portal-proto/src/features/cDave/CDaveCard/ContinuousData.tsx b/packages/portal-proto/src/features/cDave/CDaveCard/ContinuousData.tsx index 1f7d6c2b53..0bca5fc925 100644 --- a/packages/portal-proto/src/features/cDave/CDaveCard/ContinuousData.tsx +++ b/packages/portal-proto/src/features/cDave/CDaveCard/ContinuousData.tsx @@ -5,7 +5,6 @@ import { Statistics, GqlOperation, } from "@gff/core"; -import { useRangeFacet } from "../../facets/hooks"; import CDaveHistogram from "./CDaveHistogram"; import CDaveTable from "./CDaveTable"; import ClinicalSurvivalPlot from "./ClinicalSurvivalPlot"; @@ -131,13 +130,7 @@ const ContinuousData: React.FC = ({ [customBinnedData, initialData], ); - const { data, isFetching, isSuccess } = useRangeFacet( - field, - ranges, - { docType: "cases", indexType: "repository" }, - cohortFilters, - ); - const { data: statsData } = useGetContinuousDataStatsQuery({ + const { data, isSuccess, isFetching } = useGetContinuousDataStatsQuery({ field: field.replaceAll(".", "__"), queryFilters: cohortFilters, rangeFilters: { @@ -152,12 +145,14 @@ const ContinuousData: React.FC = ({ const displayedData = useDeepCompareMemo( () => - processContinuousResultData( - isSuccess ? data : {}, - customBinnedData, - field, - dataDimension, - ), + isSuccess + ? processContinuousResultData( + data.buckets, + customBinnedData, + field, + dataDimension, + ) + : [], [isSuccess, data, customBinnedData, field, dataDimension], ); @@ -186,7 +181,7 @@ const ContinuousData: React.FC = ({ From 243b4b79296bf917809b078d85e2ed119838d86e Mon Sep 17 00:00:00 2001 From: Amy Lehman Date: Tue, 25 Aug 2026 12:31:34 -0500 Subject: [PATCH 2/2] PEAR-2560 - linting --- .../src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx b/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx index 933f4b0dc2..5eda687cae 100644 --- a/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx +++ b/packages/portal-proto/src/features/cDave/CDaveCard/CDaveCard.unit.test.tsx @@ -1,5 +1,4 @@ import userEvent from "@testing-library/user-event"; -import * as facetHooks from "../../facets/hooks"; import * as router from "next/router"; import { render } from "test-utils"; import CDaveCard from "./CDaveCard";