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.

updates to the customize experiment screen to continue using jspsych

Co-Authored-By: Yury-Shevchenko <shevchenko_yury@mail.ru>

+163 -69
+82 -66
app/components/DesignComponent/CustomDesignComponent.js
··· 26 26 import researchQuestionImage from '../../assets/common/ResearchQuestion2.png'; 27 27 import methodsImage from '../../assets/common/Methods2.png'; 28 28 import hypothesisImage from '../../assets/common/Hypothesis2.png'; 29 + import { loadTimeline } from '../../utils/jspsych/functions'; 29 30 30 31 const CUSTOM_STEPS = { 31 32 OVERVIEW: 'OVERVIEW', 32 - STIMULI: 'STIMULI', 33 33 PARAMETERS: 'PARAMETERS', 34 34 PREVIEW: 'PREVIEW' 35 35 }; 36 + 37 + // const CUSTOM_STEPS = { 38 + // OVERVIEW: 'OVERVIEW', 39 + // STIMULI: 'STIMULI', 40 + // PARAMETERS: 'PARAMETERS', 41 + // PREVIEW: 'PREVIEW' 42 + // }; 36 43 37 44 const FIELDS = { 38 45 QUESTION: 'Research Question', ··· 86 93 this.handleEEGEnabled = this.handleEEGEnabled.bind(this); 87 94 if (isNil(props.params)) { 88 95 props.experimentActions.loadDefaultTimeline(); 89 - } 96 + }; 97 + this.endPreview = this.endPreview.bind(this); 98 + } 99 + 100 + endPreview() { 101 + this.setState({ isPreviewing: false }); 90 102 } 91 103 92 104 handleStepClick(step: string) { ··· 107 119 this.props.history.push(SCREENS.COLLECT.route); 108 120 } 109 121 110 - handlePreview() { 122 + handlePreview(e) { 123 + e.target.blur(); 111 124 this.setState({ isPreviewing: !this.state.isPreviewing }); 112 125 } 113 126 ··· 163 176 className={styles.contentGrid} 164 177 > 165 178 <Grid.Column stretched verticalAlign="middle"> 166 - <Segment basic> 167 - <Header as="h1">Image Duration</Header> 168 - <p> 169 - Select the trial duration. This determines the amount of time 170 - each image will be displayed during the experiment. 171 - </p> 172 - </Segment> 173 - <Segment basic> 174 - <ParamSlider 175 - label="Image Duration (seconds)" 176 - value={this.state.params.trialDuration} 177 - onChange={value => 178 - this.setState({ 179 - params: { ...this.state.params, trialDuration: value } 180 - }) 181 - } 182 - /> 183 - </Segment> 184 - </Grid.Column> 185 - <Grid.Column stretched verticalAlign="middle"> 186 - <Segment basic> 187 - <Header as="h1">Time Interval</Header> 188 - <p> 189 - Select the inter-trial interval duration. This is the amount 190 - of time between trials measured from the end of one trial to 191 - the start of the next one. 192 - </p> 193 - </Segment> 194 - <Segment basic> 195 - <ParamSlider 196 - label="ITI Duration (seconds)" 197 - value={this.state.params.iti} 198 - onChange={value => 199 - this.setState({ 200 - params: { ...this.state.params, iti: value } 201 - }) 202 - } 203 - /> 204 - </Segment> 205 - </Grid.Column> 206 - <Grid.Column stretched verticalAlign="middle"> 207 - <Segment basic> 208 - <Header as="h1">Progress Bar</Header> 209 - <p> 210 - This will display a small progress bar at the top of the 211 - experiment window 212 - </p> 213 - </Segment> 214 - <Segment basic> 215 - <Checkbox 216 - checked={this.props.params.showProgessBar} 217 - label="Enable progress bar" 218 - onChange={this.handleProgressBar} 219 - /> 220 - </Segment> 221 179 <Segment basic> 222 180 <Header as="h1">EEG Enabled</Header> 223 181 <p>EEG data collection will be enabled for this experiment</p> ··· 237 195 <Grid relaxed padded className={styles.contentGrid}> 238 196 <Grid.Column 239 197 stretched 240 - width={6} 198 + width={12} 241 199 textAlign="right" 242 - verticalAlign="top" 200 + verticalAlign="middle" 243 201 className={styles.jsPsychColumn} 244 202 > 245 203 <PreviewExperimentComponent 246 - params={this.state.params} 247 - mainTimeline={this.props.mainTimeline} 248 - trials={this.props.trials} 249 - timelines={this.props.timelines} 204 + {...loadTimeline(this.props.paradigm)} 250 205 isPreviewing={this.state.isPreviewing} 206 + onEnd={this.endPreview} 207 + type={this.props.type} 208 + paradigm={this.props.paradigm} 251 209 /> 252 210 </Grid.Column> 253 - <Grid.Column width={6} verticalAlign="middle"> 211 + <Grid.Column width={4} verticalAlign="middle"> 254 212 <Segment basic> 255 213 <Form> 256 214 <Form.TextArea ··· 269 227 </Segment> 270 228 <PreviewButton 271 229 isPreviewing={this.state.isPreviewing} 272 - onClick={this.handlePreview} 230 + onClick={(e) => this.handlePreview(e)} 273 231 /> 274 232 </Grid.Column> 275 233 </Grid> ··· 377 335 ); 378 336 } 379 337 } 338 + 339 + // <Grid.Column stretched verticalAlign="middle"> 340 + // <Segment basic> 341 + // <Header as="h1">Image Duration</Header> 342 + // <p> 343 + // Select the trial duration. This determines the amount of time 344 + // each image will be displayed during the experiment. 345 + // </p> 346 + // </Segment> 347 + // <Segment basic> 348 + // <ParamSlider 349 + // label="Image Duration (seconds)" 350 + // value={this.state.params.trialDuration} 351 + // onChange={value => 352 + // this.setState({ 353 + // params: { ...this.state.params, trialDuration: value } 354 + // }) 355 + // } 356 + // /> 357 + // </Segment> 358 + // </Grid.Column> 359 + // <Grid.Column stretched verticalAlign="middle"> 360 + // <Segment basic> 361 + // <Header as="h1">Time Interval</Header> 362 + // <p> 363 + // Select the inter-trial interval duration. This is the amount 364 + // of time between trials measured from the end of one trial to 365 + // the start of the next one. 366 + // </p> 367 + // </Segment> 368 + // <Segment basic> 369 + // <ParamSlider 370 + // label="ITI Duration (seconds)" 371 + // value={this.state.params.iti} 372 + // onChange={value => 373 + // this.setState({ 374 + // params: { ...this.state.params, iti: value } 375 + // }) 376 + // } 377 + // /> 378 + // </Segment> 379 + // </Grid.Column> 380 + 381 + 382 + // <Segment basic> 383 + // <Header as="h1">Progress Bar</Header> 384 + // <p> 385 + // This will display a small progress bar at the top of the 386 + // experiment window 387 + // </p> 388 + // </Segment> 389 + // <Segment basic> 390 + // <Checkbox 391 + // checked={this.props.params.showProgessBar} 392 + // label="Enable progress bar" 393 + // onChange={this.handleProgressBar} 394 + // /> 395 + // </Segment>
+8 -2
app/utils/jspsych/functions.js
··· 10 10 import { buildStroopTimeline } from './timelines/stroop'; 11 11 import { buildMultiTimeline } from './timelines/multi'; 12 12 import { buildSearchTimeline } from './timelines/search'; 13 + import { buildCustomLine } from './timelines/custom'; 13 14 14 15 import { 15 16 MainTimeline, ··· 34 35 const outlet = lsl.create_outlet(info, 0, 360); 35 36 36 37 // loads a normalized timeline for the default experiments with specific callback fns 37 - export const loadTimeline = (type: EXPERIMENTS) => { 38 + export const loadTimeline = (paradigm: EXPERIMENTS) => { 39 + console.log('paradigm', paradigm) 38 40 let timeline; 39 - switch (type) { 41 + switch (paradigm) { 40 42 case EXPERIMENTS.P300: 41 43 timeline = buildOddballTimeline(); 42 44 break; ··· 59 61 60 62 case EXPERIMENTS.SEARCH: 61 63 timeline = buildSearchTimeline(); 64 + break; 65 + 66 + case EXPERIMENTS.CUSTOM: 67 + timeline = buildN170Timeline(); 62 68 break; 63 69 64 70 default:
+72
app/utils/jspsych/timelines/custom.js
··· 1 + import * as path from 'path'; 2 + import { EVENTS } from '../../../constants/constants'; 3 + 4 + // Default directories containing stimuli 5 + const rootFolder = __dirname; // Note: there's a weird issue where the fs readdir function reads from BrainWaves dir 6 + 7 + const facesDir = path.join(rootFolder, 'assets', 'face_house', 'faces'); 8 + const housesDir = path.join(rootFolder, 'assets', 'face_house', 'houses'); 9 + const fixation = path.join(rootFolder, 'assets', 'common', 'fixationcross.png'); 10 + 11 + export const buildCustomLine = () => ({ 12 + overview: 13 + 'Here is the placeholder for the overview of the Visual Search task', 14 + background: 'Here is the background of the Visual Search task', 15 + background_title: `Title`, 16 + protocol: 'Here is the protocol of the Visual Search task', 17 + params: { 18 + trialDuration: 1000, 19 + nbTrials: 150, 20 + iti: 500, 21 + jitter: 200, 22 + sampleType: 'with-replacement', 23 + pluginName: 'callback-image-display', 24 + intro: 25 + 'You will see the visual search task. Press 1 when a face appears and 9 for a house. Press any key to continue', 26 + showProgressBar: false, 27 + stimulus1: { 28 + dir: facesDir, 29 + title: 'Face', 30 + type: EVENTS.STIMULUS_1, 31 + response: '1' 32 + }, 33 + stimulus2: { 34 + dir: housesDir, 35 + title: 'House', 36 + type: EVENTS.STIMULUS_2, 37 + response: '9' 38 + } 39 + }, 40 + mainTimeline: ['intro', 'faceHouseTimeline', 'end'], // array of trial and timeline ids 41 + trials: { 42 + intro: { 43 + type: 'callback-html-display', 44 + id: 'intro', 45 + post_trial_gap: 1000 46 + }, 47 + end: { 48 + id: 'end', 49 + type: 'callback-html-display', 50 + stimulus: 'Thanks for participating. Press any key to continue', 51 + response_ends_trial: true, 52 + post_trial_gap: 500 53 + } 54 + }, 55 + timelines: { 56 + faceHouseTimeline: { 57 + id: 'faceHouseTimeline', 58 + timeline: [ 59 + { 60 + id: 'interTrial', 61 + type: 'callback-image-display', 62 + stimulus: fixation, 63 + response_ends_trial: false 64 + }, 65 + { 66 + id: 'trial', 67 + response_ends_trial: false 68 + } 69 + ] 70 + } 71 + } 72 + });
+1 -1
app/utils/jspsych/timelines/multi.js
··· 21 21 sampleType: 'with-replacement', 22 22 pluginName: 'callback-image-display', 23 23 intro: 24 - 'You will see the multi-tasking test. Press 1 when a face appears and 9 for a house. Press any key to continue', 24 + 'You will see the multi-tasking test. Press any key to continue', 25 25 showProgressBar: false, 26 26 stimulus1: { 27 27 dir: facesDir,