An easy-to-use platform for EEG experimentation in the classroom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Updated utils to typescript

authored by

jdpigeon and committed by
Teon L Brooks
8b461a8d aef07315

+33 -277
-114
app/utils/pyodide/cell.js
··· 1 - /* eslint-disable */ 2 - // File we be replaced by pyodide update 3 - import * as path from 'path'; 4 - import { readFileSync } from 'fs'; 5 - import { RESOURCE_PATH } from '../../constants/constants'; 6 - 7 - export const imports = () => 8 - [ 9 - 'from mne import Epochs, find_events, set_eeg_reference, read_epochs, concatenate_epochs', 10 - 'from time import time, strftime, gmtime', 11 - 'import os', 12 - 'from collections import OrderedDict', 13 - 'from glob import glob', 14 - 'from mne import create_info, concatenate_raws', 15 - 'from mne.io import RawArray', 16 - 'from mne.io import RawArray', 17 - 'from mne.channels import read_montage', 18 - 'import pandas as pd', 19 - 'import numpy as np', 20 - 'import seaborn as sns', 21 - 'from matplotlib import pyplot as plt', 22 - "plt.style.use('fivethirtyeight')", 23 - ].join('\n'); 24 - 25 - export const utils = () => 26 - readFileSync(path.join(RESOURCE_PATH, '/utils/jupyter/utils.py'), 'utf8'); 27 - 28 - export const loadCSV = (filePathArray: Array<string>) => 29 - [ 30 - `files = [${filePathArray.map((filePath) => formatFilePath(filePath))}]`, 31 - `replace_ch_names = None`, 32 - `raw = load_data(files, replace_ch_names)`, 33 - ].join('\n'); 34 - 35 - export const loadCleanedEpochs = (filePathArray: Array<string>) => 36 - [ 37 - `files = [${filePathArray.map((filePath) => formatFilePath(filePath))}]`, 38 - `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, 39 - `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})`, 40 - ].join('\n'); 41 - 42 - // NOTE: this command includes a ';' to prevent returning data 43 - export const filterIIR = (lowCutoff: number, highCutoff: number) => 44 - `raw.filter(${lowCutoff}, ${highCutoff}, method='iir');`; 45 - 46 - export const plotPSD = () => 47 - [`%matplotlib inline`, `raw.plot_psd(fmin=1, fmax=30)`].join('\n'); 48 - 49 - export const epochEvents = ( 50 - eventIDs: { 51 - [key: string]: number; 52 - }, 53 - tmin: number, 54 - tmax: number, 55 - reject: Array<string> | string = 'None' 56 - ) => { 57 - const IDs = Object.keys(eventIDs) 58 - .filter((k) => k !== '') 59 - .reduce((res, key) => ((res[key] = eventIDs[key]), res), {}); 60 - const command = [ 61 - `event_id = ${JSON.stringify(IDs)}`, 62 - `tmin=${tmin}`, 63 - `tmax=${tmax}`, 64 - `baseline= (tmin, tmax)`, 65 - `picks = None`, 66 - `reject = ${reject}`, 67 - 'events = find_events(raw)', 68 - `raw_epochs = Epochs(raw, events=events, event_id=event_id, 69 - tmin=tmin, tmax=tmax, baseline=baseline, reject=reject, preload=True, 70 - verbose=False, picks=picks)`, 71 - `conditions = OrderedDict({key: [value] for (key, value) in raw_epochs.event_id.items()})`, 72 - ].join('\n'); 73 - return command; 74 - }; 75 - 76 - export const requestEpochsInfo = (variableName: string) => 77 - `get_epochs_info(${variableName})`; 78 - 79 - export const requestChannelInfo = () => 80 - `[ch for ch in clean_epochs.ch_names if ch != 'Marker']`; 81 - 82 - export const cleanEpochsPlot = () => 83 - [ 84 - `%matplotlib`, 85 - `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)`, 86 - ].join('\n'); 87 - 88 - export const plotTopoMap = () => 89 - [`%matplotlib inline`, `plot_topo(clean_epochs, conditions)`].join('\n'); 90 - 91 - export const plotERP = (channelIndex: number | string) => 92 - [ 93 - `%matplotlib inline`, 94 - `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions`, 95 - `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, 96 - ci=97.5, n_boot=1000, title='', diff_waveform=None)`, 97 - ].join('\n'); 98 - 99 - export const saveEpochs = (workspaceDir: string, subject: string) => 100 - `raw_epochs.save(${formatFilePath( 101 - path.join( 102 - workspaceDir, 103 - 'Data', 104 - subject, 105 - 'EEG', 106 - `${subject}-cleaned-epo.fif` 107 - ) 108 - )})`; 109 - 110 - // ------------------------------------------- 111 - // Helper methods 112 - 113 - const formatFilePath = (filePath: string) => 114 - `"${filePath.replace(/\\/g, '/')}"`;
-39
app/utils/pyodide/functions.js
··· 1 - // ------------------------------------------- 2 - // Helper & utility functions 3 - 4 - export const parseSingleQuoteJSON = (string: string) => 5 - JSON.parse(string.replace(/'/g, '"')); 6 - 7 - export const debugParseMessage = (msg: Record<string, any>) => { 8 - let content = ''; 9 - switch (msg.channel) { 10 - case 'iopub': 11 - if (msg.content.execution_state) { 12 - content = JSON.stringify(msg.content); 13 - } 14 - 15 - if (msg.content.code) { 16 - content = msg.content.code.slice(0, 300).concat('...'); 17 - } 18 - 19 - if (msg.content.text) { 20 - content = msg.content.text; 21 - } 22 - 23 - if (msg.content.data) { 24 - content = JSON.stringify(Object.keys(msg.content.data)); 25 - } 26 - 27 - break; 28 - case 'shell': 29 - content = JSON.stringify(msg.content); 30 - break; 31 - 32 - default: 33 - content = JSON.stringify(msg); 34 - } 35 - return `${msg.channel} ${content}`; 36 - }; 37 - 38 - export const formatFilePath = (filePath: string) => 39 - `"${filePath.replace(/\\/g, '/')}"`;
+8
app/utils/pyodide/functions.ts
··· 1 + // ------------------------------------------- 2 + // Helper & utility functions 3 + 4 + export const parseSingleQuoteJSON = (string: string) => 5 + JSON.parse(string.replace(/'/g, '"')); 6 + 7 + export const formatFilePath = (filePath: string) => 8 + `"${filePath.replace(/\\/g, '/')}"`;
+25 -14
app/utils/pyodide/index.js app/utils/pyodide/index.ts
··· 2 2 import { readFileSync } from 'fs'; 3 3 import { formatFilePath } from './functions'; 4 4 5 + declare const pyodideWorker: Worker; 6 + 5 7 // --------------------------------- 6 8 // This file contains the JS functions that allow the app to access python-wasm through pyodide 7 9 // These functions wrap the python strings defined in the ··· 19 21 }); 20 22 21 23 export const loadCSV = async (csvArray: Array<any>) => { 24 + // TODO: Pass attached variable name as parameter to load_data 25 + // @ts-expect-error 22 26 window.csvArray = csvArray; 23 - // TODO: Pass attached variable name as parameter to load_data 24 27 await pyodideWorker.postMessage({ data: `raw = load_data()` }); 25 28 }; 26 29 27 30 // --------------------------- 28 31 // MNE-Related Data Processing 29 32 30 - // export const loadCleanedEpochs = (epocsArray: Array<any>) => 31 - // [ 32 - // `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, 33 - // `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})` 34 - // ].join("\n"); 33 + export const loadCleanedEpochs = async (epochsArray: string[]) => { 34 + await pyodideWorker.postMessage({ 35 + data: [ 36 + `clean_epochs = concatenate_epochs([read_epochs(file) for file in ${epochsArray}])`, 37 + `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})`, 38 + ].join('\n'), 39 + }); 40 + }; 35 41 36 42 // NOTE: this command includes a ';' to prevent returning data 37 43 export const filterIIR = async (lowCutoff: number, highCutoff: number) => ··· 40 46 }); 41 47 42 48 export const epochEvents = async ( 43 - eventIDs: { [string]: number }, 49 + eventIDs: { [k: string]: number }, 44 50 tmin: number, 45 51 tmax: number, 46 - reject?: Array<string> | string = 'None' 52 + reject?: string[] | 'None' 47 53 ) => 48 54 pyodideWorker.postMessage({ 49 55 data: [ ··· 78 84 79 85 export const cleanEpochsPlot = async () => { 80 86 // TODO: Figure out how to get image results from pyodide 81 - pyodideWorker.postMessage({ 87 + await pyodideWorker.postMessage({ 82 88 data: `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)`, 83 89 }); 84 90 }; 85 91 86 92 export const plotPSD = async () => { 87 93 // TODO: Figure out how to get image results from pyodide 88 - pyodideWorker.postMessage({ data: `raw.plot_psd(fmin=1, fmax=30)` }); 94 + return pyodideWorker.postMessage({ data: `raw.plot_psd(fmin=1, fmax=30)` }); 89 95 }; 90 96 91 97 export const plotTopoMap = async () => { 92 98 // TODO: Figure out how to get image results from pyodide 93 - pyodideWorker.postMessage({ data: `plot_topo(clean_epochs, conditions)` }); 99 + return pyodideWorker.postMessage({ 100 + data: `plot_topo(clean_epochs, conditions)`, 101 + }); 94 102 }; 95 103 96 - export const plotERP = (channelIndex: number) => 97 - `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, 98 - ci=97.5, n_boot=1000, title='', diff_waveform=None)`; 104 + export const plotERP = async (channelIndex: number) => { 105 + return pyodideWorker.postMessage({ 106 + data: `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, 107 + ci=97.5, n_boot=1000, title='', diff_waveform=None)`, 108 + }); 109 + }; 99 110 100 111 export const saveEpochs = (workspaceDir: string, subject: string) => 101 112 pyodideWorker.postMessage({
-26
app/utils/pyodide/pipes.js
··· 1 - import { pipe } from 'rxjs'; 2 - import { map, pluck, filter, take, mergeMap } from 'rxjs/operators'; 3 - import { executeRequest } from '@nteract/messaging'; 4 - import { PyodideActions } from '../../actions'; 5 - import { RECEIVE_EXECUTE_REPLY } from '../../epics/pyodideEpics'; 6 - 7 - // Refactor this so command can be calculated either up stream or inside pipe 8 - export const execute = (command, state$) => 9 - pipe( 10 - map(() => state$.value.pyodide.mainChannel.next(executeRequest(command))) 11 - ); 12 - 13 - export const awaitOkMessage = (action$) => 14 - pipe( 15 - mergeMap(() => 16 - action$ 17 - .ofType(PyodideActions.ReceiveExecuteReply.type) 18 - .pipe( 19 - pluck('payload'), 20 - filter < 21 - any > 22 - ((msg) => msg.channel === 'shell' && msg.content.status === 'ok'), 23 - take(1) 24 - ) 25 - ) 26 - );
-84
app/utils/pyodide/statements.json
··· 1 - // DEPRECATED 2 - 3 - // import * as path from "path"; 4 - // import { readFileSync } from "fs"; 5 - 6 - // // The output of the functions contained in this file are python commands encoded as strings 7 - // // that would be run in a notebook environment in order to perform the experimental analyses underlying BrainWaves 8 - 9 - // export const utils = () => 10 - // readFileSync(path.join(__dirname, "/utils/pyodide/utils.py"), "utf8"); 11 - 12 - // // export const loadCSV = (filePathArray: Array<string>) => 13 - // // [ 14 - // // `files = [${filePathArray.map(filePath => formatFilePath(filePath))}]`, 15 - // // `replace_ch_names = None`, 16 - // // `raw = load_data(files, replace_ch_names)` 17 - // // ].join("\n"); 18 - 19 - // // export const loadCleanedEpochs = (filePathArray: Array<string>) => 20 - // // [ 21 - // // `files = [${filePathArray.map(filePath => formatFilePath(filePath))}]`, 22 - // // `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, 23 - // // `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})` 24 - // // ].join("\n"); 25 - 26 - // // NOTE: this command includes a ';' to prevent returning data 27 - // export const filterIIR = (lowCutoff: number, highCutoff: number) => 28 - // `raw.filter(${lowCutoff}, ${highCutoff}, method='iir');`; 29 - 30 - // export const plotPSD = () => 31 - // [`%matplotlib inline`, `raw.plot_psd(fmin=1, fmax=30)`].join("\n"); 32 - 33 - // export const epochEvents = ( 34 - // eventIDs: { [string]: number }, 35 - // tmin: number, 36 - // tmax: number, 37 - // reject?: Array<string> | string = "None" 38 - // ) => 39 - // [ 40 - // `event_id = ${JSON.stringify(eventIDs)}`, 41 - // `tmin=${tmin}`, 42 - // `tmax=${tmax}`, 43 - // `baseline= (tmin, tmax)`, 44 - // `picks = None`, 45 - // `reject = ${reject}`, 46 - // "events = find_events(raw)", 47 - // `raw_epochs = Epochs(raw, events=events, event_id=event_id, 48 - // tmin=tmin, tmax=tmax, baseline=baseline, reject=reject, preload=True, 49 - // verbose=False, picks=picks)`, 50 - // `conditions = OrderedDict({key: [value] for (key, value) in raw_epochs.event_id.items()})` 51 - // ].join("\n"); 52 - 53 - // export const requestEpochsInfo = (variableName: string) => 54 - // `get_epochs_info(${variableName})`; 55 - 56 - // export const requestChannelInfo = () => 57 - // `[ch for ch in clean_epochs.ch_names if ch != 'Marker']`; 58 - 59 - // export const cleanEpochsPlot = () => 60 - // [ 61 - // `%matplotlib`, 62 - // `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)` 63 - // ].join("\n"); 64 - 65 - // export const plotTopoMap = () => 66 - // [`%matplotlib inline`, `plot_topo(clean_epochs, conditions)`].join("\n"); 67 - 68 - // export const plotERP = (channelIndex: number) => 69 - // [ 70 - // `%matplotlib inline`, 71 - // `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, 72 - // ci=97.5, n_boot=1000, title='', diff_waveform=None)` 73 - // ].join("\n"); 74 - 75 - // export const saveEpochs = (workspaceDir: string, subject: string) => 76 - // `raw_epochs.save(${formatFilePath( 77 - // path.join( 78 - // workspaceDir, 79 - // "Data", 80 - // subject, 81 - // "EEG", 82 - // `${subject}-cleaned-epo.fif` 83 - // ) 84 - // )})`;