diff --git a/app/utils/pyodide/cell.js b/app/utils/pyodide/cell.js deleted file mode 100644 index d81a17f..0000000 --- a/app/utils/pyodide/cell.js +++ /dev/null @@ -1,114 +0,0 @@ -/* eslint-disable */ -// File we be replaced by pyodide update -import * as path from 'path'; -import { readFileSync } from 'fs'; -import { RESOURCE_PATH } from '../../constants/constants'; - -export const imports = () => - [ - 'from mne import Epochs, find_events, set_eeg_reference, read_epochs, concatenate_epochs', - 'from time import time, strftime, gmtime', - 'import os', - 'from collections import OrderedDict', - 'from glob import glob', - 'from mne import create_info, concatenate_raws', - 'from mne.io import RawArray', - 'from mne.io import RawArray', - 'from mne.channels import read_montage', - 'import pandas as pd', - 'import numpy as np', - 'import seaborn as sns', - 'from matplotlib import pyplot as plt', - "plt.style.use('fivethirtyeight')", - ].join('\n'); - -export const utils = () => - readFileSync(path.join(RESOURCE_PATH, '/utils/jupyter/utils.py'), 'utf8'); - -export const loadCSV = (filePathArray: Array) => - [ - `files = [${filePathArray.map((filePath) => formatFilePath(filePath))}]`, - `replace_ch_names = None`, - `raw = load_data(files, replace_ch_names)`, - ].join('\n'); - -export const loadCleanedEpochs = (filePathArray: Array) => - [ - `files = [${filePathArray.map((filePath) => formatFilePath(filePath))}]`, - `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, - `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})`, - ].join('\n'); - -// NOTE: this command includes a ';' to prevent returning data -export const filterIIR = (lowCutoff: number, highCutoff: number) => - `raw.filter(${lowCutoff}, ${highCutoff}, method='iir');`; - -export const plotPSD = () => - [`%matplotlib inline`, `raw.plot_psd(fmin=1, fmax=30)`].join('\n'); - -export const epochEvents = ( - eventIDs: { - [key: string]: number; - }, - tmin: number, - tmax: number, - reject: Array | string = 'None' -) => { - const IDs = Object.keys(eventIDs) - .filter((k) => k !== '') - .reduce((res, key) => ((res[key] = eventIDs[key]), res), {}); - const command = [ - `event_id = ${JSON.stringify(IDs)}`, - `tmin=${tmin}`, - `tmax=${tmax}`, - `baseline= (tmin, tmax)`, - `picks = None`, - `reject = ${reject}`, - 'events = find_events(raw)', - `raw_epochs = Epochs(raw, events=events, event_id=event_id, - tmin=tmin, tmax=tmax, baseline=baseline, reject=reject, preload=True, - verbose=False, picks=picks)`, - `conditions = OrderedDict({key: [value] for (key, value) in raw_epochs.event_id.items()})`, - ].join('\n'); - return command; -}; - -export const requestEpochsInfo = (variableName: string) => - `get_epochs_info(${variableName})`; - -export const requestChannelInfo = () => - `[ch for ch in clean_epochs.ch_names if ch != 'Marker']`; - -export const cleanEpochsPlot = () => - [ - `%matplotlib`, - `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)`, - ].join('\n'); - -export const plotTopoMap = () => - [`%matplotlib inline`, `plot_topo(clean_epochs, conditions)`].join('\n'); - -export const plotERP = (channelIndex: number | string) => - [ - `%matplotlib inline`, - `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions`, - `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, - ci=97.5, n_boot=1000, title='', diff_waveform=None)`, - ].join('\n'); - -export const saveEpochs = (workspaceDir: string, subject: string) => - `raw_epochs.save(${formatFilePath( - path.join( - workspaceDir, - 'Data', - subject, - 'EEG', - `${subject}-cleaned-epo.fif` - ) - )})`; - -// ------------------------------------------- -// Helper methods - -const formatFilePath = (filePath: string) => - `"${filePath.replace(/\\/g, '/')}"`; diff --git a/app/utils/pyodide/functions.js b/app/utils/pyodide/functions.js deleted file mode 100644 index bf67475..0000000 --- a/app/utils/pyodide/functions.js +++ /dev/null @@ -1,39 +0,0 @@ -// ------------------------------------------- -// Helper & utility functions - -export const parseSingleQuoteJSON = (string: string) => - JSON.parse(string.replace(/'/g, '"')); - -export const debugParseMessage = (msg: Record) => { - let content = ''; - switch (msg.channel) { - case 'iopub': - if (msg.content.execution_state) { - content = JSON.stringify(msg.content); - } - - if (msg.content.code) { - content = msg.content.code.slice(0, 300).concat('...'); - } - - if (msg.content.text) { - content = msg.content.text; - } - - if (msg.content.data) { - content = JSON.stringify(Object.keys(msg.content.data)); - } - - break; - case 'shell': - content = JSON.stringify(msg.content); - break; - - default: - content = JSON.stringify(msg); - } - return `${msg.channel} ${content}`; -}; - -export const formatFilePath = (filePath: string) => - `"${filePath.replace(/\\/g, '/')}"`; diff --git a/app/utils/pyodide/functions.ts b/app/utils/pyodide/functions.ts new file mode 100644 index 0000000..eb14977 --- /dev/null +++ b/app/utils/pyodide/functions.ts @@ -0,0 +1,8 @@ +// ------------------------------------------- +// Helper & utility functions + +export const parseSingleQuoteJSON = (string: string) => + JSON.parse(string.replace(/'/g, '"')); + +export const formatFilePath = (filePath: string) => + `"${filePath.replace(/\\/g, '/')}"`; diff --git a/app/utils/pyodide/index.js b/app/utils/pyodide/index.ts similarity index 75% rename from app/utils/pyodide/index.js rename to app/utils/pyodide/index.ts index 7f19f45..c23703b 100644 --- a/app/utils/pyodide/index.js +++ b/app/utils/pyodide/index.ts @@ -2,6 +2,8 @@ import * as path from 'path'; import { readFileSync } from 'fs'; import { formatFilePath } from './functions'; +declare const pyodideWorker: Worker; + // --------------------------------- // This file contains the JS functions that allow the app to access python-wasm through pyodide // These functions wrap the python strings defined in the @@ -19,19 +21,23 @@ export const loadUtils = async () => }); export const loadCSV = async (csvArray: Array) => { - window.csvArray = csvArray; // TODO: Pass attached variable name as parameter to load_data + // @ts-expect-error + window.csvArray = csvArray; await pyodideWorker.postMessage({ data: `raw = load_data()` }); }; // --------------------------- // MNE-Related Data Processing -// export const loadCleanedEpochs = (epocsArray: Array) => -// [ -// `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, -// `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})` -// ].join("\n"); +export const loadCleanedEpochs = async (epochsArray: string[]) => { + await pyodideWorker.postMessage({ + data: [ + `clean_epochs = concatenate_epochs([read_epochs(file) for file in ${epochsArray}])`, + `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})`, + ].join('\n'), + }); +}; // NOTE: this command includes a ';' to prevent returning data export const filterIIR = async (lowCutoff: number, highCutoff: number) => @@ -40,10 +46,10 @@ export const filterIIR = async (lowCutoff: number, highCutoff: number) => }); export const epochEvents = async ( - eventIDs: { [string]: number }, + eventIDs: { [k: string]: number }, tmin: number, tmax: number, - reject?: Array | string = 'None' + reject?: string[] | 'None' ) => pyodideWorker.postMessage({ data: [ @@ -78,24 +84,29 @@ export const requestChannelInfo = async () => export const cleanEpochsPlot = async () => { // TODO: Figure out how to get image results from pyodide - pyodideWorker.postMessage({ + await pyodideWorker.postMessage({ data: `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)`, }); }; export const plotPSD = async () => { // TODO: Figure out how to get image results from pyodide - pyodideWorker.postMessage({ data: `raw.plot_psd(fmin=1, fmax=30)` }); + return pyodideWorker.postMessage({ data: `raw.plot_psd(fmin=1, fmax=30)` }); }; export const plotTopoMap = async () => { // TODO: Figure out how to get image results from pyodide - pyodideWorker.postMessage({ data: `plot_topo(clean_epochs, conditions)` }); + return pyodideWorker.postMessage({ + data: `plot_topo(clean_epochs, conditions)`, + }); }; -export const plotERP = (channelIndex: number) => - `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, - ci=97.5, n_boot=1000, title='', diff_waveform=None)`; +export const plotERP = async (channelIndex: number) => { + return pyodideWorker.postMessage({ + data: `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, + ci=97.5, n_boot=1000, title='', diff_waveform=None)`, + }); +}; export const saveEpochs = (workspaceDir: string, subject: string) => pyodideWorker.postMessage({ diff --git a/app/utils/pyodide/pipes.js b/app/utils/pyodide/pipes.js deleted file mode 100644 index efbe5a6..0000000 --- a/app/utils/pyodide/pipes.js +++ /dev/null @@ -1,26 +0,0 @@ -import { pipe } from 'rxjs'; -import { map, pluck, filter, take, mergeMap } from 'rxjs/operators'; -import { executeRequest } from '@nteract/messaging'; -import { PyodideActions } from '../../actions'; -import { RECEIVE_EXECUTE_REPLY } from '../../epics/pyodideEpics'; - -// Refactor this so command can be calculated either up stream or inside pipe -export const execute = (command, state$) => - pipe( - map(() => state$.value.pyodide.mainChannel.next(executeRequest(command))) - ); - -export const awaitOkMessage = (action$) => - pipe( - mergeMap(() => - action$ - .ofType(PyodideActions.ReceiveExecuteReply.type) - .pipe( - pluck('payload'), - filter < - any > - ((msg) => msg.channel === 'shell' && msg.content.status === 'ok'), - take(1) - ) - ) - ); diff --git a/app/utils/pyodide/statements.json b/app/utils/pyodide/statements.json deleted file mode 100644 index 03cc369..0000000 --- a/app/utils/pyodide/statements.json +++ /dev/null @@ -1,84 +0,0 @@ -// DEPRECATED - -// import * as path from "path"; -// import { readFileSync } from "fs"; - -// // The output of the functions contained in this file are python commands encoded as strings -// // that would be run in a notebook environment in order to perform the experimental analyses underlying BrainWaves - -// export const utils = () => -// readFileSync(path.join(__dirname, "/utils/pyodide/utils.py"), "utf8"); - -// // export const loadCSV = (filePathArray: Array) => -// // [ -// // `files = [${filePathArray.map(filePath => formatFilePath(filePath))}]`, -// // `replace_ch_names = None`, -// // `raw = load_data(files, replace_ch_names)` -// // ].join("\n"); - -// // export const loadCleanedEpochs = (filePathArray: Array) => -// // [ -// // `files = [${filePathArray.map(filePath => formatFilePath(filePath))}]`, -// // `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, -// // `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})` -// // ].join("\n"); - -// // NOTE: this command includes a ';' to prevent returning data -// export const filterIIR = (lowCutoff: number, highCutoff: number) => -// `raw.filter(${lowCutoff}, ${highCutoff}, method='iir');`; - -// export const plotPSD = () => -// [`%matplotlib inline`, `raw.plot_psd(fmin=1, fmax=30)`].join("\n"); - -// export const epochEvents = ( -// eventIDs: { [string]: number }, -// tmin: number, -// tmax: number, -// reject?: Array | string = "None" -// ) => -// [ -// `event_id = ${JSON.stringify(eventIDs)}`, -// `tmin=${tmin}`, -// `tmax=${tmax}`, -// `baseline= (tmin, tmax)`, -// `picks = None`, -// `reject = ${reject}`, -// "events = find_events(raw)", -// `raw_epochs = Epochs(raw, events=events, event_id=event_id, -// tmin=tmin, tmax=tmax, baseline=baseline, reject=reject, preload=True, -// verbose=False, picks=picks)`, -// `conditions = OrderedDict({key: [value] for (key, value) in raw_epochs.event_id.items()})` -// ].join("\n"); - -// export const requestEpochsInfo = (variableName: string) => -// `get_epochs_info(${variableName})`; - -// export const requestChannelInfo = () => -// `[ch for ch in clean_epochs.ch_names if ch != 'Marker']`; - -// export const cleanEpochsPlot = () => -// [ -// `%matplotlib`, -// `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)` -// ].join("\n"); - -// export const plotTopoMap = () => -// [`%matplotlib inline`, `plot_topo(clean_epochs, conditions)`].join("\n"); - -// export const plotERP = (channelIndex: number) => -// [ -// `%matplotlib inline`, -// `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, -// ci=97.5, n_boot=1000, title='', diff_waveform=None)` -// ].join("\n"); - -// export const saveEpochs = (workspaceDir: string, subject: string) => -// `raw_epochs.save(${formatFilePath( -// path.join( -// workspaceDir, -// "Data", -// subject, -// "EEG", -// `${subject}-cleaned-epo.fif` -// ) -// )})`;