diff --git a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helper.spec.ts b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helper.spec.ts new file mode 100644 index 000000000..8677b4d94 --- /dev/null +++ b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helper.spec.ts @@ -0,0 +1,112 @@ +import { IAnalysesSelection } from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationDialogBase.types'; +import { getWeightAndConditionsForSpecification } from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers'; +import { EPropertyType } from 'components/EditMetadata'; +import { IAutocompleteObject } from 'components/NeurosynthAutocomplete/NeurosynthAutocomplete'; + +describe('CreateMetaAnalysisSpecificationReviewHelpers', () => { + describe('getWeightAndConditionsForSpecification', () => { + it('should set multiple weights and multiple conditions if the reference database is not default', () => { + const mockEstimator: IAutocompleteObject = { + label: 'ALESubtraction', + description: '', + }; + + const mockSelection: IAnalysesSelection = { + selectionKey: 'key', + selectionValue: 'val-selected', + referenceDataset: 'val-reference', + type: EPropertyType.STRING, + }; + + const result = getWeightAndConditionsForSpecification(mockEstimator, mockSelection); + + expect(result.weights).toEqual([1, -1]); + expect(result.conditions).toEqual(['val-selected', 'val-reference']); + expect(result.databaseStudyset).toBeUndefined(); + }); + + it('should return empty lists if the estimator is not defined', () => { + const mockSelection: IAnalysesSelection = { + selectionKey: 'key', + selectionValue: 'val', + referenceDataset: 'neuroquery', + type: EPropertyType.STRING, + }; + + const result = getWeightAndConditionsForSpecification(undefined, mockSelection); + + expect(result.conditions.length).toEqual(0); + expect(result.weights.length).toEqual(0); + expect(result.databaseStudyset).toBeUndefined(); + }); + + it('should set a single weight and a single condition if the reference database is default', () => { + const mockEstimator: IAutocompleteObject = { + label: 'ALESubtraction', + description: '', + }; + + const mockSelection: IAnalysesSelection = { + selectionKey: 'key', + selectionValue: 'val', + referenceDataset: 'neuroquery', + type: EPropertyType.STRING, + }; + + const result = getWeightAndConditionsForSpecification(mockEstimator, mockSelection); + + expect(result.conditions.length).toEqual(1); + expect(result.weights).toEqual([1]); + expect(result.databaseStudyset).toEqual('neuroquery'); + }); + + it('should parse the array into correct boolean types', () => { + let mockEstimator: IAutocompleteObject = { + label: 'ALESubtraction', + description: '', + }; + + let mockSelection: IAnalysesSelection = { + selectionKey: 'key', + selectionValue: 'true', + referenceDataset: 'false', + type: EPropertyType.BOOLEAN, + }; + + let result = getWeightAndConditionsForSpecification(mockEstimator, mockSelection); + expect(result.conditions).toEqual([true, false]); + + mockEstimator = { + label: 'ALESubtraction', + description: '', + }; + + mockSelection = { + selectionKey: 'key', + selectionValue: true, + referenceDataset: 'false', + type: EPropertyType.BOOLEAN, + }; + + result = getWeightAndConditionsForSpecification(mockEstimator, mockSelection); + expect(result.conditions).toEqual([true, false]); + }); + + it('should parse the array into correct string types', () => { + let mockEstimator: IAutocompleteObject = { + label: 'ALESubtraction', + description: '', + }; + + let mockSelection: IAnalysesSelection = { + selectionKey: 'key', + selectionValue: 'true', + referenceDataset: 'false', + type: EPropertyType.STRING, + }; + + let result = getWeightAndConditionsForSpecification(mockEstimator, mockSelection); + expect(result.conditions).toEqual(['true', 'false']); + }); + }); +}); diff --git a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers.ts b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers.ts index 0f445fc80..aa334fa4b 100644 --- a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers.ts +++ b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers.ts @@ -4,6 +4,7 @@ import { selectedReferenceDatasetIsDefaultDataset, } from '../CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers'; import { IAnalysesSelection } from '../CreateMetaAnalysisSpecificationDialogBase.types'; +import { EPropertyType } from 'components/EditMetadata'; export const getWeightAndConditionsForSpecification = ( estimator: IAutocompleteObject | null | undefined, @@ -41,6 +42,21 @@ export const getWeightAndConditionsForSpecification = ( conditions = [selection.selectionValue] as string[] | boolean[]; } + // parse condition into correct type + conditions.forEach((condition, index) => { + switch (selection.type) { + case EPropertyType.BOOLEAN: + conditions[index] = + typeof condition === 'boolean' ? condition : condition === 'true'; + break; + case EPropertyType.STRING: + conditions[index] = condition.toString(); + break; + default: + throw new Error('unsupported selection type'); + } + }); + return { weights, conditions, diff --git a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/CreateMetaAnalysisSpecificationSelectionStepMultiGroup.tsx b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/CreateMetaAnalysisSpecificationSelectionStepMultiGroup.tsx index 8a36c4889..e54c2be6b 100644 --- a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/CreateMetaAnalysisSpecificationSelectionStepMultiGroup.tsx +++ b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/CreateMetaAnalysisSpecificationSelectionStepMultiGroup.tsx @@ -20,7 +20,6 @@ const CreateMetaAnalysisSpecificationSelectionStepMultiGroup: React.FC<{ selectedValue: IAnalysesSelection; }> = (props) => { const { algorithm, onSelectValue, annotationId, selectedValue } = props; - const columnOptions = useInclusionColumnOptions(annotationId, selectedValue?.selectionKey); const colOptionsToMultiGroupOptions: IMultiGroupOption[] = useMemo(() => { return columnOptions @@ -40,9 +39,7 @@ const CreateMetaAnalysisSpecificationSelectionStepMultiGroup: React.FC<{ const selectedOption = useMemo(() => { if (!selectedValue.referenceDataset) return undefined; - const foundOption = multiGroupOptions.find( - (x) => x.label === selectedValue.referenceDataset - ); + const foundOption = multiGroupOptions.find((x) => x.id === selectedValue.referenceDataset); return foundOption; }, [multiGroupOptions, selectedValue.referenceDataset]); diff --git a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.spec.ts b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.spec.ts new file mode 100644 index 000000000..13f0900d6 --- /dev/null +++ b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.spec.ts @@ -0,0 +1,51 @@ +import { + isMultiGroupAlgorithm, + selectedReferenceDatasetIsDefaultDataset, +} from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers'; +import { + DEFAULT_REFERENCE_DATASETS, + MULTIGROUP_ALGORITHMS, +} from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.types'; + +describe('SelectAnalysesComponentHelpers', () => { + describe('selectedReferenceDatasetIsDefaultDataset', () => { + it('should be truthy for default datasets', () => { + DEFAULT_REFERENCE_DATASETS.forEach((dataset) => { + const result = selectedReferenceDatasetIsDefaultDataset(dataset.id); + expect(result).toBeTruthy(); + }); + }); + + it('should return false for non reference datasets', () => { + const result = selectedReferenceDatasetIsDefaultDataset('random dataset'); + expect(result).toBeFalsy(); + }); + + it('should return false for undefined', () => { + const result = selectedReferenceDatasetIsDefaultDataset(undefined); + expect(result).toBeFalsy(); + }); + }); + + describe('isMultiGroupAlgorithm', () => { + it('should be truthy for multigroup algorithms', () => { + MULTIGROUP_ALGORITHMS.forEach((multigroupAlgorithm) => { + const result = isMultiGroupAlgorithm({ + label: multigroupAlgorithm, + description: '', + }); + expect(result).toBeTruthy(); + }); + }); + + it('should return false for non reference datasets', () => { + const result = isMultiGroupAlgorithm({ label: 'random', description: '' }); + expect(result).toBeFalsy(); + }); + + it('should return false for undefined', () => { + const result = isMultiGroupAlgorithm(undefined); + expect(result).toBeFalsy(); + }); + }); +}); diff --git a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.ts b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.ts index 932d191ff..b58e5d143 100644 --- a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.ts +++ b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers.ts @@ -93,5 +93,5 @@ export const selectedReferenceDatasetIsDefaultDataset = ( ) => { if (!selectedReferenceDataset) return false; - return DEFAULT_REFERENCE_DATASETS.some((x) => x.label === selectedReferenceDataset); + return DEFAULT_REFERENCE_DATASETS.some((x) => x.id === selectedReferenceDataset); }; diff --git a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesSummaryComponent.tsx b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesSummaryComponent.tsx index 23b3b8580..91f9267c6 100644 --- a/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesSummaryComponent.tsx +++ b/compose/neurosynth-frontend/src/components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesSummaryComponent.tsx @@ -76,6 +76,9 @@ const SelectAnalysesSummaryComponent: React.FC<{ return ( + + Included: + {' '} {count.studies} studies {' '} diff --git a/compose/neurosynth-frontend/src/components/Dialogs/EditSpecificationDialog/EditSpecificationDialog.tsx b/compose/neurosynth-frontend/src/components/Dialogs/EditSpecificationDialog/EditSpecificationDialog.tsx index 74b2fce50..26a11285e 100644 --- a/compose/neurosynth-frontend/src/components/Dialogs/EditSpecificationDialog/EditSpecificationDialog.tsx +++ b/compose/neurosynth-frontend/src/components/Dialogs/EditSpecificationDialog/EditSpecificationDialog.tsx @@ -20,12 +20,13 @@ import { } from 'neurosynth-compose-typescript-sdk'; import { useEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; -import BaseDialog, { IDialog } from '../BaseDialog'; -import SelectSpecificationComponent from '../CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationAlgorithmStep/SelectSpecificationComponent/SelectSpecificationComponent'; -import { IAnalysesSelection } from '../CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationDialogBase.types'; -import SelectAnalysesComponent from '../CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent'; -import { isMultiGroupAlgorithm } from '../CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers'; -import { getWeightAndConditionsForSpecification } from '../CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers'; +import BaseDialog, { IDialog } from 'components/Dialogs/BaseDialog'; +import SelectSpecificationComponent from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationAlgorithmStep/SelectSpecificationComponent/SelectSpecificationComponent'; +import { IAnalysesSelection } from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationDialogBase.types'; +import SelectAnalysesComponent from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent'; +import { isMultiGroupAlgorithm } from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers'; +import { getWeightAndConditionsForSpecification } from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationReview/CreateMetaAnalysisSpecificationReview.helpers'; +import CreateMetaAnalysisSpecificationSelectionStepMultiGroup from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/CreateMetaAnalysisSpecificationSelectionStepMultiGroup'; const metaAnalysisSpecification: IMetaAnalysisParamsSpecification = metaAnalysisSpec; @@ -64,6 +65,10 @@ const EditSpecificationDialog: React.FC = (props) => { selectionKey: specification.filter, type: getType(specification?.conditions?.[0]), selectionValue: (specification.conditions || [])[0], + referenceDataset: + specification?.conditions?.[1] !== undefined + ? specification.conditions[1].toString() + : specification?.database_studyset || undefined, }); const estimator = specification?.estimator?.type @@ -103,7 +108,6 @@ const EditSpecificationDialog: React.FC = (props) => { algorithmSpec.estimator, selectedValue ); - mutate( { specificationId: specification.id, @@ -133,6 +137,8 @@ const EditSpecificationDialog: React.FC = (props) => { ); }; + const isMultiGroup = isMultiGroupAlgorithm(algorithmSpec.estimator); + const disabled = useMemo(() => { const isMultiGroup = isMultiGroupAlgorithm(algorithmSpec.estimator); return ( @@ -191,6 +197,16 @@ const EditSpecificationDialog: React.FC = (props) => { }} algorithm={algorithmSpec} /> + {isMultiGroup && ( + setSelectedValue(newVal)} + annotationId={ + (metaAnalysis?.annotation as AnnotationReturn)?.neurostore_id || '' + } + selectedValue={selectedValue} + algorithm={algorithmSpec} + /> + )} { + const queryClient = useQueryClient(); + const { enqueueSnackbar } = useSnackbar(); + return useMutation( + (id) => API.NeurosynthServices.NeurosynthDefaultApi.metaAnalysesIdDelete(id), + { + onSuccess: () => { + queryClient.invalidateQueries('meta-analyses'); + }, + onError: () => { + enqueueSnackbar('There was an error deleting the meta-analysis', { + variant: 'error', + }); + }, + } + ); +}; + +export default useDeleteMetaAnalysis; diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalyses/MetaAnalysisPage/MetaAnalysisPage.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalyses/MetaAnalysisPage/MetaAnalysisPage.tsx index 829989e7e..939b31575 100644 --- a/compose/neurosynth-frontend/src/pages/MetaAnalyses/MetaAnalysisPage/MetaAnalysisPage.tsx +++ b/compose/neurosynth-frontend/src/pages/MetaAnalyses/MetaAnalysisPage/MetaAnalysisPage.tsx @@ -2,6 +2,7 @@ import { useAuth0 } from '@auth0/auth0-react'; import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; import { Box, Button, Link, Paper, Typography } from '@mui/material'; import CodeSnippet from 'components/CodeSnippet/CodeSnippet'; +import { isMultiGroupAlgorithm } from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesComponent.helpers'; import SelectAnalysesSummaryComponent from 'components/Dialogs/CreateMetaAnalysisSpecificationDialog/CreateMetaAnalysisSpecificationSelectionStep/SelectAnalysesComponent/SelectAnalysesSummaryComponent'; import EditSpecificationDialog from 'components/Dialogs/EditSpecificationDialog/EditSpecificationDialog'; import DisplayMetaAnalysisResult from 'components/DisplayMetaAnalysisResult/DisplayMetaAnalysisResult'; @@ -126,6 +127,25 @@ const MetaAnalysisPage: React.FC = (props) => { return `${selectionKey} ${selectionValue}`; }, [specification]); + const referenceDataset = useMemo(() => { + const isMulti = isMultiGroupAlgorithm({ + label: specification?.estimator?.type || '', + description: '', + }); + + if (isMulti) { + return specification?.conditions?.[1] !== undefined + ? specification.conditions[1].toString() + : specification?.database_studyset; + } else { + return null; + } + }, [ + specification?.conditions, + specification?.database_studyset, + specification?.estimator?.type, + ]); + const metaAnalysisTypeDescription = useMemo(() => { return getAnalysisTypeDescription((metaAnalysis?.specification as Specification)?.type); }, [metaAnalysis?.specification]); @@ -296,7 +316,7 @@ const MetaAnalysisPage: React.FC = (props) => { )} - {specification?.database_studyset && ( + {referenceDataset && ( <> { }} /> - Reference Dataset: {specification.database_studyset} + Reference Dataset: {referenceDataset} )} diff --git a/compose/neurosynth-frontend/src/utils/api.ts b/compose/neurosynth-frontend/src/utils/api.ts index 3252a7d4c..47095dc77 100644 --- a/compose/neurosynth-frontend/src/utils/api.ts +++ b/compose/neurosynth-frontend/src/utils/api.ts @@ -21,6 +21,7 @@ import { AnnotationsApi as NeurosynthAnnotationApi, StudysetsApi as NeurosynthStudysetApi, ProjectsApi, + DefaultApi as NeurosynthDefaultApi, } from '../neurosynth-compose-typescript-sdk'; export type NeurostoreAnnotation = AnnotationBase & @@ -62,6 +63,7 @@ const NeurosynthServices = { StudysetsService: new NeurosynthStudysetApi(neurosynthConfig), AnnotationsService: new NeurosynthAnnotationApi(neurosynthConfig), ProjectsService: new ProjectsApi(neurosynthConfig), + NeurosynthDefaultApi: new NeurosynthDefaultApi(neurosynthConfig), }; const UpdateServicesWithToken = (token: string) => {