Skip to content

Commit b8bef22

Browse files
author
Cristina Aurrecoechea
committed
updates to notebook, ex feature-1567-integrate-de-notebook
1 parent d3eabdf commit b8bef22

File tree

10 files changed

+205
-68
lines changed

10 files changed

+205
-68
lines changed

packages/libs/eda/src/lib/core/components/computations/Types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export interface ComputationProps {
1717
}
1818

1919
export interface ComputationConfigProps extends ComputationProps {
20-
// alphadiv abundance
2120
computation: Computation;
2221
visualizationId: string;
2322
addNewComputation: (name: string, configuration: unknown) => void;
@@ -27,6 +26,7 @@ export interface ComputationConfigProps extends ComputationProps {
2726
additionalCollectionPredicate?: (
2827
variableCollection: CollectionVariableTreeNode
2928
) => boolean; // Additional constraints for allowed collection variables
29+
hideConfigurationComponent?: boolean; // Whether to hide the configuration component entirely
3030
}
3131

3232
export interface ComputationOverviewProps extends ComputationProps {}

packages/libs/eda/src/lib/core/components/computations/plugins/dimensionalityReduction.tsx

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useFindEntityAndVariableCollection } from '../../..';
1+
import { StudyEntity, useFindEntityAndVariableCollection, useStudyEntities } from '../../..';
22
import { VariableCollectionDescriptor } from '../../../types/variable';
33
import { scatterplotVisualization } from '../../visualizations/implementations/ScatterplotVisualization';
44
import { ComputationConfigProps, ComputationPlugin } from '../Types';
@@ -17,6 +17,9 @@ import { makeClassNameHelper } from '@veupathdb/wdk-client/lib/Utils/ComponentUt
1717
import { VariableCollectionSingleSelect } from '../../variableSelectors/VariableCollectionSingleSelect';
1818
import { IsEnabledInPickerParams } from '../../visualizations/VisualizationTypes';
1919
import { entityTreeToArray } from '../../../utils/study-metadata';
20+
import { useEffect, useMemo, useState } from 'react';
21+
import { ItemGroup } from '@veupathdb/coreui/lib/components/inputs/SingleSelect';
22+
import { Item } from '@veupathdb/coreui/lib/components/inputs/checkboxes/CheckboxList';
2023

2124
const cx = makeClassNameHelper('AppStepConfigurationContainer');
2225

@@ -120,6 +123,7 @@ export function DimensionalityReductionConfiguration(
120123
visualizationId,
121124
changeConfigHandlerOverride,
122125
showStepNumber = true,
126+
hideConfigurationComponent = false,
123127
} = props;
124128
assertComputationWithConfig(computation, DimensionalityReductionConfig);
125129
const configuration = computation.descriptor.configuration;
@@ -134,7 +138,52 @@ export function DimensionalityReductionConfiguration(
134138
? changeConfigHandlerOverride
135139
: workspaceChangeConfigHandler;
136140

137-
return (
141+
const entities = useStudyEntities();
142+
143+
const collectionsInStudy = useMemo(() => {
144+
const collectionItems = entities
145+
.filter(
146+
(e): e is StudyEntity & Required<Pick<StudyEntity, 'collections'>> =>
147+
!!e.collections?.length
148+
)
149+
.map((e): ItemGroup<string> => {
150+
const collections = e.collections.filter(isNotAbsoluteAbundanceVariableCollection)
151+
return {
152+
label: e.displayName,
153+
items: collections.map(
154+
(collection): Item<string> => ({
155+
value: `${e.id}:${collection.id}`,
156+
display: collection.displayName ?? collection.id,
157+
})
158+
),
159+
};
160+
})
161+
.filter((itemGroup) => itemGroup.items.length > 0); // Remove entites that had all their collections fail the collection predicate.
162+
return collectionItems;
163+
}, [entities]);
164+
165+
console.log('collectionsInStudy', collectionsInStudy);
166+
167+
// This computation only has one input. If there is only one option for the input (one collection),
168+
// then we can set it automaticaly.
169+
// With only one collection, it may be useful to hide the entire configuration component (differential expression notebook)
170+
useEffect(() => {
171+
// If there is only one collection variable, set it automatically
172+
// TEMPORARY - until we have the right data, just pretend we only have one by
173+
// using the first collection
174+
// if (collectionsInStudy.length === 1) {
175+
console.log('only one collection group');
176+
changeConfigHandler('collectionVariable', {
177+
entityId: collectionsInStudy[0].items[0].value.split(':')[0],
178+
collectionId: collectionsInStudy[0].items[0].value.split(':')[1],
179+
});
180+
// }
181+
}, [collectionsInStudy, changeConfigHandler]);
182+
183+
184+
return hideConfigurationComponent ? (
185+
null
186+
) : (
138187
<ComputationStepContainer
139188
computationStepInfo={{
140189
stepNumber: 1,
@@ -159,8 +208,8 @@ export function DimensionalityReductionConfiguration(
159208
);
160209
}
161210

162-
// Dimensionality reduction's only requirement of the study is that it contains
163-
// at least one collection.
211+
// For now, dimensionality reduction's only requirement of the study is that it contains
212+
// at least one collection. This may change in the future or possibly made configurable via the notebook.
164213
function isEnabledInPicker({
165214
studyMetadata,
166215
}: IsEnabledInPickerParams): boolean {

packages/libs/eda/src/lib/core/components/computations/plugins/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { plugin as distributions } from './distributions';
66
import { plugin as countsandproportions } from './countsAndProportions';
77
import { plugin as abundance } from './abundance';
88
import { plugin as differentialabundance } from './differentialabundance';
9-
// import { plugin as differentialexpression } from './differentialExpression';
9+
import { plugin as differentialexpression } from './differentialExpression';
1010
import { plugin as correlationassaymetadata } from './correlationAssayMetadata'; // mbio
1111
import { plugin as correlationassayassay } from './correlationAssayAssay'; // mbio
1212
import { plugin as correlation } from './correlation'; // genomics (- vb)
@@ -19,7 +19,7 @@ export const plugins: Record<string, ComputationPlugin> = {
1919
betadiv,
2020
dimensionalityreduction,
2121
differentialabundance,
22-
// differentialexpression,
22+
differentialexpression,
2323
correlationassaymetadata,
2424
correlationassayassay,
2525
correlation,

packages/libs/eda/src/lib/core/components/variableSelectors/VariableCollectionSingleSelect.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export function VariableCollectionSingleSelect(props: Props) {
2323
const { collectionPredicate, onSelect, value, additionalItemGroups } = props;
2424
const entities = useStudyEntities();
2525

26+
console.log('collections', entities
27+
.filter(
28+
(e): e is StudyEntity & Required<Pick<StudyEntity, 'collections'>> =>
29+
!!e.collections?.length
30+
));
31+
2632
const items = useMemo(() => {
2733
const collectionItems = entities
2834
.filter(

packages/libs/eda/src/lib/notebook/ComputeNotebookCell.tsx

Lines changed: 92 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { plugins } from '../core/components/computations/plugins';
88
import { ComputeCellDescriptor } from './NotebookPresets';
99
import { useCachedPromise } from '../core/hooks/cachedPromise';
1010
import ExpandablePanel from '@veupathdb/coreui/lib/components/containers/ExpandablePanel';
11+
import { useEffect, useState } from 'react';
12+
import Dialog from '@veupathdb/wdk-client/lib/Components/Overlays/Dialog';
13+
import { FilledButton, OutlinedButton } from '@veupathdb/coreui';
14+
import { Link } from 'react-router-dom';
1115

1216
export function ComputeNotebookCell(
1317
props: NotebookCellProps<ComputeCellDescriptor>
@@ -22,11 +26,13 @@ export function ComputeNotebookCell(
2226
computationId,
2327
cells,
2428
getAdditionalCollectionPredicate,
29+
hidden=false,
2530
} = cell;
2631
const computation = analysis.descriptor.computations.find(
2732
(comp) => comp.computationId === computationId
2833
);
2934
if (computation == null) throw new Error('Cannot find computation.');
35+
const [showErrorDialog, setShowErrorDialog] = useState(false);
3036

3137
// fetch 'apps'
3238
const dataClient = useDataClient();
@@ -49,7 +55,6 @@ export function ComputeNotebookCell(
4955
// Ideally it should also use the functional update form of setComputations.
5056
//
5157
// We'll use a special, simple changeConfigHandler for the computation configuration
52-
console.log('analysis', analysis.descriptor.computations);
5358
const changeConfigHandler = (propertyName: string, value?: any) => {
5459
if (!computation || !analysis.descriptor.computations[0]) return;
5560

@@ -96,50 +101,101 @@ export function ComputeNotebookCell(
96101
const isComputationConfigurationValid = !!plugin?.isConfigurationComplete(
97102
computation.descriptor.configuration
98103
);
99-
104+
100105
// Prep any additional restrictions on collections, if defined
101106
const additionalCollectionPredicate =
102107
getAdditionalCollectionPredicate &&
103108
getAdditionalCollectionPredicate(projectId);
109+
110+
111+
// Run the compute if we're all set to go. Useful when there is a default configuration.
112+
useEffect(() => {
113+
if (isComputationConfigurationValid && jobStatus === 'no-such-job' && hidden) {
114+
console.log("creating job");
115+
createJob();
116+
}
117+
}, [isComputationConfigurationValid, jobStatus, createJob, hidden]);
118+
119+
// Show error dialog when hidden compute fails
120+
useEffect(() => {
121+
if (hidden && jobStatus === 'failed') {
122+
setShowErrorDialog(true);
123+
}
124+
}, [hidden, jobStatus]);
125+
104126

105127
return computation && appOverview ? (
106128
<>
107-
{cell.helperText && (
108-
<div className="NotebookCellHelpText">
109-
<span>{cell.helperText}</span>
110-
</div>
111-
)}
112-
<ExpandablePanel
113-
title={cell.title}
114-
subTitle={''}
115-
state="open"
116-
themeRole="primary"
129+
{/* Error Dialog */}
130+
<Dialog
131+
open={showErrorDialog}
132+
onClose={() => setShowErrorDialog(false)}
133+
aria-labelledby="compute-error-dialog-title"
134+
title="Computation failed"
117135
>
118-
<div
119-
className={'NotebookCellContent' + (isDisabled ? ' disabled' : '')}
120-
>
121-
<plugin.configurationComponent
122-
analysisState={analysisState}
123-
computation={computation}
124-
totalCounts={totalCountsResult}
125-
filteredCounts={filteredCountsResult}
126-
visualizationId="not_used" // irrelevant because we have our own changeConfigHandler
127-
addNewComputation={() => {}} // also irrelevant for us because we add the computation elsewhere
128-
computationAppOverview={appOverview}
129-
geoConfigs={[]}
130-
changeConfigHandlerOverride={changeConfigHandler}
131-
showStepNumber={false}
132-
showExpandableHelp={false} // no expandable sections within an expandable element.
133-
additionalCollectionPredicate={additionalCollectionPredicate}
134-
/>
135-
<RunComputeButton
136-
computationAppOverview={appOverview}
137-
status={jobStatus}
138-
isConfigured={isComputationConfigurationValid}
139-
createJob={createJob}
140-
/>
136+
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', width: '600px', padding: '1em', alignItems: 'center', fontSize: '1.2em', height: '130px', textAlign: 'center', margin: '0.5em 0' }}>
137+
<p>The background {cell.title + ' ' || ''}computation for has failed. <strong>Please <Link to="/contact-us">contact us</Link> for assistance.</strong>
138+
</p>
139+
<p>After closing this dialog, you may continue with your search.</p>
141140
</div>
142-
</ExpandablePanel>
141+
</Dialog>
142+
{hidden ? (
143+
<plugin.configurationComponent
144+
analysisState={analysisState}
145+
computation={computation}
146+
totalCounts={totalCountsResult}
147+
filteredCounts={filteredCountsResult}
148+
visualizationId="not_used" // irrelevant because we have our own changeConfigHandler
149+
addNewComputation={() => {}} // also irrelevant for us because we add the computation elsewhere
150+
computationAppOverview={appOverview}
151+
geoConfigs={[]}
152+
changeConfigHandlerOverride={changeConfigHandler}
153+
showStepNumber={false}
154+
showExpandableHelp={false} // no expandable sections within an expandable element.
155+
additionalCollectionPredicate={additionalCollectionPredicate}
156+
hideConfigurationComponent={true}
157+
/>
158+
) : (
159+
<>
160+
{cell.helperText && (
161+
<div className="NotebookCellHelpText">
162+
<span>{cell.helperText}</span>
163+
</div>
164+
)}
165+
<ExpandablePanel
166+
title={cell.title}
167+
subTitle={''}
168+
state="open"
169+
themeRole="primary"
170+
>
171+
<div
172+
className={'NotebookCellContent' + (isDisabled ? ' disabled' : '')}
173+
>
174+
<plugin.configurationComponent
175+
analysisState={analysisState}
176+
computation={computation}
177+
totalCounts={totalCountsResult}
178+
filteredCounts={filteredCountsResult}
179+
visualizationId="not_used" // irrelevant because we have our own changeConfigHandler
180+
addNewComputation={() => {}} // also irrelevant for us because we add the computation elsewhere
181+
computationAppOverview={appOverview}
182+
geoConfigs={[]}
183+
changeConfigHandlerOverride={changeConfigHandler}
184+
showStepNumber={false}
185+
showExpandableHelp={false} // no expandable sections within an expandable element.
186+
additionalCollectionPredicate={additionalCollectionPredicate}
187+
hideConfigurationComponent={false}
188+
/>
189+
<RunComputeButton
190+
computationAppOverview={appOverview}
191+
status={jobStatus}
192+
isConfigured={isComputationConfigurationValid}
193+
createJob={createJob}
194+
/>
195+
</div>
196+
</ExpandablePanel>
197+
</>
198+
)}
143199
{cells &&
144200
cells.map((subCell, index) => {
145201
const isSubCellDisabled =

packages/libs/eda/src/lib/notebook/EdaNotebookAnalysis.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type UpdateParamValue = (
2626
) => void;
2727

2828
export interface WdkState {
29+
queryName?: string;
2930
parameters?: Parameter[];
3031
paramValues?: ParameterValues;
3132
updateParamValue?: UpdateParamValue;

packages/libs/eda/src/lib/notebook/NotebookPresets.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface ComputeCellDescriptor
5454
getAdditionalCollectionPredicate?: (
5555
projectId?: string
5656
) => (variableCollection: CollectionVariableTreeNode) => boolean;
57+
hidden?: boolean; // Whether to hide this computation cell in the UI. Useful for computations where the entire configuration is already known.
5758
}
5859

5960
export interface TextCellDescriptor extends NotebookCellDescriptorBase<'text'> {
@@ -91,32 +92,33 @@ export const presetNotebooks: Record<string, PresetNotebook> = {
9192
title: 'Select samples (optional)',
9293
helperText: (
9394
<NumberedHeader
94-
number={0}
95+
number={1}
9596
text={'Optionally refine samples for differential expression.'}
9697
color={colors.grey[800]}
9798
/>
9899
),
99100
},
100101
{
101102
type: 'compute',
102-
title: 'Configure PCA',
103+
title: 'PCA',
103104
computationName: 'dimensionalityreduction',
104105
computationId: 'pca_1',
105-
helperText: (
106-
<NumberedHeader
107-
number={1}
108-
text={
109-
'Use PCA to investigate possible sources of variation in the dataset.'
110-
}
111-
color={colors.grey[800]}
112-
/>
113-
),
106+
hidden: true, // Hide in UI since config is already known.
114107
cells: [
115108
{
116109
type: 'visualization',
117110
title: 'PCA Plot',
118111
visualizationName: 'scatterplot',
119112
visualizationId: 'pca_1',
113+
helperText: (
114+
<NumberedHeader
115+
number={2}
116+
text={
117+
'Use PCA to investigate possible sources of variation in the dataset.'
118+
}
119+
color={colors.grey[800]}
120+
/>
121+
),
120122
},
121123
],
122124
},
@@ -127,7 +129,7 @@ export const presetNotebooks: Record<string, PresetNotebook> = {
127129
computationId: 'de_1',
128130
helperText: (
129131
<NumberedHeader
130-
number={2}
132+
number={3}
131133
text={
132134
'Run a differential expression analysis using DESeq2. Please choose the metadata variable for comparison, and then set up the reference and comparison groups. When all selections have been made, we can run the computation.'
133135
}
@@ -177,7 +179,7 @@ export const presetNotebooks: Record<string, PresetNotebook> = {
177179
},
178180
helperText: (
179181
<NumberedHeader
180-
number={3}
182+
number={4}
181183
text={
182184
'Once the DESeq2 results are ready, a volcano plot will appear below. Set the threshold lines to color the genes based on their significance and fold change.'
183185
}
@@ -190,7 +192,7 @@ export const presetNotebooks: Record<string, PresetNotebook> = {
190192
title: 'Review and run search',
191193
helperText: (
192194
<NumberedHeader
193-
number={4}
195+
number={5}
194196
text={
195197
'After identifying genes of interest from the volcano plot, run a gene search to review the genes in the Gene Search Results table.'
196198
}

0 commit comments

Comments
 (0)