Skip to content

Commit

Permalink
display Pydantic errors in Settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
toni-neurosc committed Oct 10, 2024
1 parent 2518990 commit 166ad98
Show file tree
Hide file tree
Showing 14 changed files with 441 additions and 281 deletions.
Binary file modified gui_dev/bun.lockb
Binary file not shown.
21 changes: 10 additions & 11 deletions gui_dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,26 @@
"plotly.js-basic-dist-min": "^2.35.2",
"react": "next",
"react-dom": "next",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.2",
"zustand": "next"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
"@eslint/compat": "^1.1.1",
"@vitejs/plugin-react": "^4.3.1",
"@babel/core": "^7.25.7",
"@babel/eslint-parser": "^7.25.7",
"@babel/preset-env": "^7.25.7",
"@babel/preset-react": "^7.25.7",
"@eslint/compat": "^1.2.0",
"@vitejs/plugin-react": "^4.3.2",
"@welldone-software/why-did-you-render": "^8.0.3",
"babel-plugin-react-compiler": "latest",
"eslint": "^9.10.0",
"eslint": "^9.12.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsdoc": "^50.2.3",
"eslint-plugin-react": "^7.36.1",
"eslint-plugin-jsdoc": "^50.3.1",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react-compiler": "latest",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.12",
"prettier": "^3.3.3",
"vite": "^5.4.6"
"vite": "^5.4.8"
}
}
56 changes: 53 additions & 3 deletions gui_dev/src/components/StatusBar/StatusBar.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import { useState } from "react";

import { ResizeHandle } from "./ResizeHandle";
import { SocketStatus } from "./SocketStatus";
import { WebviewStatus } from "./WebviewStatus";
import { useSettingsStore } from "@/stores";

import { useWebviewStore } from "@/stores";

import { Stack } from "@mui/material";
import { Popover, Stack, Typography } from "@mui/material";

export const StatusBar = () => {
const { isWebView } = useWebviewStore((state) => state.isWebView);
const isWebView = useWebviewStore((state) => state.isWebView);
const validationErrors = useSettingsStore((state) => state.validationErrors);

const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);

const handleOpenErrorsPopover = (event) => {
setAnchorEl(event.currentTarget);
};

const handleCloseErrorsPopover = () => {
setAnchorEl(null);
};

return (
<Stack
Expand All @@ -17,8 +32,43 @@ export const StatusBar = () => {
bgcolor="background.level1"
borderTop="2px solid"
borderColor="background.level3"
height="2rem"
>
<WebviewStatus />
{validationErrors?.length > 0 && (
<>
<Typography
variant="body1"
color="tomato"
onClick={handleOpenErrorsPopover}
sx={{ cursor: "pointer" }}
>
{validationErrors?.length} errors found in Settings
</Typography>

<Popover
open={open}
anchorEl={anchorEl}
onClose={handleCloseErrorsPopover}
anchorOrigin={{
vertical: "top",
horizontal: "center",
}}
transformOrigin={{
vertical: "bottom",
horizontal: "center",
}}
>
<Stack px={2} py={1} alignItems="flex-start">
{validationErrors.map((error, index) => (
<Typography key={index} variant="body1" color="tomato">
{index} - [{error.type}] {error.msg}
</Typography>
))}
</Stack>
</Popover>
</>
)}
{/* <WebviewStatus /> */}
{/* Current experiment */}
{/* Current stream */}
{/* Current activity */}
Expand Down
18 changes: 6 additions & 12 deletions gui_dev/src/pages/Settings/Dropdown.jsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
import { useState } from "react";
import "../App.css";

var stringJson =
'{"sampling_rate_features_hz":10.0,"segment_length_features_ms":1000.0,"frequency_ranges_hz":{"theta":{"frequency_low_hz":4.0,"frequency_high_hz":8.0},"alpha":{"frequency_low_hz":8.0,"frequency_high_hz":12.0},"low beta":{"frequency_low_hz":13.0,"frequency_high_hz":20.0},"high beta":{"frequency_low_hz":20.0,"frequency_high_hz":35.0},"low gamma":{"frequency_low_hz":60.0,"frequency_high_hz":80.0},"high gamma":{"frequency_low_hz":90.0,"frequency_high_hz":200.0},"HFA":{"frequency_low_hz":200.0,"frequency_high_hz":400.0}},"preprocessing":["raw_resampling","notch_filter","re_referencing"],"raw_resampling_settings":{"resample_freq_hz":1000.0},"preprocessing_filter":{"bandstop_filter":true,"bandpass_filter":true,"lowpass_filter":true,"highpass_filter":true,"bandstop_filter_settings":{"frequency_low_hz":100.0,"frequency_high_hz":160.0},"bandpass_filter_settings":{"frequency_low_hz":2.0,"frequency_high_hz":200.0},"lowpass_filter_cutoff_hz":200.0,"highpass_filter_cutoff_hz":3.0},"raw_normalization_settings":{"normalization_time_s":30.0,"normalization_method":"zscore","clip":3.0},"postprocessing":{"feature_normalization":true,"project_cortex":false,"project_subcortex":false},"feature_normalization_settings":{"normalization_time_s":30.0,"normalization_method":"zscore","clip":3.0},"project_cortex_settings":{"max_dist_mm":20.0},"project_subcortex_settings":{"max_dist_mm":5.0},"features":{"raw_hjorth":true,"return_raw":true,"bandpass_filter":false,"stft":false,"fft":true,"welch":true,"sharpwave_analysis":true,"fooof":false,"nolds":false,"coherence":false,"bursts":true,"linelength":true,"mne_connectivity":false,"bispectrum":false},"fft_settings":{"windowlength_ms":1000,"log_transform":true,"features":{"mean":true,"median":false,"std":false,"max":false},"return_spectrum":false},"welch_settings":{"windowlength_ms":1000,"log_transform":true,"features":{"mean":true,"median":false,"std":false,"max":false},"return_spectrum":false},"stft_settings":{"windowlength_ms":1000,"log_transform":true,"features":{"mean":true,"median":false,"std":false,"max":false},"return_spectrum":false},"bandpass_filter_settings":{"segment_lengths_ms":{"theta":1000,"alpha":500,"low beta":333,"high beta":333,"low gamma":100,"high gamma":100,"HFA":100},"bandpower_features":{"activity":true,"mobility":false,"complexity":false},"log_transform":true,"kalman_filter":false},"kalman_filter_settings":{"Tp":0.1,"sigma_w":0.7,"sigma_v":1.0,"frequency_bands":["theta","alpha","low_beta","high_beta","low_gamma","high_gamma","HFA"]},"burst_settings":{"threshold":75.0,"time_duration_s":30.0,"frequency_bands":["low beta","high beta","low gamma"],"burst_features":{"duration":true,"amplitude":true,"burst_rate_per_s":true,"in_burst":true}},"sharpwave_analysis_settings":{"sharpwave_features":{"peak_left":false,"peak_right":false,"trough":false,"width":false,"prominence":true,"interval":true,"decay_time":false,"rise_time":false,"sharpness":true,"rise_steepness":false,"decay_steepness":false,"slope_ratio":false},"filter_ranges_hz":[{"frequency_low_hz":5.0,"frequency_high_hz":80.0},{"frequency_low_hz":5.0,"frequency_high_hz":30.0}],"detect_troughs":{"estimate":true,"distance_troughs_ms":10.0,"distance_peaks_ms":5.0},"detect_peaks":{"estimate":true,"distance_troughs_ms":10.0,"distance_peaks_ms":5.0},"estimator":{"mean":["interval"],"median":[],"max":["prominence","sharpness"],"min":[],"var":[]},"apply_estimator_between_peaks_and_troughs":true},"mne_connectivity":{"method":"plv","mode":"multitaper"},"coherence":{"features":{"mean_fband":true,"max_fband":true,"max_allfbands":true},"method":{"coh":true,"icoh":true},"channels":[],"frequency_bands":["high beta"]},"fooof":{"aperiodic":{"exponent":true,"offset":true,"knee":true},"periodic":{"center_frequency":false,"band_width":false,"height_over_ap":false},"windowlength_ms":800.0,"peak_width_limits":{"frequency_low_hz":0.5,"frequency_high_hz":12.0},"max_n_peaks":3,"min_peak_height":0.0,"peak_threshold":2.0,"freq_range_hz":{"frequency_low_hz":2.0,"frequency_high_hz":40.0},"knee":true},"nolds_features":{"raw":true,"frequency_bands":["low beta"],"features":{"sample_entropy":false,"correlation_dimension":false,"lyapunov_exponent":true,"hurst_exponent":false,"detrended_fluctuation_analysis":false}},"bispectrum":{"f1s":{"frequency_low_hz":5.0,"frequency_high_hz":35.0},"f2s":{"frequency_low_hz":5.0,"frequency_high_hz":35.0},"compute_features_for_whole_fband_range":true,"frequency_bands":["theta","alpha","low_beta","high_beta"],"components":{"absolute":true,"real":true,"imag":true,"phase":true},"bispectrum_features":{"mean":true,"sum":true,"var":true}}}';
const nm_settings = JSON.parse(stringJson);

const filterByKeys = (dict, keys) => {
const filterByKeys = (obj, keys) => {
const filteredDict = {};
keys.forEach((key) => {
if (typeof key === "string") {
// Top-level key
if (dict.hasOwnProperty(key)) {
filteredDict[key] = dict[key];
if (obj.hasOwn(key)) {
filteredDict[key] = obj[key];
}
} else if (typeof key === "object") {
// Nested key
const topLevelKey = Object.keys(key)[0];
const nestedKeys = key[topLevelKey];
if (
dict.hasOwnProperty(topLevelKey) &&
typeof dict[topLevelKey] === "object"
) {
filteredDict[topLevelKey] = filterByKeys(dict[topLevelKey], nestedKeys);
if (obj.hasOwn(topLevelKey) && typeof obj[topLevelKey] === "object") {
filteredDict[topLevelKey] = filterByKeys(obj[topLevelKey], nestedKeys);
}
}
});
return filteredDict;
};

const Dropdown = ({ onChange, keysToInclude }) => {
export const Dropdown = ({ onChange, keysToInclude }) => {
const filteredSettings = filterByKeys(nm_settings, keysToInclude);
const [selectedOption, setSelectedOption] = useState("");

Expand Down Expand Up @@ -62,5 +58,3 @@ const Dropdown = ({ onChange, keysToInclude }) => {
</div>
);
};

export default Dropdown;
8 changes: 6 additions & 2 deletions gui_dev/src/pages/Settings/FrequencyRange.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ export const FrequencyRangeList = ({

const updatedRanges = {
...ranges,
[newName]: { frequency_low_hz: 1, frequency_high_hz: 500 },
[newName]: {
__field_type__: "FrequencyRange",
frequency_low_hz: 1,
frequency_high_hz: 500,
},
};
onChange(["frequency_ranges_hz"], updatedRanges);

Expand All @@ -147,7 +151,7 @@ export const FrequencyRangeList = ({
};

return (
<Stack>
<Stack gap={1}>
{rangeOrder.map((name, index) => (
<FrequencyRange
key={index}
Expand Down
Loading

0 comments on commit 166ad98

Please sign in to comment.