From 73aff8cd95e2ee550b10e1b5035ac13bc08fe834 Mon Sep 17 00:00:00 2001 From: moblim Date: Thu, 14 Jul 2022 11:35:24 -0400 Subject: [PATCH 1/8] Event Code Configuration Turns out the triggers are tied specifically to public>electron.js. the code chunk called //TRIGGER PORT HELPERS are where electron looks for EEG ports to enable triggers. It creates a pop-up if EEG is not connected. Commenting out this code will remove this pop-up and stop triggers from happening: // TRIGGER PORT HELPERS // let triggerPort; // let portAvailable; // let SKIP_SENDING_DEV = false; // const setUpPort = async () => { // p = await getPort(activeComName); // if (p) { // triggerPort = p; // portAvailable = true; // triggerPort.on("error", (err) => { // log.error(err); // let buttons = ["OK", "Continue Anyway"]; // // if (process.env.ELECTRON_START_URL) { // // buttons.push("Continue Anyway") // // } // dialog // .showMessageBox(mainWindow, { // type: "error", // message: "Error communicating with event marker.", // title: "Task Error", // buttons: buttons, // defaultId: 0, // }) // .then((opt) => { // if (opt.response == 0) { // app.exit(); // } else { // SKIP_SENDING_DEV = true; // portAvailable = false; // triggerPort = false; // } // }); // }); // } else { // triggerPort = false; // portAvailable = false; // } // }; In the interest of reducing any major changes to the code, I am leaving both files named trigger.js in the code. They are useless now but I would rather keep them for reversibility. DELETING ALL MENTION OF EEG WITHIN INSTRUCTIONS Commented out RecordNow qihn src/timelines/main.js to circumvent any EEG prompts This should encompass all of the EEG related changes. SESSION NUMBER INCLUSION Added text in src/language/en_us.json between "set" and ""get_prolific": "sessionNum": "Please enter the Session #.
(The file will automatically be named Subject ID_Session_Effort.)", Added chunk of code after line 59 in src/trials/userId.js Added code in src/lib/utils.js added a const within const getUserId const sessionNum = JSON.parse(data.responses)["Q1"]; jsPsych.data.addProperties({ sessionNum: sessionNum, timestamp: Date.now() }); console.log("session", sessionNum); Added code in public/electron.js Added let sessionNum = "" Added sessionNum = args.sessionNum; changed line 205 to: fileName = `${patientID}_${sessionNum}_${Effort}.json`; from fileName = `${patientID}_${Effort}.json`; changed line 303 to const copyPath = path.join(desktop, dataDir, `${patientID}_${sessionNum}_${Effort}`); from const copyPath = path.join(desktop, dataDir, `${patientID}_${Effort}`); There should now be a prompt on the participant ID screen that asks for the --- public/electron.js | 176 ++++++++++++++++++++-------------------- src/language/en_us.json | 3 +- src/lib/utils.js | 4 + src/timelines/main.js | 26 +++--- src/trials/userId.js | 8 ++ 5 files changed, 116 insertions(+), 101 deletions(-) diff --git a/public/electron.js b/public/electron.js index 996941a..916a521 100644 --- a/public/electron.js +++ b/public/electron.js @@ -99,97 +99,98 @@ function createWindow() { }); } // end of createWindow -// TRIGGER PORT HELPERS -let triggerPort; -let portAvailable; -let SKIP_SENDING_DEV = false; - -const setUpPort = async () => { - p = await getPort(activeComName); - if (p) { - triggerPort = p; - portAvailable = true; - - triggerPort.on("error", (err) => { - log.error(err); - let buttons = ["OK", "Continue Anyway"]; - // if (process.env.ELECTRON_START_URL) { - // buttons.push("Continue Anyway") - // } - dialog - .showMessageBox(mainWindow, { - type: "error", - message: "Error communicating with event marker.", - title: "Task Error", - buttons: buttons, - defaultId: 0, - }) - .then((opt) => { - if (opt.response == 0) { - app.exit(); - } else { - SKIP_SENDING_DEV = true; - portAvailable = false; - triggerPort = false; - } - }); - }); - } else { - triggerPort = false; - portAvailable = false; - } -}; - -const handleEventSend = async (code) => { - if (!portAvailable && !SKIP_SENDING_DEV) { - let message = "Event Marker not connected"; - log.warn(message); - let buttons = ["Quit", "Retry", "Continue Anyway"]; - // if (process.env.ELECTRON_START_URL) { - // buttons.push("Continue Anyway") - // } - dialog - .showMessageBox(mainWindow, { - type: "error", - message: message, - title: "Task Error", - buttons: buttons, - defaultId: 0, - }) - .then((resp) => { - let opt = resp.response; - if (opt == 0) { - // quit - app.exit(); - } else if (opt == 1) { - // retry - setUpPort().then(() => handleEventSend(code)); - } else if (opt == 2) { - SKIP_SENDING_DEV = true; - } - }); - } else if (!SKIP_SENDING_DEV) { - await sendToPort(triggerPort, code); - } -}; - -// EVENT TRIGGER - -ipc.on("trigger", (event, args) => { - let code = args; - if (code != undefined) { - log.info(`Event: ${_.invert(eventCodes)[code]}, code: ${code}`); - if (!AT_HOME) { - handleEventSend(code); - } - } -}); +// // TRIGGER PORT HELPERS +// let triggerPort; +// let portAvailable; +// let SKIP_SENDING_DEV = false; + +// const setUpPort = async () => { +// p = await getPort(activeComName); +// if (p) { +// triggerPort = p; +// portAvailable = true; + +// triggerPort.on("error", (err) => { +// log.error(err); +// let buttons = ["OK", "Continue Anyway"]; +// // if (process.env.ELECTRON_START_URL) { +// // buttons.push("Continue Anyway") +// // } +// dialog +// .showMessageBox(mainWindow, { +// type: "error", +// message: "Error communicating with event marker.", +// title: "Task Error", +// buttons: buttons, +// defaultId: 0, +// }) +// .then((opt) => { +// if (opt.response == 0) { +// app.exit(); +// } else { +// SKIP_SENDING_DEV = true; +// portAvailable = false; +// triggerPort = false; +// } +// }); +// }); +// } else { +// triggerPort = false; +// portAvailable = false; +// } +// }; + +// const handleEventSend = async (code) => { +// if (!portAvailable && !SKIP_SENDING_DEV) { +// let message = "Event Marker not connected"; +// log.warn(message); +// let buttons = ["Quit", "Retry", "Continue Anyway"]; +// // if (process.env.ELECTRON_START_URL) { +// // buttons.push("Continue Anyway") +// // } +// dialog +// .showMessageBox(mainWindow, { +// type: "error", +// message: message, +// title: "Task Error", +// buttons: buttons, +// defaultId: 0, +// }) +// .then((resp) => { +// let opt = resp.response; +// if (opt == 0) { +// // quit +// app.exit(); +// } else if (opt == 1) { +// // retry +// setUpPort().then(() => handleEventSend(code)); +// } else if (opt == 2) { +// SKIP_SENDING_DEV = true; +// } +// }); +// } else if (!SKIP_SENDING_DEV) { +// await sendToPort(triggerPort, code); +// } +// }; + +// // EVENT TRIGGER + +// ipc.on("trigger", (event, args) => { +// let code = args; +// if (code != undefined) { +// log.info(`Event: ${_.invert(eventCodes)[code]}, code: ${code}`); +// if (!AT_HOME) { +// handleEventSend(code); +// } +// } +// }); // INCREMENTAL FILE SAVING let stream = false; let fileName = ""; let filePath = ""; let patientID = ""; +let sessionNum = ""; let images = []; let startTrial = -1; @@ -199,8 +200,9 @@ ipc.on("data", (event, args) => { if (args.patient_id && fileName === "") { const dir = app.getPath("userData"); patientID = args.patient_id; + sessionNum = args.sessionNum; Effort = "Effort"; - fileName = `${patientID}_${Effort}.json`; + fileName = `${patientID}_${sessionNum}_${Effort}.json`; filePath = path.resolve(dir, fileName); startTrial = args.trial_index; log.warn(filePath); @@ -300,7 +302,7 @@ app.on("will-quit", () => { const today = new Date(Date.now()); // const date = today.toISOString().slice(0,10) // currently don't need date // fileName = `${patientID}_${Effort}.json` - const copyPath = path.join(desktop, dataDir, `${patientID}_${Effort}`); + const copyPath = path.join(desktop, dataDir, `${patientID}_${sessionNum}_${Effort}`); fs.mkdir(copyPath, { recursive: true }, (err) => { log.error(err); fs.copyFileSync(filePath, path.join(copyPath, fileName)); diff --git a/src/language/en_us.json b/src/language/en_us.json index 9ce218e..924647d 100644 --- a/src/language/en_us.json +++ b/src/language/en_us.json @@ -25,7 +25,8 @@ "message": "Instructions" }, "userid": { - "set": "Please enter the Subject ID.
(The file will automatically be named Subject ID_Effort.)", + "set": "Please enter the Subject ID.
(The file will automatically be named Subject ID_Session_Effort.)", + "sessionNum": "Please enter the Session #.
(The file will automatically be named Subject ID_Session_Effort.)", "get_prolific": "Collecting your Prolific ID...", "set_prolific": "Please enter your Prolific ID into the box below:
Warning: You cannot leave this box empty!" }, diff --git a/src/lib/utils.js b/src/lib/utils.js index e159603..8dc73e4 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -111,6 +111,10 @@ const getUserId = (data) => { const patientId = JSON.parse(data.responses)["Q0"]; jsPsych.data.addProperties({ patient_id: patientId, timestamp: Date.now() }); console.log("ID", patientId); + + const sessionNum = JSON.parse(data.responses)["Q1"]; + jsPsych.data.addProperties({ sessionNum: sessionNum, timestamp: Date.now() }); + console.log("session", sessionNum); }; export { diff --git a/src/timelines/main.js b/src/timelines/main.js index 09d85a9..45763ee 100644 --- a/src/timelines/main.js +++ b/src/timelines/main.js @@ -27,19 +27,19 @@ import quizTimeline from "../trials/quizTrials"; const inLabTimeline = [ experimentStart(), userId(), - preamble, - bluePracticeInstructions(), - buildCountdown(lang.countdown.practice1, 3), - taskBlock(practiceBlock1), - greenPracticeInstructions(), - buildCountdown(lang.countdown.practice2, 3), - taskBlock(practiceBlock2), - realPracticeInstructions(), - buildCountdown(lang.countdown.practice3, 3), - taskBlock(practiceBlock3), - quizTimeline(practiceBlock3), - relaxReminder(), - recordNow(), + // preamble, + // bluePracticeInstructions(), + // buildCountdown(lang.countdown.practice1, 3), + // taskBlock(practiceBlock1), + // greenPracticeInstructions(), + // buildCountdown(lang.countdown.practice2, 3), + // taskBlock(practiceBlock2), + // realPracticeInstructions(), + // buildCountdown(lang.countdown.practice3, 3), + // taskBlock(practiceBlock3), + // quizTimeline(practiceBlock3), + // relaxReminder(), + //recordNow(), postPracticeInstructions(), buildCountdown(lang.countdown.expt1, 3), practiceAndMainBlockDivider(500), diff --git a/src/trials/userId.js b/src/trials/userId.js index 6dc71ee..a8bbab1 100644 --- a/src/trials/userId.js +++ b/src/trials/userId.js @@ -57,6 +57,14 @@ const userId = () => { true ), }, + { + prompt: baseStimulus( + `
+

${lang.userid.sessionNum}

+
`, + true + ), + }, ], on_finish: (data) => { getUserId(data); From c392baec035307106ce6ab61491e3dcdfebb3c54 Mon Sep 17 00:00:00 2001 From: moblim Date: Thu, 14 Jul 2022 12:06:52 -0400 Subject: [PATCH 2/8] Uncommented a timeline edit used for debugging. changed version to match 1.3.4.2 release --- package-lock.json | 4 ++-- package.json | 2 +- src/language/en_us.json | 2 +- src/timelines/main.js | 26 +++++++++++++------------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index db37d2f..8c50be3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "effort-v1.3.3", - "version": "1.3.3", + "name": "effort-v1.3.4.2", + "version": "1.3.4.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index deefd1a..c922804 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "effort-v1.3.3", + "name": "effort-v1.3.4.2", "description": "all-in-one app with jsPsych + React + Electron + psiTurk", "author": { "name": "Wasita Mahaphanit (Brown) & Rashi Dhar (Brown CCV)", diff --git a/src/language/en_us.json b/src/language/en_us.json index 924647d..8e9f548 100644 --- a/src/language/en_us.json +++ b/src/language/en_us.json @@ -2,7 +2,7 @@ "task": { "name": "Effort Task", "end": "This experiment has ended.", - "version": "v1.3.4" + "version": "v1.3.4.2" }, "prompt": { "continue": { diff --git a/src/timelines/main.js b/src/timelines/main.js index 45763ee..09d85a9 100644 --- a/src/timelines/main.js +++ b/src/timelines/main.js @@ -27,19 +27,19 @@ import quizTimeline from "../trials/quizTrials"; const inLabTimeline = [ experimentStart(), userId(), - // preamble, - // bluePracticeInstructions(), - // buildCountdown(lang.countdown.practice1, 3), - // taskBlock(practiceBlock1), - // greenPracticeInstructions(), - // buildCountdown(lang.countdown.practice2, 3), - // taskBlock(practiceBlock2), - // realPracticeInstructions(), - // buildCountdown(lang.countdown.practice3, 3), - // taskBlock(practiceBlock3), - // quizTimeline(practiceBlock3), - // relaxReminder(), - //recordNow(), + preamble, + bluePracticeInstructions(), + buildCountdown(lang.countdown.practice1, 3), + taskBlock(practiceBlock1), + greenPracticeInstructions(), + buildCountdown(lang.countdown.practice2, 3), + taskBlock(practiceBlock2), + realPracticeInstructions(), + buildCountdown(lang.countdown.practice3, 3), + taskBlock(practiceBlock3), + quizTimeline(practiceBlock3), + relaxReminder(), + recordNow(), postPracticeInstructions(), buildCountdown(lang.countdown.expt1, 3), practiceAndMainBlockDivider(500), From 66119f9b4cabcba46ef4540d71d662f53bd359a8 Mon Sep 17 00:00:00 2001 From: moblim Date: Wed, 20 Jul 2022 10:13:56 -0400 Subject: [PATCH 3/8] Added checkSess.js to check the session number prompt for either a one or two. Denying anything else. Added checkSess to the timeline removed baseStimulus from userID, reformatted userID prompts to show closely stacked instead of on seperate pages. --- src/trials/checkSess.js | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/trials/checkSess.js diff --git a/src/trials/checkSess.js b/src/trials/checkSess.js new file mode 100644 index 0000000..51ae065 --- /dev/null +++ b/src/trials/checkSess.js @@ -0,0 +1,44 @@ +import userId from "../trials/userId"; +import { jsPsych } from "jspsych-react"; + +const retakeLoop = () => { + return { + timeline: [ userId() ], + loop_function: () => { + const sess1 = jsPsych.data.getLastTrialData().values()[0]; + var sess = parseInt(JSON.parse(sess1.responses)["Q1"]); + + if ( + sess != 1 & sess!=2 + ) { + return true; + } else { + return false; + } + }, + }; + }; + +const checkSess = () => { + return { + timeline: [retakeLoop()], + conditional_function: () => { + const sess1 = jsPsych.data.getLastTrialData().values()[0]; + var sess = parseInt(JSON.parse(sess1.responses)["Q1"]); + if ( + sess != 1 & sess!=2 + ) { + return true; + } else { + return false; + } + }, + }; + }; + let checkSessTimeline = () => { + return { + timeline: [checkSess()], + type: "html_keyboard_response", + }; + } + export default checkSessTimeline; \ No newline at end of file From 7e5dddd6e439378964695e3cf19108c71b2addf8 Mon Sep 17 00:00:00 2001 From: moblim Date: Wed, 20 Jul 2022 10:17:24 -0400 Subject: [PATCH 4/8] *PLEASE SEE PREVIOUS COMMITT FOR INFO* --- src/language/en_us.json | 2 +- src/timelines/main.js | 2 ++ src/trials/userId.js | 20 +++++--------------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/language/en_us.json b/src/language/en_us.json index 8e9f548..def50a3 100644 --- a/src/language/en_us.json +++ b/src/language/en_us.json @@ -26,7 +26,7 @@ }, "userid": { "set": "Please enter the Subject ID.
(The file will automatically be named Subject ID_Session_Effort.)", - "sessionNum": "Please enter the Session #.
(The file will automatically be named Subject ID_Session_Effort.)", + "sessionNum": "Please enter the Session # (Enter ONLY 1 or 2).
(The file will automatically be named Subject ID_Session_Effort.)", "get_prolific": "Collecting your Prolific ID...", "set_prolific": "Please enter your Prolific ID into the box below:
Warning: You cannot leave this box empty!" }, diff --git a/src/timelines/main.js b/src/timelines/main.js index 09d85a9..914643a 100644 --- a/src/timelines/main.js +++ b/src/timelines/main.js @@ -23,10 +23,12 @@ import { postPracticeInstructions, } from "../trials/instructions"; import quizTimeline from "../trials/quizTrials"; +import checkSessTimeline from "../trials/checkSess"; const inLabTimeline = [ experimentStart(), userId(), + checkSessTimeline(), preamble, bluePracticeInstructions(), buildCountdown(lang.countdown.practice1, 3), diff --git a/src/trials/userId.js b/src/trials/userId.js index a8bbab1..287ff72 100644 --- a/src/trials/userId.js +++ b/src/trials/userId.js @@ -50,21 +50,11 @@ const userId = () => { type: "survey_text", questions: [ { - prompt: baseStimulus( - `
-

${lang.userid.set}

-
`, - true - ), - }, - { - prompt: baseStimulus( - `
-

${lang.userid.sessionNum}

-
`, - true - ), - }, + prompt:`${lang.userid.set}`, + }, + { + prompt:`${lang.userid.sessionNum}`, + }, ], on_finish: (data) => { getUserId(data); From b18f5bc6bff0c87d0d5b2e21d0389a476f2ea572 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 20 Jul 2022 16:37:51 -0400 Subject: [PATCH 5/8] only update jspsych.data if parsed session number is 1 or 2 --- src/lib/utils.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/lib/utils.js b/src/lib/utils.js index 8dc73e4..59afa48 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -108,13 +108,22 @@ const getProlificId = (data) => { }; const getUserId = (data) => { - const patientId = JSON.parse(data.responses)["Q0"]; - jsPsych.data.addProperties({ patient_id: patientId, timestamp: Date.now() }); - console.log("ID", patientId); + // IAN: Try to check that session number is correct (0 or 1). If it is not, + // we don't add Properties to data, and thus don't create a new listener. + if ( + // IAN: + JSON.parse(data.responses)["Q1"] == 1 || JSON.parse(data.responses)["Q1"] == 2 + ) { + const patientId = JSON.parse(data.responses)["Q0"]; + jsPsych.data.addProperties({ patient_id: patientId, timestamp: Date.now() }); + console.log("ID", patientId); + + const sessionNum = JSON.parse(data.responses)["Q1"]; + jsPsych.data.addProperties({ sessionNum: sessionNum, timestamp: Date.now() }); + console.log("session", sessionNum); + } - const sessionNum = JSON.parse(data.responses)["Q1"]; - jsPsych.data.addProperties({ sessionNum: sessionNum, timestamp: Date.now() }); - console.log("session", sessionNum); + }; export { From b997b233baee4da076d7468b130312eb6ce8e5ae Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 20 Jul 2022 17:08:48 -0400 Subject: [PATCH 6/8] Kill EEG prompts: commented out recordNow ()and relaxReminder() in main --- src/timelines/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timelines/main.js b/src/timelines/main.js index 914643a..18b9fc1 100644 --- a/src/timelines/main.js +++ b/src/timelines/main.js @@ -40,8 +40,8 @@ const inLabTimeline = [ buildCountdown(lang.countdown.practice3, 3), taskBlock(practiceBlock3), quizTimeline(practiceBlock3), - relaxReminder(), - recordNow(), + //relaxReminder(), + //recordNow(), postPracticeInstructions(), buildCountdown(lang.countdown.expt1, 3), practiceAndMainBlockDivider(500), From ab4bd3f71659fb5076cad97a76701722a6de98f7 Mon Sep 17 00:00:00 2001 From: aoblim <60661724+moblim@users.noreply.github.com> Date: Tue, 26 Jul 2022 11:38:03 -0400 Subject: [PATCH 7/8] Remove additional trigger code in electron.js Commented out lines 14-20 and 272-274. These lines pertained to triggers and event codes. Given lines 102-186 were commented out, the pop-up for eeg connection would not appear, and the aforementioned code would fail, causing the package version of the task to be suspended as a background process in windows. Commentig out these lines solved the issue and the app will run as expected. --- public/electron.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/electron.js b/public/electron.js index 916a521..550b545 100644 --- a/public/electron.js +++ b/public/electron.js @@ -12,12 +12,12 @@ const log = require("electron-log"); const AT_HOME = process.env.REACT_APP_AT_HOME === "true"; // Event Trigger -const { eventCodes, comName } = require("./config/trigger"); -const { isPort, getPort, sendToPort } = require("event-marker"); +//const { eventCodes, comName } = require("./config/trigger"); +//const { isPort, getPort, sendToPort } = require("event-marker"); // Override comName if environment variable set -const activeComName = process.env.COMNAME || comName; -log.info("Trigger Box comName", activeComName); +//const activeComName = process.env.COMNAME || comName; +//log.info("Trigger Box comName", activeComName); // Data Saving const { dataDir } = require("./config/saveData"); @@ -269,9 +269,9 @@ process.on("uncaughtException", (error) => { // Some APIs can only be used after this event occurs. app.on("ready", () => { createWindow(); - if (!AT_HOME) { - setUpPort().then(() => handleEventSend(eventCodes.test_connect)); - } + //if (!AT_HOME) { + //setUpPort().then(() => handleEventSend(eventCodes.test_connect)); + //} }); // Quit when all windows are closed. app.on("window-all-closed", function () { From d59aa0e15ed4e7b86712f1dc33f0e3d32aa3034a Mon Sep 17 00:00:00 2001 From: aoblim <60661724+moblim@users.noreply.github.com> Date: Fri, 29 Jul 2022 13:20:34 -0400 Subject: [PATCH 8/8] Update taskTrial.js Commenting out lines 47 and 48 as they are no longer relevant for RT collection, streamlining the task slightly. --- src/timelines/taskTrial.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/timelines/taskTrial.js b/src/timelines/taskTrial.js index a0aaee0..53bae03 100644 --- a/src/timelines/taskTrial.js +++ b/src/timelines/taskTrial.js @@ -44,8 +44,8 @@ const taskTrial = (blockSettings, blockDetails, opts) => { // show condition fixation(300), rewardProbability(1000, blockSettings, opts, trialDetails), - frameSpike(700, blockSettings, opts, trialDetails), - costBenefits(1500, blockSettings, opts, trialDetails), + //frameSpike(700, blockSettings, opts, trialDetails), + //costBenefits(1500, blockSettings, opts, trialDetails), choice(6000, blockSettings, opts, trialDetails), pressBalloon(25000, blockSettings, opts), fixation(500), @@ -61,4 +61,4 @@ const taskTrial = (blockSettings, blockDetails, opts) => { }; }; -export default taskTrial; \ No newline at end of file +export default taskTrial;