Skip to content

Commit

Permalink
refactored default time variable into appState initialisation and got…
Browse files Browse the repository at this point in the history
… rid of a useEffect
  • Loading branch information
bobular committed Sep 7, 2023
1 parent 28c0a68 commit a5bf210
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 71 deletions.
68 changes: 67 additions & 1 deletion packages/libs/eda/src/lib/core/hooks/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ import {
import { ComputeClient } from '../api/ComputeClient';
import { DownloadClient } from '../api';
import { Filter } from '../types/filter';
import { mapStructure } from '@veupathdb/wdk-client/lib/Utils/TreeUtils';
import {
mapStructure,
preorder,
} from '@veupathdb/wdk-client/lib/Utils/TreeUtils';
import {
useFeaturedFieldsFromTree,
useFieldTree,
useFlattenedFields,
} from '../components/variableTrees/hooks';
import { findFirstVariable } from '../../workspace/Utils';
import {
DataElementConstraintRecord,
filterVariablesByConstraint,
} from '../utils/data-element-constraints';

/** Return the study identifier and a hierarchy of the study entities. */
export function useStudyMetadata(): StudyMetadata {
Expand Down Expand Up @@ -262,3 +269,62 @@ export function useGetDefaultVariableDescriptor() {
[entities, featuredFields, fieldTree]
);
}

export const timeSliderVariableConstraints: DataElementConstraintRecord[] = [
{
overlayVariable: {
isRequired: true,
minNumVars: 1,
maxNumVars: 1,
// TODO: testing with SCORE S. mansoni Cluster Randomized Trial study
// however, this study does not have date variable, thus temporarily use below for test purpose
// i.e., additionally allowing 'integer'
allowedTypes: ['date', 'integer'],
// TODO: below two are correct ones
// allowedTypes: ['date'],
// isTemporal: true,
},
},
];

export function useGetDefaultTimeVariableDescriptor() {
const entities = useStudyEntities();
// filter constraint for time slider inputVariables component

return useCallback(
function getDefaultTimeVariableDescriptor() {
const temporalVariableTree = filterVariablesByConstraint(
entities[0],
timeSliderVariableConstraints[0]['overlayVariable']
);

// take the first suitable variable from the filtered variable tree

// first find the first entity with some variables that passed the filter
const defaultTimeSliderEntity: StudyEntity | undefined = Array.from(
preorder(temporalVariableTree, (entity) => entity.children ?? [])
)
// not all `variables` are actually variables, so we filter to be sure
.filter(
(entity) =>
entity.variables.filter((variable) => Variable.is(variable))
.length > 0
)[0];

// then take the first variable from it
const defaultTimeSliderVariable: Variable | undefined =
defaultTimeSliderEntity.variables.filter(
(variable): variable is Variable => Variable.is(variable)
)[0];

return defaultTimeSliderEntity != null &&
defaultTimeSliderVariable != null
? {
entityId: defaultTimeSliderEntity.id,
variableId: defaultTimeSliderVariable.id,
}
: undefined;
},
[entities, timeSliderVariableConstraints]
);
}
72 changes: 4 additions & 68 deletions packages/libs/eda/src/lib/map/analysis/DraggableTimeFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
import { useState, useMemo, useEffect, useCallback } from 'react';
import { useMemo, useEffect, useCallback } from 'react';
import { DraggablePanel } from '@veupathdb/coreui/lib/components/containers';
import EzTimeFilter, {
EZTimeFilterDataProp,
} from '@veupathdb/components/lib/components/plotControls/EzTimeFilter';
import { InputVariables } from '../../core/components/visualizations/InputVariables';
import {
VariablesByInputName,
DataElementConstraintRecord,
filterVariablesByConstraint,
} from '../../core/utils/data-element-constraints';
import { AnalysisState, usePromise } from '../../core';
import { VariablesByInputName } from '../../core/utils/data-element-constraints';
import { timeSliderVariableConstraints, usePromise } from '../../core';
import {
DateVariable,
NumberVariable,
StudyEntity,
Variable,
} from '../../core/types/study';
import { VariableDescriptor } from '../../core/types/variable';

import { SubsettingClient } from '../../core/api';
import Spinner from '@veupathdb/components/lib/components/Spinner';
import { useFindEntityAndVariable, Filter } from '../../core';
import {
DateRange,
NumberRange,
} from '@veupathdb/components/lib/types/general';
import { DateRangeFilter, NumberRangeFilter } from '../../core/types/filter';
import { Tooltip } from '@material-ui/core';
import { preorder } from '@veupathdb/wdk-client/lib/Utils/TreeUtils';
import { zip } from 'lodash';
import { AppState } from './appState';

Expand Down Expand Up @@ -62,58 +50,6 @@ export default function DraggableTimeFilter({
active, // to do - add a toggle to enable/disable
setActive, // the small filter and grey everything out
}: Props) {
// filter constraint for time slider inputVariables component
const timeSliderVariableConstraints: DataElementConstraintRecord[] = [
{
overlayVariable: {
isRequired: true,
minNumVars: 1,
maxNumVars: 1,
// TODO: testing with SCORE S. mansoni Cluster Randomized Trial study
// however, this study does not have date variable, thus temporarily use below for test purpose
// i.e., additionally allowing 'integer'
allowedTypes: ['date', 'integer'],
// TODO: below two are correct ones
// allowedTypes: ['date'],
// isTemporal: true,
},
},
];

const temporalVariableTree = filterVariablesByConstraint(
entities[0],
timeSliderVariableConstraints[0]['overlayVariable']
);

// take the first suitable variable from the filtered variable tree

// first find the first entity with some variables that passed the filter
const defaultTimeSliderEntity: StudyEntity | undefined = Array.from(
preorder(temporalVariableTree, (entity) => entity.children ?? [])
)
// not all `variables` are actually variables, so we filter to be sure
.filter(
(entity) =>
entity.variables.filter((variable) => Variable.is(variable)).length > 0
)[0];

// then take the first variable from it
const defaultTimeSliderVariable: Variable | undefined =
defaultTimeSliderEntity.variables.filter((variable): variable is Variable =>
Variable.is(variable)
)[0];

// sorry, a useEffect for initialising the default variable
useEffect(() => {
if (variable == null && defaultTimeSliderVariable != null) {
setVariable({
variableId: defaultTimeSliderVariable.id,
entityId: defaultTimeSliderEntity.id,
});
}
}, [variable, defaultTimeSliderVariable]);

// find variable metadata: use timeSliderVariable, not defaultTimeSlider ids
const findEntityAndVariable = useFindEntityAndVariable();
const variableMetadata = findEntityAndVariable(variable);

Expand Down Expand Up @@ -214,7 +150,7 @@ export default function DraggableTimeFilter({
}, [getTimeSliderData]);

// if no variable in a study is suitable to time slider, do not show time slider
return defaultTimeSliderVariable != null ? (
return variable != null ? (
<DraggablePanel
showPanelTitle
panelTitle={'Time Slider'}
Expand Down
22 changes: 20 additions & 2 deletions packages/libs/eda/src/lib/map/analysis/appState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isEqual } from 'lodash';
import { useCallback, useEffect, useMemo } from 'react';
import {
AnalysisState,
useGetDefaultTimeVariableDescriptor,
useGetDefaultVariableDescriptor,
useStudyMetadata,
} from '../../core';
Expand Down Expand Up @@ -131,11 +132,16 @@ export function useAppState(uiStateKey: string, analysisState: AnalysisState) {
studyMetadata.rootEntity.id
);

const getDefaultTimeVariableDescriptor =
useGetDefaultTimeVariableDescriptor();
const defaultTimeVariable = getDefaultTimeVariableDescriptor();

const defaultAppState: AppState = useMemo(
() => ({
viewport: defaultViewport,
mouseMode: 'default',
activeMarkerConfigurationType: 'pie',
timeSliderVariable: defaultTimeVariable,
markerConfigurations: [
{
type: 'pie',
Expand Down Expand Up @@ -182,11 +188,16 @@ export function useAppState(uiStateKey: string, analysisState: AnalysisState) {
)
);

if (missingMarkerConfigs.length > 0) {
const timeVariableIsMissing = appState.timeSliderVariable == null;

if (missingMarkerConfigs.length > 0 || timeVariableIsMissing) {
setVariableUISettings((prev) => ({
...prev,
[uiStateKey]: {
...appState,
...(timeVariableIsMissing
? { timeSliderVariable: defaultTimeVariable }
: {}),
markerConfigurations: [
...appState.markerConfigurations,
...missingMarkerConfigs,
Expand All @@ -196,7 +207,14 @@ export function useAppState(uiStateKey: string, analysisState: AnalysisState) {
}
}
}
}, [analysis, appState, setVariableUISettings, uiStateKey, defaultAppState]);
}, [
analysis,
appState,
setVariableUISettings,
uiStateKey,
defaultAppState,
defaultTimeVariable,
]);

function useSetter<T extends keyof AppState>(key: T) {
return useCallback(
Expand Down

0 comments on commit a5bf210

Please sign in to comment.