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.

Merge pull request #62 from makebrainwaves/readme-update

Small Analyze and Clean screen bugfixes

authored by

Dano Morrison and committed by
GitHub
993d0e3d 6afe11c5

+72 -38
+18 -14
app/components/AnalyzeComponent.js
··· 15 15 MUSE_CHANNELS, 16 16 EMOTIV_CHANNELS 17 17 } from '../constants/constants'; 18 - import { readWorkspaceCleanedEEGData, getSubjectNamesFromFiles } from '../utils/filesystem/storage'; 18 + import { 19 + readWorkspaceCleanedEEGData, 20 + getSubjectNamesFromFiles 21 + } from '../utils/filesystem/storage'; 19 22 import SecondaryNavComponent from './SecondaryNavComponent'; 20 23 import ClickableHeadDiagramSVG from './svgs/ClickableHeadDiagramSVG'; 21 24 import JupyterPlotWidget from './JupyterPlotWidget'; ··· 52 55 export default class Analyze extends Component<Props, State> { 53 56 props: Props; 54 57 state: State; 55 - handleAnalyze: () => void; 56 58 handleChannelSelect: string => void; 57 59 handleStepClick: (Object, Object) => void; 58 60 handleDatasetChange: (Object, Object) => void; ··· 70 72 ? EMOTIV_CHANNELS[0] 71 73 : MUSE_CHANNELS[0] 72 74 }; 73 - this.handleAnalyze = this.handleAnalyze.bind(this); 74 75 this.handleChannelSelect = this.handleChannelSelect.bind(this); 75 76 this.handleDatasetChange = this.handleDatasetChange.bind(this); 76 77 this.handleStepClick = this.handleStepClick.bind(this); ··· 93 94 this.setState({ activeStep: step }); 94 95 } 95 96 96 - handleAnalyze() { 97 - this.props.jupyterActions.loadERP(null); 98 - } 99 - 100 97 handleDatasetChange(event: Object, data: Object) { 101 - this.setState({ selectedFilePaths: data.value, 102 - selectedSubjects: getSubjectNamesFromFiles(data.value) }); 98 + this.setState({ 99 + selectedFilePaths: data.value, 100 + selectedSubjects: getSubjectNamesFromFiles(data.value) 101 + }); 103 102 this.props.jupyterActions.loadCleanedEpochs(data.value); 104 103 } 105 104 ··· 109 108 } 110 109 111 110 concatSubjectNames(subjects: Array<?string>) { 112 - if(subjects.length < 1) { return '' } 113 - return subjects.reduce((acc, curr) => `${acc}-${curr}`) 111 + if (subjects.length < 1) { 112 + return ''; 113 + } 114 + return subjects.reduce((acc, curr) => `${acc}-${curr}`); 114 115 } 115 116 116 117 renderEpochLabels() { ··· 170 171 <Grid.Column width={8}> 171 172 <JupyterPlotWidget 172 173 title={this.props.title} 173 - imageTitle={`${this.concatSubjectNames(this.state.selectedSubjects)}-Topoplot`} 174 + imageTitle={`${this.concatSubjectNames( 175 + this.state.selectedSubjects 176 + )}-Topoplot`} 174 177 plotMIMEBundle={this.props.topoPlot} 175 178 /> 176 179 </Grid.Column> ··· 202 205 <Grid.Column width={8}> 203 206 <JupyterPlotWidget 204 207 title={this.props.title} 205 - imageTitle={ 206 - `${this.concatSubjectNames(this.state.selectedSubjects)}-${this.state.selectedChannel}-ERP`} 208 + imageTitle={`${this.concatSubjectNames( 209 + this.state.selectedSubjects 210 + )}-${this.state.selectedChannel}-ERP`} 207 211 plotMIMEBundle={this.props.erpPlot} 208 212 /> 209 213 </Grid.Column>
+6
app/constants/constants.js
··· 46 46 AVAILABLE: 'AVAILABLE' 47 47 }; 48 48 49 + // Names of variables in the jupyter kernel 50 + export const JUPYTER_VARIABLE_NAMES = { 51 + RAW_EPOCHS: 'raw_epochs', 52 + CLEAN_EPOCHS: 'clean_epochs' 53 + }; 54 + 49 55 export const SEARCH_TIMER = 3000; 50 56 51 57 // NOTE: the actual marker id values of stimulus 1 and 2 are reversed
+37 -14
app/epics/jupyterEpics.js
··· 49 49 EMOTIV_CHANNELS, 50 50 EVENTS, 51 51 DEVICES, 52 - MUSE_CHANNELS 52 + MUSE_CHANNELS, 53 + JUPYTER_VARIABLE_NAMES 53 54 } from '../constants/constants'; 54 55 import { 55 56 parseSingleQuoteJSON, ··· 76 77 // ------------------------------------------------------------------------- 77 78 // Action Creators 78 79 79 - const getEpochsInfo = () => ({ type: GET_EPOCHS_INFO }); 80 + const getEpochsInfo = payload => ({ payload, type: GET_EPOCHS_INFO }); 80 81 81 82 const getChannelInfo = () => ({ type: GET_CHANNEL_INFO }); 82 83 ··· 255 256 state$.value.jupyter.mainChannel.next(executeRequest(epochEventsCommand)) 256 257 ), 257 258 awaitOkMessage(action$), 258 - map(getEpochsInfo) 259 + map(() => getEpochsInfo(JUPYTER_VARIABLE_NAMES.RAW_EPOCHS)) 259 260 ); 260 261 261 262 const loadCleanedEpochsEpic = (action$, state$) => ··· 268 269 ) 269 270 ), 270 271 awaitOkMessage(action$), 271 - mergeMap(() => of(getEpochsInfo(), getChannelInfo(), loadTopo())) 272 + mergeMap(() => 273 + of( 274 + getEpochsInfo(JUPYTER_VARIABLE_NAMES.CLEAN_EPOCHS), 275 + getChannelInfo(), 276 + loadTopo() 277 + ) 278 + ) 272 279 ); 273 280 274 281 const cleanEpochsEpic = (action$, state$) => ··· 296 303 ) 297 304 ), 298 305 awaitOkMessage(action$), 299 - map(getEpochsInfo) 306 + map(() => getEpochsInfo(JUPYTER_VARIABLE_NAMES.RAW_EPOCHS)) 300 307 ); 301 308 302 309 const getEpochsInfoEpic = (action$, state$) => 303 310 action$.ofType(GET_EPOCHS_INFO).pipe( 304 - execute(requestEpochsInfo(), state$), 311 + pluck('payload'), 312 + map(variableName => 313 + state$.value.jupyter.mainChannel.next( 314 + executeRequest(requestEpochsInfo(variableName)) 315 + ) 316 + ), 305 317 mergeMap(() => 306 318 action$.ofType(RECEIVE_EXECUTE_RESULT).pipe( 307 319 pluck('payload'), ··· 363 375 take(1) 364 376 ) 365 377 ), 366 - mergeMap(topoPlot => of(setTopoPlot(topoPlot), loadERP())) 378 + mergeMap(topoPlot => 379 + of( 380 + setTopoPlot(topoPlot), 381 + loadERP( 382 + state$.value.device.deviceType === DEVICES.EMOTIV 383 + ? EMOTIV_CHANNELS[0] 384 + : MUSE_CHANNELS[0] 385 + ) 386 + ) 387 + ) 367 388 ); 368 389 369 390 const loadERPEpic = (action$, state$) => 370 391 action$.ofType(LOAD_ERP).pipe( 371 392 pluck('payload'), 372 393 map(channelName => { 373 - const channels = 374 - state$.value.device.deviceType === DEVICES.EMOTIV 375 - ? EMOTIV_CHANNELS 376 - : MUSE_CHANNELS; 377 - if (channels.includes(channelName)) { 378 - return channels.indexOf(channelName); 394 + console.log(channelName); 395 + if (MUSE_CHANNELS.includes(channelName)) { 396 + return MUSE_CHANNELS.indexOf(channelName); 397 + } else if (EMOTIV_CHANNELS.includes(channelName)) { 398 + return EMOTIV_CHANNELS.indexOf(channelName); 379 399 } 380 - return 0; 400 + console.warn( 401 + 'channel name supplied to loadERPEpic does not belong to either device' 402 + ); 403 + return EMOTIV_CHANNELS[0]; 381 404 }), 382 405 map(channelIndex => 383 406 state$.value.jupyter.mainChannel.next(
+11 -10
app/utils/jupyter/cells.js
··· 32 32 export const loadCleanedEpochs = (filePathArray: Array<string>) => 33 33 [ 34 34 `files = [${filePathArray.map(filePath => formatFilePath(filePath))}]`, 35 - `epochs = concatenate_epochs([read_epochs(file) for file in files])`, 36 - `conditions = OrderedDict({key: [value] for (key, value) in epochs.event_id.items()})` 35 + `clean_epochs = concatenate_epochs([read_epochs(file) for file in files])`, 36 + `conditions = OrderedDict({key: [value] for (key, value) in clean_epochs.event_id.items()})` 37 37 ].join('\n'); 38 38 39 39 // NOTE: this command includes a ';' to prevent returning data ··· 57 57 `picks = None`, 58 58 `reject = ${reject}`, 59 59 'events = find_events(raw)', 60 - `epochs = Epochs(raw, events=events, event_id=event_id, 60 + `raw_epochs = Epochs(raw, events=events, event_id=event_id, 61 61 tmin=tmin, tmax=tmax, baseline=baseline, reject=reject, preload=True, 62 62 verbose=False, picks=picks)`, 63 - `conditions = OrderedDict({key: [value] for (key, value) in epochs.event_id.items()})` 63 + `conditions = OrderedDict({key: [value] for (key, value) in raw_epochs.event_id.items()})` 64 64 ].join('\n'); 65 65 return command; 66 66 }; 67 67 68 - export const requestEpochsInfo = () => `get_epochs_info(epochs)`; 68 + export const requestEpochsInfo = (variableName: string) => 69 + `get_epochs_info(${variableName})`; 69 70 70 71 export const requestChannelInfo = () => 71 - `[ch for ch in epochs.ch_names if ch != 'Marker']`; 72 + `[ch for ch in clean_epochs.ch_names if ch != 'Marker']`; 72 73 73 74 export const cleanEpochsPlot = () => 74 75 [ 75 76 `%matplotlib`, 76 - `epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)` 77 + `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)` 77 78 ].join('\n'); 78 79 79 80 export const plotTopoMap = () => 80 - [`%matplotlib inline`, `plot_topo(epochs, conditions)`].join('\n'); 81 + [`%matplotlib inline`, `plot_topo(clean_epochs, conditions)`].join('\n'); 81 82 82 83 export const plotERP = (channelIndex: number) => 83 84 [ 84 85 `%matplotlib inline`, 85 - `X, y = plot_conditions(epochs, ch_ind=${channelIndex}, conditions=conditions, 86 + `X, y = plot_conditions(clean_epochs, ch_ind=${channelIndex}, conditions=conditions, 86 87 ci=97.5, n_boot=1000, title='', diff_waveform=None)` 87 88 ].join('\n'); 88 89 89 90 export const saveEpochs = (workspaceDir: string, subject: string) => 90 - `epochs.save(${formatFilePath( 91 + `raw_epochs.save(${formatFilePath( 91 92 path.join( 92 93 workspaceDir, 93 94 'Data',