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.

Speculatively updated epics for pyodide

authored by

jdpigeon and committed by
Teon L Brooks
0bd343da 584dfad6

+38 -97
+7
app/actions/pyodideActions.js
··· 1 1 // ------------------------------------------------------------------------- 2 2 // Action Types 3 3 4 + export const LAUNCH = "LAUNCH"; 4 5 export const SEND_EXECUTE_REQUEST = "SEND_EXECUTE_REQUEST"; 5 6 export const LOAD_EPOCHS = "LOAD_EPOCHS"; 6 7 export const LOAD_CLEANED_EPOCHS = "LOAD_CLEANED_EPOCHS"; ··· 11 12 12 13 // ------------------------------------------------------------------------- 13 14 // Actions 15 + 16 + 17 + export const launch = () => ({ 18 + type: LAUNCH 19 + }); 20 + 14 21 15 22 export const sendExecuteRequest = (payload: string) => ({ 16 23 payload,
+3 -2
app/components/HomeComponent/index.tsx
··· 29 29 deleteWorkspaceDir, 30 30 } from '../../utils/filesystem/storage'; 31 31 import { 32 - JupyterActions, 32 + PyodideActions, 33 33 DeviceActions, 34 34 ExperimentActions, 35 35 } from '../../actions'; ··· 64 64 deviceType: DEVICES; 65 65 ExperimentActions: typeof ExperimentActions; 66 66 history: History; 67 - JupyterActions: typeof JupyterActions; 67 + PyodideActions: typeof PyodideActions; 68 68 kernelStatus: KERNEL_STATUS; 69 69 signalQualityObservable?: Observable<SignalQualityData>; 70 70 } ··· 98 98 } 99 99 100 100 componentDidMount() { 101 + this.props.pyodideActions.launch(); 101 102 this.setState({ recentWorkspaces: readWorkspaces() }); 102 103 } 103 104
+28 -95
app/epics/pyodideEpics.js
··· 12 12 import { isNil } from 'lodash'; 13 13 import { toast } from 'react-toastify'; 14 14 import { getWorkspaceDir } from '../utils/filesystem/storage'; 15 + import { languagePluginLoader } from '../../utils/pyodide/pyodide'; 15 16 import { 17 + LAUNCH, 16 18 LOAD_EPOCHS, 17 19 LOAD_CLEANED_EPOCHS, 18 20 LOAD_PSD, ··· 44 46 MUSE_CHANNELS, 45 47 PYODIDE_VARIABLE_NAMES 46 48 } from '../constants/constants'; 47 - 48 49 49 50 export const GET_EPOCHS_INFO = 'GET_EPOCHS_INFO'; 50 51 export const GET_CHANNEL_INFO = 'GET_CHANNEL_INFO'; ··· 119 120 // ------------------------------------------------------------------------- 120 121 // Epics 121 122 123 + const launchEpic = (action$, state$) => 124 + action$.ofType(LAUNCH).pipe( 125 + mergeMap(languagePluginLoader), 126 + tap(() => console.log('launched pyodide')) 127 + ); 128 + 122 129 const loadEpochsEpic = (action$, state$) => 123 130 action$.ofType(LOAD_EPOCHS).pipe( 124 131 pluck('payload'), 125 132 filter(filePathsArray => filePathsArray.length >= 1), 126 - map(filePathsArray => 127 - state$.value.pyodide.mainChannel.next( 128 - executeRequest(loadCSV(filePathsArray)) 129 - ) 130 - ), 131 - awaitOkMessage(action$), 132 - execute(filterIIR(1, 30), state$), 133 - awaitOkMessage(action$), 133 + map(filePathsArray => loadCSV(filePathsArray)), 134 + map(() => filterIIR(1, 30)), 134 135 map(() => 135 136 epochEvents( 136 137 { ··· 143 144 0.8 144 145 ) 145 146 ), 146 - map(epochEventsCommand => 147 - state$.value.pyodide.mainChannel.next(executeRequest(epochEventsCommand)) 148 - ), 149 - awaitOkMessage(action$), 147 + map(epochEventsCommand => epochEventsCommand), 150 148 map(() => getEpochsInfo(PYODIDE_VARIABLE_NAMES.RAW_EPOCHS)) 151 149 ); 152 150 ··· 154 152 action$.ofType(LOAD_CLEANED_EPOCHS).pipe( 155 153 pluck('payload'), 156 154 filter(filePathsArray => filePathsArray.length >= 1), 157 - map(filePathsArray => 158 - state$.value.pyodide.mainChannel.next( 159 - executeRequest(loadCleanedEpochs(filePathsArray)) 160 - ) 161 - ), 162 - awaitOkMessage(action$), 155 + map(filePathsArray => loadCleanedEpochs(filePathsArray)), 163 156 mergeMap(() => 164 157 of( 165 158 getEpochsInfo(PYODIDE_VARIABLE_NAMES.CLEAN_EPOCHS), ··· 171 164 172 165 const cleanEpochsEpic = (action$, state$) => 173 166 action$.ofType(CLEAN_EPOCHS).pipe( 174 - execute(cleanEpochsPlot(), state$), 175 - mergeMap(() => 176 - action$.ofType(RECEIVE_STREAM).pipe( 177 - pluck('payload'), 178 - filter( 179 - (msg) => msg.channel === 'iopub' && msg.content.text.includes('Channels marked as bad') 180 - ), 181 - take(1) 182 - ) 183 - ), 167 + map(cleanEpochsPlot), 184 168 map(() => 185 - state$.value.pyodide.mainChannel.next( 186 - executeRequest( 187 - saveEpochs( 188 - getWorkspaceDir(state$.value.experiment.title), 189 - state$.value.experiment.subject 190 - ) 191 - ) 169 + saveEpochs( 170 + getWorkspaceDir(state$.value.experiment.title), 171 + state$.value.experiment.subject 192 172 ) 193 173 ), 194 - awaitOkMessage(action$), 195 174 map(() => getEpochsInfo(PYODIDE_VARIABLE_NAMES.RAW_EPOCHS)) 196 175 ); 197 176 198 177 const getEpochsInfoEpic = (action$, state$) => 199 178 action$.ofType(GET_EPOCHS_INFO).pipe( 200 179 pluck('payload'), 201 - map(variableName => 202 - state$.value.pyodide.mainChannel.next( 203 - executeRequest(requestEpochsInfo(variableName)) 204 - ) 205 - ), 206 - mergeMap(() => 207 - action$.ofType(RECEIVE_EXECUTE_RESULT).pipe( 208 - pluck('payload'), 209 - filter((msg) => msg.channel === 'iopub' && !isNil(msg.content.data)), 210 - pluck('content', 'data', 'text/plain'), 211 - filter((msg) => msg.includes('Drop Percentage')), 212 - take(1) 213 - ) 214 - ), 215 - map((epochInfoString) => 216 - parseSingleQuoteJSON(epochInfoString).map((infoObj) => ({ 180 + map(variableName => requestEpochsInfo(variableName)), 181 + map(epochInfoString => 182 + parseSingleQuoteJSON(epochInfoString).map(infoObj => ({ 217 183 name: Object.keys(infoObj)[0], 218 184 value: infoObj[Object.keys(infoObj)[0]], 219 185 })) ··· 223 189 224 190 const getChannelInfoEpic = (action$, state$) => 225 191 action$.ofType(GET_CHANNEL_INFO).pipe( 226 - execute(requestChannelInfo(), state$), 227 - mergeMap(() => 228 - action$.ofType(RECEIVE_EXECUTE_RESULT).pipe( 229 - pluck('payload'), 230 - filter((msg) => msg.channel === 'iopub' && !isNil(msg.content.data)), 231 - pluck('content', 'data', 'text/plain'), 232 - // Filter to prevent this from reading requestEpochsInfo returns 233 - filter((msg) => !msg.includes('Drop Percentage')), 234 - take(1) 235 - ) 236 - ), 237 - map((channelInfoString) => setChannelInfo(parseSingleQuoteJSON(channelInfoString))) 192 + map(requestChannelInfo), 193 + map(channelInfoString => 194 + setChannelInfo(parseSingleQuoteJSON(channelInfoString)) 195 + ) 238 196 ); 239 197 240 198 const loadPSDEpic = (action$, state$) => 241 199 action$.ofType(LOAD_PSD).pipe( 242 - execute(plotPSD(), state$), 243 - mergeMap(() => 244 - action$.ofType(RECEIVE_DISPLAY_DATA).pipe( 245 - pluck('payload'), 246 - // PSD graphs should have two axes 247 - filter((msg) => msg.content.data['text/plain'].includes('2 Axes')), 248 - pluck('content', 'data'), 249 - take(1) 250 - ) 251 - ), 200 + map(plotPSD), 252 201 map(setPSDPlot) 253 202 ); 254 203 255 204 const loadTopoEpic = (action$, state$) => 256 205 action$.ofType(LOAD_TOPO).pipe( 257 - execute(plotTopoMap(), state$), 258 - mergeMap(() => 259 - action$.ofType(RECEIVE_DISPLAY_DATA).pipe(pluck('payload'), pluck('content', 'data'), take(1)) 260 - ), 261 - mergeMap((topoPlot) => 206 + map(plotTopoMap), 207 + mergeMap(topoPlot => 262 208 of( 263 209 setTopoPlot(topoPlot), 264 210 loadERP( ··· 280 226 console.warn('channel name supplied to loadERPEpic does not belong to either device'); 281 227 return EMOTIV_CHANNELS[0]; 282 228 }), 283 - map(channelIndex => 284 - state$.value.pyodide.mainChannel.next( 285 - executeRequest(plotERP(channelIndex)) 286 - ) 287 - ), 288 - mergeMap(() => 289 - action$.ofType(RECEIVE_DISPLAY_DATA).pipe( 290 - pluck('payload'), 291 - // ERP graphs should have 1 axis according to MNE 292 - filter((msg) => msg.content.data['text/plain'].includes('1 Axes')), 293 - pluck('content', 'data'), 294 - take(1) 295 - ) 296 - ), 229 + map(channelIndex => plotERP(channelIndex)), 297 230 map(setERPPlot) 298 231 ); 299 232 ··· 308 241 getChannelInfoEpic, 309 242 loadPSDEpic, 310 243 loadTopoEpic, 311 - loadERPEpic, 244 + loadERPEpic 312 245 );