Skip to content

Commit 8f64df9

Browse files
authored
feat: pre-filter workflow sequencing read runs immediately after ENA data is returned (#910) (#926)
* feat: pre-filter workflow sequencing read runs immediately after ENA data is returned (#910) * feat: make worklow state to preserve stable object (#910) * feat: pre-filter ena data by taxonomy (#910) * feat: updated prefiltering based on sequencing step (#910) --------- Co-authored-by: Fran McDade <[email protected]>
1 parent de01776 commit 8f64df9

File tree

12 files changed

+109
-78
lines changed

12 files changed

+109
-78
lines changed

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/components/CollectionSelector/collectionSelector.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { Table } from "./components/Table/table";
88
import { RowSelectionState } from "@tanstack/table-core";
99
import { getSequencingData } from "../../utils";
1010
import { ColumnFilters } from "./components/ColumnFilters/columnFilters";
11-
import { preSelectColumnFilters } from "./utils";
1211

1312
export const CollectionSelector = ({
1413
onClose,
@@ -17,15 +16,13 @@ export const CollectionSelector = ({
1716
selectedCount,
1817
stepKey,
1918
table,
20-
workflowParameter,
2119
}: Props): JSX.Element => {
2220
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
2321
return (
2422
<StyledDialog
2523
onTransitionEnter={() => {
2624
setRowSelection(table.getState().rowSelection);
2725
if (selectedCount > 0) return;
28-
preSelectColumnFilters(table, stepKey, workflowParameter);
2926
table.resetSorting(); // Reset sorting to default.
3027
}}
3128
onClose={onClose}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { WORKFLOW_PARAMETER_VARIABLE } from "../../../../../../../../../../../../../../../apis/catalog/brc-analytics-catalog/common/schema-entities";
2+
import { SEQUENCING_DATA_TYPE } from "../../../../types";
3+
4+
export const WORKFLOW_PARAMETER_BY_STEP_KEY: Record<
5+
SEQUENCING_DATA_TYPE.READ_RUNS_PAIRED | SEQUENCING_DATA_TYPE.READ_RUNS_SINGLE,
6+
WORKFLOW_PARAMETER_VARIABLE
7+
> = {
8+
readRunsPaired: WORKFLOW_PARAMETER_VARIABLE.SANGER_READ_RUN_PAIRED,
9+
readRunsSingle: WORKFLOW_PARAMETER_VARIABLE.SANGER_READ_RUN_SINGLE,
10+
};

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/components/CollectionSelector/hooks/UseTable/hook.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { BaseReadRun, ReadRun } from "../../../../types";
1010
import { ROW_POSITION } from "@databiosphere/findable-ui/lib/components/Table/features/RowPosition/constants";
1111
import { ROW_PREVIEW } from "@databiosphere/findable-ui/lib/components/Table/features/RowPreview/constants";
1212
import { columns } from "./columnDef";
13-
import { useCallback, useMemo, useState } from "react";
13+
import { useCallback, useEffect, useMemo, useState } from "react";
1414
import { getFacetedUniqueValuesWithArrayValues } from "@databiosphere/findable-ui/lib/components/Table/common/utils";
1515
import { arrIncludesSome } from "@databiosphere/findable-ui/lib/components/Table/columnDef/columnFilters/filterFn";
1616
import { ColumnFiltersState, Updater } from "@tanstack/react-table";
@@ -43,6 +43,17 @@ export const useTable = (
4343
[ENA_QUERY_METHOD.TAXONOMY_ID]: [],
4444
});
4545

46+
// Grab the column filters for ENA by taxonomy ID.
47+
const { columnFilters } = enaTaxonomyId;
48+
49+
useEffect(() => {
50+
if (columnFilters.length === 0) return;
51+
// Pre-filter the table data for ENA by taxonomy ID.
52+
setColumnFiltersByMethod(
53+
updateColumnFilters(ENA_QUERY_METHOD.TAXONOMY_ID, columnFilters)
54+
);
55+
}, [columnFilters]);
56+
4657
const onColumnFiltersChange = useCallback(
4758
(updaterOrValue: Updater<ColumnFiltersState>): void =>
4859
setColumnFiltersByMethod(

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/components/CollectionSelector/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { ReadRun } from "../../types";
22
import { OnConfigure } from "../../../../../../../../../../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types";
33
import { Table } from "@tanstack/react-table";
44
import { SEQUENCING_DATA_TYPE } from "../../../../types";
5-
import { WorkflowParameter } from "app/apis/catalog/brc-analytics-catalog/common/entities";
65

76
export interface Props {
87
onClose: () => void;
@@ -11,5 +10,4 @@ export interface Props {
1110
selectedCount: number;
1211
stepKey: SEQUENCING_DATA_TYPE;
1312
table: Table<ReadRun>;
14-
workflowParameter?: WorkflowParameter;
1513
}

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/components/CollectionSelector/utils.ts

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
1-
import { ENA_QUERY_METHOD, SEQUENCING_DATA_TYPE } from "../../../../types";
2-
import { ColumnFiltersState, Table } from "@tanstack/react-table";
3-
import { ReadRun } from "../../types";
1+
import { SEQUENCING_DATA_TYPE } from "../../../../types";
2+
import { ColumnFiltersState } from "@tanstack/react-table";
43
import { CATEGORY_CONFIGS } from "./hooks/UseTable/categoryConfigs";
5-
import { WorkflowParameter } from "app/apis/catalog/brc-analytics-catalog/common/entities";
4+
import {
5+
Workflow,
6+
WorkflowParameter,
7+
} from "../../../../../../../../../../../../../../../apis/catalog/brc-analytics-catalog/common/entities";
8+
import { WORKFLOW_PARAMETER_BY_STEP_KEY } from "./constants";
69

710
/**
811
* Builds column filters from preselected values only if they are valid for the current data.
9-
* A set of preselected filters is considered valid if at least one row in the table satisfies all column filters (AND across columns),
12+
* A set of preselected filters is considered valid if at least one row of data satisfies all column filters (AND across columns),
1013
* where each column filter matches if the row contains at least one of the desired values for that column (OR within a column).
1114
* If no rows satisfy the filters, an empty list is returned.
1215
*
1316
* Note: This does not mutate or filter out individual values; it validates the combination overall.
1417
*
15-
* @param table - Table instance.
18+
* @param rows - Rows (read runs).
1619
* @param preselectedFilters - Record of column IDs to arrays of desired filter values.
1720
* @returns Column filters state.
1821
*/
19-
function buildValidatedColumnFilters(
20-
table: Table<ReadRun>,
22+
function buildValidatedColumnFilters<T>(
23+
rows: T[],
2124
preselectedFilters: Record<string, string[]>
2225
): ColumnFiltersState {
23-
const { rows } = table.getRowModel();
24-
2526
// We need to ensure that the combined column filters are valid for the given data.
2627
// A row must have at least one of the desired values (OR-join) for each column filter (AND-join).
2728
for (const row of rows) {
@@ -32,7 +33,7 @@ function buildValidatedColumnFilters(
3233
const filterValueSet = new Set(value as string[]);
3334

3435
// Determine the column values.
35-
const columnValues = toStringArray(row.getValue(id));
36+
const columnValues = toStringArray(row[id as keyof T]);
3637

3738
// At least one of the desired values must be present in the column "OR".
3839
let hasAny = false;
@@ -54,7 +55,7 @@ function buildValidatedColumnFilters(
5455
if (!isRowValid) continue;
5556

5657
// Found a valid row i.e. we know the pre-selected column filters will be a valid
57-
// combination for the table data. There is no need to continue checking the rest of the rows.
58+
// combination for the data. There is no need to continue checking the rest of the rows.
5859
return Object.entries(preselectedFilters).map(([id, value]) => ({
5960
id,
6061
value,
@@ -65,18 +66,6 @@ function buildValidatedColumnFilters(
6566
return [];
6667
}
6768

68-
/**
69-
* Returns true if the table is using data from ENA query method by taxonomy ID.
70-
* @param table - The table.
71-
* @returns True if the table is using data from ENA query method by taxonomy ID.
72-
*/
73-
function isENAQueryMethodByTaxonomyId(table: Table<ReadRun>): boolean {
74-
if (!table.options.meta) return false;
75-
if (!("enaQueryMethod" in table.options.meta)) return false;
76-
77-
return table.options.meta.enaQueryMethod === ENA_QUERY_METHOD.TAXONOMY_ID;
78-
}
79-
8069
/**
8170
* Gets library strategy requirements from a workflow parameter.
8271
* @param parameter - The workflow parameter.
@@ -131,14 +120,33 @@ export function getDescriptionRequirement(
131120
return parameter.data_requirements.description;
132121
}
133122

123+
/**
124+
* Returns the workflow parameter for the given step key.
125+
* @param workflow - Workflow.
126+
* @param stepKey - Step key.
127+
* @returns Workflow parameter for the given step key.
128+
*/
129+
export function getWorkflowParameter(
130+
workflow: Workflow,
131+
stepKey:
132+
| SEQUENCING_DATA_TYPE.READ_RUNS_PAIRED
133+
| SEQUENCING_DATA_TYPE.READ_RUNS_SINGLE
134+
): WorkflowParameter | undefined {
135+
return workflow.parameters.find(
136+
({ variable }) => variable === WORKFLOW_PARAMETER_BY_STEP_KEY[stepKey]
137+
);
138+
}
139+
134140
/**
135141
* Builds filters based on step key and workflow parameter requirements.
136142
* @param stepKey - The sequencing data type (paired or single).
137143
* @param workflowParameter - Optional workflow parameter with data requirements.
138144
* @returns Filters with layout, strategy, source, and other requirements.
139145
*/
140146
export function buildFilters(
141-
stepKey: SEQUENCING_DATA_TYPE,
147+
stepKey:
148+
| SEQUENCING_DATA_TYPE.READ_RUNS_PAIRED
149+
| SEQUENCING_DATA_TYPE.READ_RUNS_SINGLE,
142150
workflowParameter?: WorkflowParameter
143151
): Record<string, string[]> {
144152
const filters: Record<string, string[]> = {};
@@ -186,22 +194,21 @@ export function buildFilters(
186194

187195
/**
188196
* Pre-selects column filters based on the given step key for ENA query method by taxonomy ID.
189-
* @param table - Table instance.
197+
* @param workflow - Workflow.
198+
* @param rows - Rows (read runs).
190199
* @param stepKey - Step key.
191-
* @param workflowParameter - Optional workflow parameter with data requirements.
200+
* @returns Column filters for the given ENA data.
192201
*/
193-
export function preSelectColumnFilters(
194-
table: Table<ReadRun>,
195-
stepKey: SEQUENCING_DATA_TYPE,
196-
workflowParameter?: WorkflowParameter
197-
): void {
198-
// Do not apply any filters for step key READ_RUNS_ANY.
199-
if (stepKey === SEQUENCING_DATA_TYPE.READ_RUNS_ANY) return;
200-
if (isENAQueryMethodByTaxonomyId(table)) {
201-
const filters = buildFilters(stepKey, workflowParameter);
202-
const columnFiltersState = buildValidatedColumnFilters(table, filters);
203-
table.setColumnFilters(columnFiltersState);
204-
}
202+
export function preSelectColumnFilters<T>(
203+
workflow: Workflow,
204+
rows: T[],
205+
stepKey:
206+
| SEQUENCING_DATA_TYPE.READ_RUNS_PAIRED
207+
| SEQUENCING_DATA_TYPE.READ_RUNS_SINGLE
208+
): ColumnFiltersState {
209+
const workflowParameter = getWorkflowParameter(workflow, stepKey);
210+
const filters = buildFilters(stepKey, workflowParameter);
211+
return buildValidatedColumnFilters(rows, filters);
205212
}
206213

207214
/**

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/enaSequencingData.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export const ENASequencingData = ({
1515
stepKey,
1616
table,
1717
taxonomicLevelSpecies,
18-
workflowParameter,
1918
}: Props): JSX.Element => {
2019
const accessionDialog = useDialog();
2120
const collectionDialog = useDialog();
@@ -53,7 +52,6 @@ export const ENASequencingData = ({
5352
selectedCount={selectedCount}
5453
table={table}
5554
stepKey={stepKey}
56-
workflowParameter={workflowParameter}
5755
/>
5856
<CollectionSummary
5957
onClear={() => {

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/hooks/UseENADataByTaxonomyId/hook.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,28 @@ import { UseENADataByTaxonomyId } from "./types";
33
import { fetchENAData } from "./request";
44
import { useAsync } from "@databiosphere/findable-ui/lib/hooks/useAsync";
55
import { shouldFetch } from "./utils";
6-
import { BRCDataCatalogGenome } from "../../../../../../../../../../../../../../../apis/catalog/brc-analytics-catalog/common/entities";
6+
import {
7+
BRCDataCatalogGenome,
8+
Workflow,
9+
} from "../../../../../../../../../../../../../../../apis/catalog/brc-analytics-catalog/common/entities";
710
import { GA2AssemblyEntity } from "../../../../../../../../../../../../../../../apis/catalog/ga2/entities";
811
import { config } from "../../../../../../../../../../../../../../../../app/config/config";
12+
import { ColumnFiltersState } from "@tanstack/react-table";
13+
import { preSelectColumnFilters } from "../../components/CollectionSelector/utils";
14+
import { SEQUENCING_DATA_TYPE } from "../../../../types";
915

1016
export const useENADataByTaxonomyId = <T>(
11-
genome: BRCDataCatalogGenome | GA2AssemblyEntity
17+
workflow: Workflow,
18+
genome: BRCDataCatalogGenome | GA2AssemblyEntity,
19+
stepKey: SEQUENCING_DATA_TYPE
1220
): UseENADataByTaxonomyId<T> => {
1321
const { data, run } = useAsync<T[] | undefined>();
22+
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
1423
const [errors, setErrors] = useState<Record<string, string>>({});
1524
const [loading, setLoading] = useState<boolean>(true);
1625
const { ncbiTaxonomyId: taxonomyId } = genome;
1726
const { maxReadRunsForBrowseAll } = config();
27+
1828
const onRequestData = useCallback(async (): Promise<void> => {
1929
run(
2030
fetchENAData({
@@ -23,14 +33,19 @@ export const useENADataByTaxonomyId = <T>(
2333
setErrors({ taxonomyId: e.message });
2434
setLoading(false);
2535
},
26-
onSuccess: () => {
36+
onSuccess: (readRuns) => {
2737
setLoading(false);
38+
// If the step key is READ_RUNS_ANY, do not pre-select column filters.
39+
if (stepKey === SEQUENCING_DATA_TYPE.READ_RUNS_ANY) return;
40+
setColumnFilters(
41+
preSelectColumnFilters(workflow, readRuns, stepKey)
42+
);
2843
},
2944
},
3045
taxonomyId,
3146
})
3247
);
33-
}, [run, taxonomyId]);
48+
}, [run, stepKey, taxonomyId, workflow]);
3449

3550
useEffect(() => {
3651
if (shouldFetch(taxonomyId, maxReadRunsForBrowseAll)) {
@@ -43,5 +58,5 @@ export const useENADataByTaxonomyId = <T>(
4358
}
4459
}, [onRequestData, taxonomyId, maxReadRunsForBrowseAll]);
4560

46-
return { data, status: { errors, loading } };
61+
return { columnFilters, data, status: { errors, loading } };
4762
};

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/hooks/UseENADataByTaxonomyId/request.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export async function fetchENAData<T>({
1313
submitOptions,
1414
taxonomyId,
1515
}: {
16-
submitOptions: Required<SubmitOptions>;
16+
submitOptions: Required<SubmitOptions<T>>;
1717
taxonomyId: string;
1818
}): Promise<T[] | undefined> {
1919
try {
@@ -36,7 +36,7 @@ export async function fetchENAData<T>({
3636
return;
3737
}
3838

39-
submitOptions.onSuccess?.();
39+
submitOptions.onSuccess?.(data);
4040

4141
return data;
4242
} catch (error) {
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
export type OnRequestData = (
1+
import { ColumnFiltersState } from "@tanstack/react-table";
2+
3+
export type OnRequestData<T> = (
24
taxonomyId: string,
3-
submitOptions?: SubmitOptions
5+
submitOptions?: SubmitOptions<T>
46
) => Promise<void>;
57

68
export interface Status {
79
errors: Record<string, string>;
810
loading: boolean;
911
}
1012

11-
export interface SubmitOptions {
13+
export interface SubmitOptions<T> {
1214
onError?: (error: Error) => void;
13-
onSuccess?: () => void;
15+
onSuccess?: (data: T[]) => void;
1416
}
1517

1618
export interface UseENADataByTaxonomyId<T> {
19+
columnFilters: ColumnFiltersState;
1720
data?: T[];
1821
status: Status;
1922
}

app/components/Entity/components/ConfigureWorkflowInputs/components/Main/components/Stepper/components/Step/SequencingStep/components/ENASequencingData/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { SEQUENCING_DATA_TYPE } from "../../types";
55
import { UseENADataByTaxonomyId } from "./hooks/UseENADataByTaxonomyId/types";
66
import { Dispatch, SetStateAction } from "react";
77
import { ENA_QUERY_METHOD } from "../../types";
8-
import { WorkflowParameter } from "app/apis/catalog/brc-analytics-catalog/common/entities";
98

109
export interface BaseReadRun {
1110
base_count: number;
@@ -44,5 +43,4 @@ export interface Props {
4443
stepKey: SEQUENCING_DATA_TYPE;
4544
table: Table<ReadRun>;
4645
taxonomicLevelSpecies: string;
47-
workflowParameter?: WorkflowParameter;
4846
}

0 commit comments

Comments
 (0)