Skip to content

Commit

Permalink
Finished survey and sketchpad, have questions about data for response…
Browse files Browse the repository at this point in the history
… whether it needs to be 'nested' vs 'parameters' or if the type definition for response is correct
  • Loading branch information
vzhang03 committed Jun 20, 2024
1 parent f482161 commit f26f0a7
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 26 deletions.
97 changes: 80 additions & 17 deletions packages/plugin-sketchpad/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";

import { version } from "../package.json";

const info = <const>{
name: "sketchpad",
version: version,
parameters: {
/**
* The shape of the canvas element. Accepts `'rectangle'` or `'circle'`
Expand All @@ -11,14 +14,14 @@ const info = <const>{
default: "rectangle",
},
/**
* Width of the canvas in pixels.
* Width of the canvas in pixels when `canvas_shape` is a `"rectangle"`.
*/
canvas_width: {
type: ParameterType.INT,
default: 500,
},
/**
* Width of the canvas in pixels.
* Height of the canvas in pixels when `canvas_shape` is a `"rectangle"`.
*/
canvas_height: {
type: ParameterType.INT,
Expand Down Expand Up @@ -53,7 +56,7 @@ const info = <const>{
default: null,
},
/**
* Background color of the canvas.
* Color of the canvas background. Note that a `background_image` will render on top of the color.
*/
background_color: {
type: ParameterType.STRING,
Expand All @@ -67,14 +70,14 @@ const info = <const>{
default: 2,
},
/**
* The color of the stroke on the canvas
* The color of the stroke on the canvas.
*/
stroke_color: {
type: ParameterType.STRING,
default: "#000000",
},
/**
* An array of colors to render as a palette of options for stroke colors.
* Array of colors to render as a palette of choices for stroke color. Clicking on the corresponding color button will change the stroke color.
*/
stroke_color_palette: {
type: ParameterType.STRING,
Expand All @@ -96,36 +99,38 @@ const info = <const>{
default: "abovecanvas",
},
/**
* Whether to save the final image in the data as dataURL
* Whether to save the final image in the data as a base64 encoded data URL.
*/
save_final_image: {
type: ParameterType.BOOL,
default: true,
},
/**
* Whether to save the set of strokes that generated the image
* Whether to save the individual stroke data that generated the final image.
*/
save_strokes: {
type: ParameterType.BOOL,
default: true,
},
/**
* If this key is held down then it is like the mouse button being clicked for controlling
* the flow of the "ink".
* If this key is held down then it is like the mouse button being held down.
* The "ink" will flow when the button is held and stop when it is lifted.
* Pass in the string representation of the key, e.g., `'a'` for the A key
* or `' '` for the spacebar.
*/
key_to_draw: {
type: ParameterType.KEY,
default: null,
},
/**
* Whether to show the button that ends the trial
* Whether to show the button that ends the trial.
*/
show_finished_button: {
type: ParameterType.BOOL,
default: true,
},
/**
* The label for the button that ends the trial
* The label for the button that ends the trial.
*/
finished_button_label: {
type: ParameterType.STRING,
Expand Down Expand Up @@ -175,42 +180,100 @@ const info = <const>{
default: "Redo",
},
/**
* Array of keys that will end the trial when pressed.
* This array contains the key(s) that the participant is allowed to press in order to end
* the trial. Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`,
* `'ArrowDown'`) - see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)
* and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)
* for more examples. Any key presses that are not listed in the array will be ignored. The default value of `"NO_KEYS"`
* means that no keys will be accepted as valid responses. Specifying `"ALL_KEYS"` will mean that all responses are allowed.
*/
choices: {
type: ParameterType.KEYS,
default: "NO_KEYS",
},
/**
* Length of time before trial ends. If `null` the trial will not timeout.
* Length of time before the trial ends. If `null` the trial will continue indefinitely
* (until another way of ending the trial occurs).
*/
trial_duration: {
type: ParameterType.INT,
default: null,
},
/**
* Whether to show a countdown timer for the remaining trial duration
* Whether to show a timer that counts down until the end of the trial when `trial_duration` is not `null`.
*/
show_countdown_trial_duration: {
type: ParameterType.BOOL,
default: false,
},
/**
* The html for the countdown timer.
* The HTML to use for rendering the countdown timer. The element with `id="sketchpad-timer"`
* will have its content replaced by a countdown timer in the format `MM:SS`.
*/
countdown_timer_html: {
type: ParameterType.HTML_STRING,
default: `<span id="sketchpad-timer"></span> remaining`,
},
},
data: {
/** The length of time from the start of the trial to the end of the trial. */
rt: {
type: ParameterType.INT,
},
/** If the trial was ended by clicking the finished button, then `"button"`. If the trial was ended by pressing a key, then the key that was pressed. If the trial timed out, then `null`. */
response: {
type: ParameterType.STRING,
},
/** If `save_final_image` is true, then this will contain the base64 encoded data URL for the image, in png format. */
png: {
type: ParameterType.STRING,
},
/** If `save_strokes` is true, then this will contain an array of stroke objects. Objects have an `action` property that is either `"start"`, `"move"`, or `"end"`. If `action` is `"start"` or `"move"` it will have an `x` and `y` property that report the coordinates of the action relative to the upper-left corner of the canvas. If `action` is `"start"` then the object will also have a `t` and `color` property, specifying the time of the action relative to the onset of the trial (ms) and the color of the stroke. If `action` is `"end"` then it will only have a `t` property. */
strokes: {
type: ParameterType.COMPLEX,
array: true,
parameters: {
action: {
type: ParameterType.STRING,
},
x: {
type: ParameterType.INT,
optional: true,
},
y: {
type: ParameterType.INT,
optional: true,
},
t: {
type: ParameterType.INT,
optional: true,
},
color: {
type: ParameterType.STRING,
optional: true,
},
},
},
},
};

type Info = typeof info;

/**
* **sketchpad**
* This plugin creates an interactive canvas that the participant can draw on using their mouse or touchscreen.
* It can be used for sketching tasks, like asking the participant to draw a particular object.
* It can also be used for some image segmentation or annotation tasks by setting the `background_image` parameter to render an image on the canvas.
*
* The plugin stores a [base 64 data URL representation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) of the final image.
* This can be converted to an image file using [online tools](https://www.google.com/search?q=base64+image+decoder) or short programs in [R](https://stackoverflow.com/q/58604195/3726673), [python](https://stackoverflow.com/q/2323128/3726673), or another language of your choice.
* It also records all of the individual strokes that the participant made during the trial.
*
* !!! warning
* This plugin generates **a lot** of data. Each trial can easily add 500kb+ of data to a final JSON output.
* You can reduce the amount of data generated by turning off storage of the individual stroke data (`save_strokes: false`) or storage of the final image (`save_final_image: false`) if your use case doesn't require that information.
* If you are going to be collecting a lot of data with this plugin you may want to save your data to your server after each trial and not wait until the end of the experiment to perform a single bulk upload.
* You can do this by putting data saving code inside the [`on_data_update` event handler](../overview/events.md#on_data_update).
*
* jsPsych plugin for displaying a canvas stimulus and getting a slider response
*
* @author Josh de Leeuw
* @see {@link https://www.jspsych.org/latest/plugins/sketchpad/ sketchpad plugin documentation on jspsych.org}
Expand Down
69 changes: 60 additions & 9 deletions packages/plugin-survey/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,73 @@
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
import * as SurveyJS from "survey-knockout-ui";

import { version } from "../package.json";

const info = <const>{
name: "survey",
version: version,
parameters: {
/**
* A SurveyJS survey model defined as a JavaScript object.
* See: https://surveyjs.io/form-library/documentation/design-survey/create-a-simple-survey#define-a-static-survey-model-in-json
*
* A SurveyJS-compatible JavaScript object that defines the survey (we refer to this as the survey 'JSON'
* for consistency with the SurveyJS documentation, but this parameter should be a JSON-compatible
* JavaScript object rather than a string). If used with the `survey_function` parameter, the survey
* will initially be constructed with this object and then passed to the `survey_function`. See
* the [SurveyJS JSON documentation](https://surveyjs.io/form-library/documentation/design-survey/create-a-simple-survey#define-a-static-survey-model-in-json) for more information.
*
*/
survey_json: {
type: ParameterType.OBJECT,
default: {},
pretty_name: "Survey JSON object",
},
/**
* A SurveyJS survey model defined as a function. The function receives an empty SurveyJS survey object as an argument.
* See: https://surveyjs.io/form-library/documentation/design-survey/create-a-simple-survey#create-or-change-a-survey-model-dynamically
*
* A function that receives a SurveyJS survey object as an argument. If no `survey_json` is specified, then
* the function receives an empty survey model and must add all pages/elements to it. If a `survey_json`
* object is provided, then this object forms the basis of the survey model that is passed into the `survey_function`.
* See the [SurveyJS JavaScript documentation](https://surveyjs.io/form-library/documentation/design-survey/create-a-simple-survey#create-or-change-a-survey-model-dynamically) for more information.
*
*/
survey_function: {
type: ParameterType.FUNCTION,
default: null,
pretty_name: "Survey function",
},
/**
* A function that can be used to validate responses. This function is called whenever the SurveyJS onValidateQuestion event occurs.
* See: https://surveyjs.io/form-library/documentation/data-validation#implement-custom-client-side-validation
* A function that can be used to validate responses. This function is called whenever the SurveyJS `onValidateQuestion`
* event occurs. (Note: it is also possible to add this function to the survey using the `survey_function` parameter -
* we've just added it as a parameter for convenience).
*/
validation_function: {
type: ParameterType.FUNCTION,
default: null,
pretty_name: "Validation function",
},
},
data: {
/** An object containing the response to each question. The object will have a separate key (identifier) for each question. If the `name` parameter is defined for the question (recommended), then the response object will use the value of `name` as the key for each question. If any questions do not have a name parameter, their keys will named automatically, with the first unnamed question recorded as `question1`, the second as `question2`, and so on. The response type will depend on the question type. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
response: {
type: ParameterType.COMPLEX,
nested: {
identifier: {
type: ParameterType.STRING,
},
response: {
type:
ParameterType.STRING |
ParameterType.INT |
ParameterType.FLOAT |
ParameterType.BOOL |
ParameterType.OBJECT,
},
},
},
/** The response time in milliseconds for the participant to make a response. The time is measured from when the questions first appear on the screen until the participant's response(s) are submitted. */
rt: {
type: ParameterType.INT,
},
},
};

type Info = typeof info;
Expand Down Expand Up @@ -63,12 +99,27 @@ const jsPsychSurveyCssClassMap = {
};

/**
* **survey**
* SurveyJS version: 1.9.138
*
* This plugin is a wrapper for the [**SurveyJS form library**](https://surveyjs.io/form-library/documentation/overview). It displays survey-style questions across one or more pages. You can mix different question types on the same page, and participants can navigate back and forth through multiple survey pages without losing responses. SurveyJS provides a large number of built-in question types, response validation options, conditional display options, special response options ("None", "Select all", "Other"), and other useful features for building complex surveys. See the [Building Surveys in jsPsych](../overview/building-surveys.md) page for a more detailed list of all options and features.
*
* With SurveyJS, surveys can be defined using a JavaScript/JSON object, a JavaScript function, or a combination of both. The jsPsych `survey` plugin provides parameters that accept these methods of constructing a SurveyJS survey, and passes them into SurveyJS. The fact that this plugin just acts as a wrapper means you can take advantage of all of the SurveyJS features, and copy/paste directly from SurveyJS examples into the plugin's `survey_json` parameter (for JSON object configuration) or `survey_function` parameter (for JavaScript code).
*
* This page contains the plugin's reference information and examples. The [Building Surveys in jsPsych](../overview/building-surveys.md) page contains a more detailed guide for using this plugin.
*
* For the most comprehensive guides on survey configuration and features, please see the [SurveyJS form library documentation](https://surveyjs.io/form-library/documentation/overview) and [examples](https://surveyjs.io/form-library/examples/overview).
*
* !!! warning "Limitations"
*
* The jsPsych `survey` plugin is not compatible with certain jsPsych and SurveyJS features. Specifically:
*
* - **It is not always well-suited for use with jsPsych's [timeline variables](../overview/timeline.md#timeline-variables) feature.** This is because the timeline variables array must store the entire `survey_json` object for each trial, rather than just the parameters that change across trials, which are nested within the `survey_json` object. We offer some alternative methods for dynamically constructing questions/trials in [this section](../overview/building-surveys.md#defining-survey-trialsquestions-programmatically) of the Building Surveys in jsPsych documentation page.
* - **It does not support the SurveyJS "[complete page](https://surveyjs.io/form-library/documentation/design-survey/create-a-multi-page-survey#complete-page)" parameter.** This is a parameter for HTML formatted content that should appear after the participant clicks the 'submit' button. Instead of using this parameter, you should create another jsPsych trial that comes after the survey trial to serve the same purpose.
* - **It does not support the SurveyJS question's `correctAnswer` property**, which is used for SurveyJS quizzes and automatic response scoring. SurveyJS does not store this value or the response score in the data - instead this is only used to display scores on the survey's 'complete page'. Since the complete page is not supported, this 'correctAnswer' property also does not work as intended in the jsPsych plugin.
*
* jsPsych plugin for presenting complex questionnaires using the SurveyJS library
*
* @author Becky Gilbert
* @see {@link https://www.jspsych.org/plugins/survey/ survey plugin documentation on jspsych.org}
* @see {@link https://www.jspsych.org/latest/plugins/survey/ survey plugin documentation on jspsych.org}
*/
class SurveyPlugin implements JsPsychPlugin<Info> {
static info = info;
Expand Down

0 comments on commit f26f0a7

Please sign in to comment.