Skip to content

Commit

Permalink
Various improvements to the logic of the Data Visualization module an…
Browse files Browse the repository at this point in the history
…d Dialog-based form components.
  • Loading branch information
MauricePasternak committed Aug 10, 2022
1 parent 66301e6 commit 6539528
Show file tree
Hide file tree
Showing 21 changed files with 662 additions and 99 deletions.
58 changes: 50 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,40 @@ Versions are in format: `[MAJOR.MINOR.BUGFIX]`

Dates are in format: `YYYY-MM-DD`

## [0.2.2] - 2022-08-09

Various improvements to the logic of the Data Visualization module and Dialog-based form components.

### Fixed

- Fixed a bug where multiple dialog windows could be opened for dialog-based components. A useRef was used to track
the dialog's open state to prevent this.

- Fixed a bug in Data Visualization where changing between plots did not reset all the plotting variables.

- Fixed a bug where additional hover data permitted the selection of variables that were already dedicated to X/Y axis,
causing a doubling effect to occur. This has been fixed twofold:
- Variables that are already dedicated to X/Y are disabled in the "additional hover data" section.
- Selecting a variable for X/Y that is already in "additional hover data" will cause it to be removed from the latter.

- Fixed a bug in Data Visualization Swarmplot where changing the orientation field did not change said orientation.

### Added

- Help sections have been added for the first two steps in the Data Visualization module. The main plotting page itself
hasn't been given any help buttons for the time being.

- The main plotting page's typography has been wrapped in a CardHeader component for a more aesthetically-pleasing look
and consistency with the previous steps.

### Changed

- Hovering over datapoints in Data Visualization now has several changes:
- Subject and session are explicitly stated in the hoverdata instead of the previous ambiguous "ID" label
- Instead of X and Y, the variable names are now explicitly stated in the hoverlabel

- Swarmplot's continuous var axis is now better scaled so that the maximum/minimum values do not appear at the edges.

---

## [0.2.1] - 2022-08-08
Expand All @@ -14,19 +48,23 @@ Additional information added to README and reformatted RunImportModule visuals.

### Fixed

- Fixed a bug where saving a DataPar.json file in Define Parameters resulting in snackbar feedback that did not respect OS-specific filepath delimiters
- Fixed a bug where saving a DataPar.json file in Define Parameters resulting in snackbar feedback that did not respect
OS-specific filepath delimiters

- Fixed a bug in MRIViewSettings where the maximum value exceeded the possible indexable value of MRI slices by 1 due to Javascript's zero-based indexing vs MNI space indexing discrepancy, causing crashes.
- Fixed a bug in MRIViewSettings where the maximum value exceeded the possible indexable value of MRI slices by 1 due
to Javascript's zero-based indexing vs MNI space indexing discrepancy, causing crashes.

- Fixed a bug in StructureByParts where selecting a new value in the Select components did not trigger validation for that field. Changing any of the selects now forces validation.
- Fixed a bug in StructureByParts where selecting a new value in the Select components did not trigger validation for
that field. Changing any of the selects now forces validation.

### Added

- Added some initial RHFInterDep components to begin addressing the issues of interdependent fields w.r.t validation.

- Started adding some configuration for the auto-update feature in `package.json`.

- Modules that fail to complete will now give some feedback to the particular subjects/visits/sessions and the step where they did not complete.
- Modules that fail to complete will now give some feedback to the particular subjects/visits/sessions and the step
where they did not complete.

### Changed

Expand All @@ -40,11 +78,13 @@ Important bugfixes and successful deployment on Windows 10.

### Fixed

- Fixed bugs in DataVisualization where previous graphical settings would not be reset if the user backtracked to load a new dataframe
- Fixed bugs in DataVisualization where previous graphical settings would not be reset if the user backtracked to load
a new dataframe

- Fixed `calculateASLWorkload` to anticipate proper lock paths. Previously, it would early-exit due to issues with the session regex and a ES6 parsing error involving an if statement.

- Fixed `calculateStructuralWorkload` which made the mistake of using an async forEach loop, causing early return of the function. This has been fixed to use a traditional for-loop.
- Fixed `calculateStructuralWorkload` which made the mistake of using an async forEach loop, causing early return of
the function. This has been fixed to use a traditional for-loop.

- Fixed a bug in which the StepRunImportModule component did not communicate the channel name when pausing/resuming/terminating

Expand All @@ -54,13 +94,15 @@ Important bugfixes and successful deployment on Windows 10.

- Added scripts for generating an Inno wizard installer for the program on Windows as an alternative to Squirrel

- Added a compiled node mode `win32-x64_lib.node` to backend. This should allow for circumventing the ntsuspend optional dep.
- Added a compiled node mode `win32-x64_lib.node` to backend. This should allow for circumventing the ntsuspend
optional dep.

- Added legend settings to Scatterplot. Users can now alter legend symbol size, label fontsize, item spacing, etc.

### Changed

- Change the DrawerItems in GUIFrame such that the parent item of a nested structure will now be a ListItemButton which, when clicked, will expand/collapse the nested structure
- Change the DrawerItems in GUIFrame such that the parent item of a nested structure will now be a ListItemButton
which, when clicked, will expand/collapse the nested structure

---

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ Additional features to keep in mind:
- [x] Create a Data Visualization Module.
- [x] Add plot settings for plot legends (i.e. legend text fontsize, positioning, etc.) within the DataVisualization Module.
- [x] Add more helpful information in the Process Studies module for when a study does not fully complete.
- [ ] Add help information on the steps within the Data Visualization Module.
- [x] Add help information on the steps within the Data Visualization Module.
- [ ] Add plot settings for renaming axis main labels within the Data Visualization Module.
- [ ] Add Multiprocessing Capability to the Import Module as well. \*\*
- [ ] Add a submodule to Process Studies where users can pin-point change the JSON sidecars of individual subjects/visits/sessions.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ExploreASLJS",
"productName": "ExploreASLJS",
"version": "0.2.1",
"version": "0.2.2",
"description": "A user interface for the analysis of arterial spin labeling (ASL) images",
"repository": "https://github.com/MauricePasternak/ExploreASLJS",
"main": ".webpack/main",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 9 additions & 3 deletions src/common/utilityFunctions/dataFrameFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,22 +440,28 @@ export function toNivoSwarmPlotDataSingle(df: DataFrame, id: string, value: stri

export function toNivoSwarmPlotDataGroupBy(
df: DataFrame,
id: string,
id: string | string[],
value: string,
group: string,
...other: string[]
) {
console.log(`toNivoSwarmPlotDataGroupBy invoked with id: ${id}, value: ${value}, group: ${group}, other: ${other}`);

const groups = df.getSeries(group).distinct().toArray();
const iid = Array.isArray(id) ? id : [id];
const data = df
.subset([id, value, group, ...other])
.subset([...iid, value, group, ...other])
.toArray()
.map(obj => {
const groupName = obj[group];
// const result = { ...lodashPick(obj, other), id: obj[id], value: obj[value], group: groupName };
// return result;
return { ...lodashPick(obj, other), id: obj[id], value: obj[value], group: groupName };
return {
...lodashPick(obj, other),
id: Array.isArray(id) ? `${obj[id[0]]}_${obj[id[1]]}` : obj[id],
value: obj[value],
group: groupName,
};
});
return [groups, data] as const;
}
9 changes: 7 additions & 2 deletions src/components/AtomicSnackbarMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,17 @@ function AtomicSnackbarMessage({
return message.map((msg, idx) => {
const key = `AtomicSnackbarMessageLine_${idx}`;
// Array of strings
if (typeof msg === "string") return /^\s+$/gm.test(msg) ? <br /> : <Typography key={key}>{msg}</Typography>;
if (typeof msg === "string")
return /^\s+$/gm.test(msg) ? <br key={key} /> : <Typography key={key}>{msg}</Typography>;
// Array of SnackbarMessageType objects
return msg;
});
} else if (typeof message === "string") {
return /^\s+$/gm.test(message) ? <br /> : <Typography key={`AtomicSnackbarMessageLine`}>{message}</Typography>;
return /^\s+$/gm.test(message) ? (
<br key={`AtomicSnackbarMessageLine`} />
) : (
<Typography key={`AtomicSnackbarMessageLine`}>{message}</Typography>
);
} else {
return message;
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/FormComponents/RHFFilepathDropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ function ControlledFilepathDropzone<TValues extends FieldValues, TName extends P
const hasError = !!fieldState.error;
const [isAcceptingDrop, setIsAcceptingDrop] = React.useState(false);
const enterCounter = useRef(0); // We use this to keep track of enter/exit drop state
const isDialogOpened = useRef(false); // We use this to keep track of dialog state

// console.log(
// `RHFFIlepathDropzone with fieldname ${field.name} has rendered with value and innerValue:`,
Expand Down Expand Up @@ -284,10 +285,13 @@ function ControlledFilepathDropzone<TValues extends FieldValues, TName extends P
};

const handleDialog = async () => {
if (isDialogOpened.current) return;
if (!dialogOptions) throw new Error("dialogOptions were not specified");

// Get the filepaths from the dialog and early exit if no files were selected
isDialogOpened.current = true;
const { canceled, filePaths } = await api.invoke("Dialog:OpenDialog", dialogOptions);
isDialogOpened.current = false;
if (canceled) return;

// Filter the filepaths and update the innerVal state
Expand Down
7 changes: 5 additions & 2 deletions src/components/FormComponents/RHFFilepathTextfield.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState, useEffect } from "react";
import React, { useCallback, useState, useEffect, useRef } from "react";
import TextField, { TextFieldProps } from "@mui/material/TextField";
import { Controller, FieldValues, Path } from "react-hook-form";
import { useDebouncedCallback } from "use-debounce";
Expand Down Expand Up @@ -57,7 +57,7 @@ export function ControlledFilepathTextField<TValues extends FieldValues, TName e
const hasError = !!fieldState.error;
const errorMessage = hasError ? fieldState.error?.message : "";
const debouncedHandleChange = useDebouncedCallback(field.onChange, debounceTime);

const isDialogOpened = useRef(false);
// console.log(`RHFFilepathTextField with name ${field.name} has field.value and innerValue: `, field.value, innerVal);

/**
Expand Down Expand Up @@ -154,7 +154,10 @@ export function ControlledFilepathTextField<TValues extends FieldValues, TName e
);

const handleDialogueClick = async () => {
if (isDialogOpened.current) return;
isDialogOpened.current = true;
const { canceled, filePaths } = await api.invoke("Dialog:OpenDialog", dialogOptions);
isDialogOpened.current = false;
!canceled && handleChange(filePaths[0]);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import FormHelperText from "@mui/material/FormHelperText";
import IconButton from "@mui/material/IconButton";
import TextField, { TextFieldProps } from "@mui/material/TextField";
import { OpenDialogOptions } from "electron";
import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { Controller, FieldValues, Path } from "react-hook-form";
import { useDebouncedCallback } from "use-debounce";
import { RHFFieldAndFieldStateType, RHFInterDepBaseProps, RHFTriggerType } from "../../common/types/formTypes";
Expand Down Expand Up @@ -64,6 +64,7 @@ export function InterDepControlledFilepathTextField<
const [innerVal, setInnerVal] = useState(field.value);
const hasError = !!fieldState.error;
const errorMessage = hasError ? fieldState.error?.message : "";
const isDialogOpened = useRef(false);

// Setup default dialog options if a dialogOptions object was not provided and a button is to be presented
if (!dialogOptions && includeButton) {
Expand Down Expand Up @@ -171,7 +172,10 @@ export function InterDepControlledFilepathTextField<
);

const handleDialogueClick = async () => {
if (isDialogOpened.current) return;
isDialogOpened.current = true;
const { canceled, filePaths } = await api.invoke("Dialog:OpenDialog", dialogOptions);
isDialogOpened.current = false;
!canceled && handleChange(filePaths[0]);
};

Expand Down
35 changes: 26 additions & 9 deletions src/pages/DataVisualization/DataVizPlots/EASLScatterplot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,23 @@ function EASLScatterplot() {
const setMRIDataStats = useSetAtom(atomMRIDataStats);
const [currentMRIViewSubject, setMRICurrentViewSubject] = useAtom(atomCurrentMRIViewSubject);
const scatterplotSettings = useAtomValue(atomEASLScatterplotSettings);

const SubjectSessionSpread = dataFrame.hasSeries("session") ? ["SUBJECT", "session"] : ["SUBJECT"];

const data = dataVarsSchema.GroupingVar
? toNivoScatterPlotDataGroupBy(
dataFrame,
dataVarsSchema.XAxisVar,
dataVarsSchema.YAxisVar,
dataVarsSchema.GroupingVar,
"SUBJECT",
...SubjectSessionSpread,
...dataVarsSchema.HoverVariables
)
: toNivoScatterPlotDataSingle(
dataFrame,
dataVarsSchema.XAxisVar,
dataVarsSchema.YAxisVar,
"SUBJECT",
...SubjectSessionSpread,
...dataVarsSchema.HoverVariables
);

Expand All @@ -71,7 +74,7 @@ function EASLScatterplot() {
if (!(await api.path.filepathExists(popPath.path))) return;

// console.log("handleLoadSubject -- found popPath: ", popPath.path);
const qCBFFiles = await api.path.glob(popPath.path, `qCBF_${subjectToLoad}_*.nii*`);
const qCBFFiles = await api.path.glob(popPath.path, `qCBF_${subjectToLoad}*.nii*`);
if (qCBFFiles.length === 0) return;

// console.log("handleLoadSubject -- found qCBFFiles: ", qCBFFiles);
Expand Down Expand Up @@ -147,9 +150,20 @@ function EASLScatterplot() {
return (
<Paper elevation={2} sx={{ p: 1 }}>
<Stack>
<Typography component={"strong"}>ID: {data["SUBJECT" as "x"]}</Typography>
<Typography>X: {data.x}</Typography>
<Typography>Y: {data.y}</Typography>
{SubjectSessionSpread.length === 1 ? (
<Typography component={"strong"}>Subject/Visit: {data["SUBJECT" as "x"]}</Typography>
) : (
<>
<Typography component={"strong"}>Subject/Visit: {data["SUBJECT" as "x"]}</Typography>
<Typography component={"strong"}>Session: {data["session" as "x"]}</Typography>
</>
)}
<Typography>
{dataVarsSchema.XAxisVar}: {data.x}
</Typography>
<Typography>
{dataVarsSchema.YAxisVar}: {data.y}
</Typography>
{...dataVarsSchema.HoverVariables.map(varName => (
<Typography key={varName}>
{varName}: {data[varName as "x"]}
Expand Down Expand Up @@ -193,12 +207,15 @@ function EASLScatterplot() {
legends: {
text: {
fontSize: scatterplotSettings.theme.legendTextFontSize,
}
}
},
},
}}
onClick={async ({ data }) => {
if (!("SUBJECT" in data)) return;
await handleLoadSubject(data["SUBJECT" as "x"]);
const subjectToLoad =
"session" in data ? `${data["SUBJECT" as "x"]}_${data["session" as "y"]}` : data["SUBJECT" as "x"];
console.log("Scatterplot trying to load in subject/session: ", subjectToLoad);
await handleLoadSubject(subjectToLoad);
}}
/>
);
Expand Down
Loading

0 comments on commit 6539528

Please sign in to comment.