Skip to content

Commit

Permalink
Add code from lookit-api
Browse files Browse the repository at this point in the history
  • Loading branch information
okaycj committed Dec 18, 2023
1 parent 94a327b commit 578370d
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ npm ci
Build all packages:

```
npm run build --workspaces
npm run build
```

## Deploy NPM
40 changes: 37 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/lookit-initjspsych/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@jspsych/config": "^2.0.0"
"@jspsych/config": "^2.0.0",
"jspsych": "^7.3.4"
}
}
148 changes: 147 additions & 1 deletion packages/lookit-initjspsych/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,147 @@
export default {};
import { initJsPsych as origInitJsPsych } from "jspsych";

const controller = new AbortController();

function csrfToken() {
/**
* Function to get csrf token from cookies.
*/
return document.cookie
.split("; ")
.find((row) => row.startsWith("csrftoken="))
?.split("=")[1];
}

async function get(url) {
/**
* Function for REST get.
*/
const request = new Request(url, {
method: "GET",
mode: "same-origin",
});

const response = await fetch(request);
if (response.ok) {
return response.json();
}
}

async function patch(url, use_signal, data) {
/**
* Function for REST patch.
*/
const request = new Request(url, {
method: "PATCH",
headers: {
"X-CSRFToken": csrfToken(),
"Content-Type": "application/vnd.api+json",
},
mode: "same-origin", // Do not send CSRF token to another domain.
signal: use_signal ? controller.signal : undefined,
body: JSON.stringify({ data }),
});

const response = await fetch(request);
if (response.ok) {
return response.json();
}
}

function on_data_update(responseApiUrl, id, userFunc) {
/**
* Function that returns a function to be used in place of jsPsych's option
* "on_data_update". "userFunc" should be the user's implementation of
* "on_data_update". Since this is the data that is returned from each
* trial, this function will get the collected trial data and append the
* current data point.
*/
return async function (data) {
const {
data: {
attributes: { exp_data },
},
} = await get(responseApiUrl);

await patch(responseApiUrl, true, {
id,
type: "responses",
attributes: {
exp_data: [...exp_data, data],
},
});

// Don't call the function if not defined by user.
if (typeof userFunc === "function") {
userFunc(data);
}
};
}

function on_finish(responseApiUrl, id, exitUrl, userFunc) {
/**
* Function that returns a function to be used in place of jsPsych's option
* "on_finish". "userFunc" should be the user's implementation of
* "on_finish". Since this is point where the experiment has ended, the
* function will set "completed" to true and overwrites all experiment data
* with the full set of collected data. Once the user function has been
* ran, this will redirect to the study's exit url.
*/
return async function (data) {
/**
* The on_data_update and on_finish functions aren't called as async
* functions. This means that each function isn't completed before the
* next is ran. To handle this, we're going to abort the patch function
* in on_data_update. This will cause a reliable error,
*/
controller.abort("Writing final response data.");

await patch(responseApiUrl, false, {
id,
type: "responses",
attributes: {
exp_data: data.trials,
completed: true,
},
});

// Don't call the function if not defined by user.
if (typeof userFunc === "function") {
userFunc(data);
}

window.location.replace(exitUrl);
};
}

function lookitInitJsPsych(responseApiUrl, responseUuid, exitUrl) {
/**
* Function that returns a function to replace jsPsych's initJsPsych.
*/
return function (opts) {
const jsPsych = origInitJsPsych({
...opts,
on_data_update: on_data_update(
responseApiUrl,
responseUuid,
opts?.on_data_update,
),
on_finish: on_finish(
responseApiUrl,
responseUuid,
exitUrl,
opts?.on_finish,
),
});
const origJsPsychRun = jsPsych.run;

jsPsych.run = async function (timeline) {
// check timeline here...
return origJsPsychRun(timeline);
};

return jsPsych;
};
}

export default lookitInitJsPsych;

0 comments on commit 578370d

Please sign in to comment.