Skip to content
Open
Show file tree
Hide file tree
Changes from all 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,169 @@
import { useFindEntityAndVariableCollection } from '../../..';
import { VariableCollectionDescriptor } from '../../../types/variable';
import { scatterplotVisualization } from '../../visualizations/implementations/ScatterplotVisualization';
import { ComputationConfigProps, ComputationPlugin } from '../Types';
import {
useConfigChangeHandler,
assertComputationWithConfig,
isNotAbsoluteAbundanceVariableCollection,
partialToCompleteCodec,
} from '../Utils';
import * as t from 'io-ts';
import { Computation } from '../../../types/visualization';
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';

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',
};
}
},
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>
);
}

// Dimensionality reduction'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,14 @@ function ScatterplotViz(props: VisualizationProps<Options>) {
config: {
outputEntityId: outputEntity.id,
valueSpec: hideTrendlines ? undefined : valueSpecValue,
xAxisVariable: vizConfig.xAxisVariable,
yAxisVariable: vizConfig.yAxisVariable,
xAxisVariable:
sendComputedVariablesInRequest && computedXAxisDescriptor
? computedXAxisDescriptor
: vizConfig.xAxisVariable,
yAxisVariable:
sendComputedVariablesInRequest && computedYAxisDescriptor
? computedYAxisDescriptor
: vizConfig.yAxisVariable,
overlayVariable: vizConfig.overlayVariable,
facetVariable: vizConfig.facetVariable
? [vizConfig.facetVariable]
Expand Down
Loading