Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { useFindEntityAndVariableCollection } from '../../..';
import { VariableCollectionDescriptor } from '../../../types/variable';
import { scatterplotVisualization } from '../../visualizations/implementations/ScatterplotVisualization';
import { ComputationConfigProps, ComputationPlugin } from '../Types';
import { partial } from 'lodash';
import {
useConfigChangeHandler,
assertComputationWithConfig,
isNotAbsoluteAbundanceVariableCollection,
partialToCompleteCodec,
} from '../Utils';
import * as t from 'io-ts';
import { Computation } from '../../../types/visualization';
import SingleSelect from '@veupathdb/coreui/lib/components/inputs/SingleSelect';
import { useMemo } from 'react';
import ScatterBetadivSVG from '../../visualizations/implementations/selectorIcons/ScatterBetadivSVG';
import { ComputationStepContainer } from '../ComputationStepContainer';
import './Plugins.scss';
import { makeClassNameHelper } from '@veupathdb/wdk-client/lib/Utils/ComponentUtils';
import { VariableCollectionSingleSelect } from '../../variableSelectors/VariableCollectionSingleSelect';
import { IsEnabledInPickerParams } from '../../visualizations/VisualizationTypes';
import { entityTreeToArray } from '../../../utils/study-metadata';
import { InputSpec } from '../../visualizations/InputVariables';

const cx = makeClassNameHelper('AppStepConfigurationContainer');

export type DimensionalityReductionConfig = t.TypeOf<
typeof DimensionalityReductionConfig
>;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export const DimensionalityReductionConfig = t.partial({
collectionVariable: VariableCollectionDescriptor,
});

const CompleteDimensionalityReductionConfig = partialToCompleteCodec(
DimensionalityReductionConfig
);

export const plugin: ComputationPlugin = {
configurationComponent: DimensionalityReductionConfiguration,
configurationDescriptionComponent:
DimensionalityReductionConfigDescriptionComponent,
createDefaultConfiguration: () => ({}),
isConfigurationComplete: CompleteDimensionalityReductionConfig.is,
visualizationPlugins: {
scatterplot: scatterplotVisualization
.withOptions({
getComputedXAxisDetails(config) {
if (
DimensionalityReductionConfig.is(config) &&
config.collectionVariable
) {
return {
entityId: config.collectionVariable.entityId,
placeholderDisplayName: 'PCA Axis 1',
variableId: 'PC1',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we just "know" that "PC1" is a valid variableId here? This is a computed (not curated) variable collection, so I guess "PC%d" is defined on the back end somewhere. It's not going to change, I guess. Just wanted to understand better. I'm not demanding hugely complex changes to make this more bulletproof than it needs to be!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for right now we just "know", and yep you're right it's defined in the backend. The differential expression function in veupathutils returns multiple PCs and you can even tell it how many to return. I had started to implement support for choosing PCs but then left it alone because it felt like it was making the initial deliverable too big.

};
}
},
getComputedYAxisDetails(config) {
if (
DimensionalityReductionConfig.is(config) &&
config.collectionVariable
) {
return {
entityId: config.collectionVariable.entityId,
placeholderDisplayName: 'PCA Axis 2',
variableId: 'PC2',
};
}
},
hideShowMissingnessToggle: true,
hideTrendlines: true,
hideFacetInputs: true,
hideLogScale: true,
returnPointIds: false,
sendComputedVariablesInRequest: true,
})
.withSelectorIcon(ScatterBetadivSVG),
},
isEnabledInPicker: isEnabledInPicker,
studyRequirements:
'These visualizations are only available for studies with compatible assay data.',
};

function DimensionalityReductionConfigDescriptionComponent({
computation,
}: {
computation: Computation;
}) {
const findEntityAndVariableCollection = useFindEntityAndVariableCollection();
assertComputationWithConfig(computation, DimensionalityReductionConfig);
const { configuration } = computation.descriptor;
const collectionVariable =
'collectionVariable' in configuration
? configuration.collectionVariable
: undefined;
const updatedCollectionVariable =
findEntityAndVariableCollection(collectionVariable);

return (
<div className="ConfigDescriptionContainer">
<h4>
Data:{' '}
<span>
{updatedCollectionVariable ? (
`${updatedCollectionVariable.entity.displayName} > ${updatedCollectionVariable.variableCollection.displayName}`
) : (
<i>Not selected</i>
)}
</span>
</h4>
</div>
);
}

export function DimensionalityReductionConfiguration(
props: ComputationConfigProps
) {
const {
computationAppOverview,
computation,
analysisState,
visualizationId,
} = props;
assertComputationWithConfig(computation, DimensionalityReductionConfig);
const configuration = computation.descriptor.configuration;

const changeConfigHandler = useConfigChangeHandler(
analysisState,
computation,
visualizationId
);

return (
<ComputationStepContainer
computationStepInfo={{
stepNumber: 1,
stepTitle: `Configure ${computationAppOverview.displayName}`,
}}
>
<div className={cx()}>
<div className={cx('-InputContainer')}>
<span>Data</span>
<VariableCollectionSingleSelect
value={configuration.collectionVariable}
onSelect={(value) => {
if (typeof value === 'string') return;
changeConfigHandler('collectionVariable', value);
}}
collectionPredicate={isNotAbsoluteAbundanceVariableCollection}
/>
</div>
</div>
</ComputationStepContainer>
);
}

// Beta div's only requirement of the study is that it contains
// at least one collection
function isEnabledInPicker({
studyMetadata,
}: IsEnabledInPickerParams): boolean {
if (!studyMetadata) return false;
const entities = entityTreeToArray(studyMetadata.rootEntity);

// Ensure there are collections in this study. Otherwise, disable app
const studyHasCollections = entities.some(
(entity) => !!entity.collections?.length
);

return studyHasCollections;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import { plugin as correlationassayassay } from './correlationAssayAssay'; // mb
import { plugin as correlation } from './correlation'; // genomics (- vb)
import { plugin as selfcorrelation } from './selfCorrelation';
import { plugin as xyrelationships } from './xyRelationships';
import { plugin as dimensionalityreduction } from './dimensionalityReduction';
export const plugins: Record<string, ComputationPlugin> = {
abundance,
alphadiv,
betadiv,
dimensionalityreduction,
differentialabundance,
correlationassaymetadata,
correlationassayassay,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ interface Options
hideTrendlines?: boolean;
hideLogScale?: boolean;
returnPointIds?: boolean; // Determines whether the backend should return the ids of each point in the scatterplot
sendComputedVariablesInRequest?: boolean; // Determines whether computed variable descriptors should be sent to the backend in the data request.
}

function ScatterplotViz(props: VisualizationProps<Options>) {
Expand Down Expand Up @@ -332,6 +333,7 @@ function ScatterplotViz(props: VisualizationProps<Options>) {
computedYAxisDetails,
computedOverlayVariableDescriptor,
providedOverlayVariableDescriptor,
sendComputedVariablesInRequest,
] = useMemo(
() => [
options?.getComputedXAxisDetails?.(computation.descriptor.configuration),
Expand All @@ -340,6 +342,7 @@ function ScatterplotViz(props: VisualizationProps<Options>) {
computation.descriptor.configuration
),
options?.getOverlayVariable?.(computation.descriptor.configuration),
options?.sendComputedVariablesInRequest ?? false,
],
[computation.descriptor.configuration, options]
);
Expand Down Expand Up @@ -745,8 +748,12 @@ function ScatterplotViz(props: VisualizationProps<Options>) {
config: {
outputEntityId: outputEntity.id,
valueSpec: hideTrendlines ? undefined : valueSpecValue,
xAxisVariable: vizConfig.xAxisVariable,
yAxisVariable: vizConfig.yAxisVariable,
xAxisVariable:
(sendComputedVariablesInRequest && computedXAxisDescriptor) ||
vizConfig.xAxisVariable,
yAxisVariable:
(sendComputedVariablesInRequest && computedYAxisDescriptor) ||
vizConfig.yAxisVariable,
overlayVariable: vizConfig.overlayVariable,
facetVariable: vizConfig.facetVariable
? [vizConfig.facetVariable]
Expand Down
Loading