-
Notifications
You must be signed in to change notification settings - Fork 1
Scripting Examples
This page gathers some examples of common problems that can be solved by scripting. For more information on scripting in ESMira in general, see the Scripting page. For information on the scripting langue, see the Merlin Language Reference.
Task: Some item should only be displayed if the item "demo_item" on a previous pages was responded to in a certain way. For the purpose of this example we will assume that "demo_item" is a 5-point Likert scale, and we only want to show other item if the response to "demo_item" was 4 or 5.
To achieve this, we give the item we want to dynamically display the following relevance script:
getQuestionnaireVar("demo_item") >= 4;
This will retrieve the response to "demo_item" and check if it was 4 or greater. If so, the item will be shown. If the response was less than 4, or if "demo_item" hasn't been filled out, this item will be hidden.
Task: Some item should only be displayed if the item "demo_item" on a previous page was responded to in a certain way. "demo_item" here is a multiple choice item with the answer options "apple", "banana", "cucumber", "date", "eggplant" (in that order). We want to display the item if any of the vegetables (cucumber or eggplant, i.e., indices 3 or 5) have been selected.
To achieve this, we give the item we want to dynamically display the following relevance script:
getQuestionnaireVarAlternative("demo_item", [3, 5]) >> any();
This will retrieve the answers to the third and fifth item to "demo_item" in particular. The alternative responses for a multiple choice item are 1
if the response option was selected, and 0
otherwise, so we get an array of two elements, each either 1
or 0
. We pipe this array into the any()
function, which will be 1
if any of the elements in the input array are truthy, which in our case means if any of the response options that interest us have been selected. Otherwise it will return 0
, which will hide this item.
Task: Some item should only be displayed every second time the questionnaire is filled out.
To achieve this, we give the item the following relevance script:
init{
globals.should_display = true;
}
return globals.should_display;
We initialize a should_display
field on the globals
object. We initialize this to true
. Then we simply return this value. This means the item will be displayed the first time this questionnaire is displayed. We could of course toggle this variable right in this script, and most of the time that works out fine. However, if we want to have this item in every other line of the dataset, then we could run into issues if the participant starts the questionnaire, and the item tells us that it has been displayed, so it won't show next time. To remedy this, we put the code for toggling the variable in the scripting end block:
globals.should_display = !globals.should_display;
If we get here than we can be sure the variable has already be initialized, so accessing it is safe. So we can simply apply a boolean not operation and flip the switch. This happens every time the participant fully fils out the questionnaire. If they saw the item, they won't see it the next time, but if they didn't see it, it will be there next time.
Task: We want to calculate the moving average of an instrument consisting of four variables (using a sum score) from the last three days and store the result in the dataset. For the purpose of this example imagine that we have four items called "demo_item_1", "demo_item_2", "demo_item_3", and "demo_item_4". These items measure on a 5-point Likert scale. The items "demo_item_2" and "demo_item_4" are reverse-coded. The questionnaire has a virtual input with the name "moving_avg" configured.
In this example we will also make sure to only add the value if all the items have been filled out. We can do all the necessary calculations in the scripting end block of this questionnaire:
init{
globals.last_values = [];
}
// Calculate the sum of the four items
item_values = [
getQuestionnaireVar("demo_item_1"),
getQuestionnaireVar("demo_item_2"),
getQuestionnaireVar("demo_item_3"),
getQuestionnaireVar("demo_item_4")
];
item_values[2, 4] = reverse(item_values[2, 4], 1, 5); // Reverting the reverse-scored items
sum_score = sum(item_values);
today = day(); // Number of days since the study started.
if(sum_score != none) { // If any of the items hasn't been filled out, sum score will be none at this point.
// Create an object that holds the day and the corresponding sum_score.
values_obj = object;
values_obj.day = today;
values_obj.value = sum_score;
globals.last_values = globals.last_values..values_obj; // Add this value to the list.
}
// Filtering the list for data points from the last three days.
new_values = [];
for(values_obj in globals.last_values) { // Note that this is a different values_obj
if(values_obj.day > today - 3) {
new_values = new_values..[values_obj];
}
}
globals.last_values = new_values; // Replace the global value with the filtered array.
setVirtualItem("moving_avg", mean(globals.last_values)); // Calculates the mean of the now correctly filtered dataset and assigns it to moving_avg.
Task: A questionnaire that can be filled out by the participant at any time triggers a follow-up questionnaire (via the trigger system) some time after the initial questionnaire. We want a way to associate the resulting two datasets, to make merging easier later.
In a case like this, where a participant may fill out the questionnaire at will (e.g., because it is event-contingent), we cannot just use the date for matching lines in the dataset of the first questionnaire to lines in the dataset of the second questionnaire, as there may be multiple lines per date. As participants might not fill out the follow-up questionnaire, we also are unable to simply match the lines one-to-one. The simplest solution to this is to have an id/counter that increments with each questionnaire, and that is saved in both questionnaires. That way the datasets can easily be matched by this id.
To do so, we create a virtual input called "counter_id" on each of the questionnaires. Then, we put the following code into the scripting end block of the first questionnaire:
init {
globals.counter = 0;
}
globals.counter = globals.counter + 1;
setVirtualInput("counter_id", globals.counter);
Then we put the following code into the scripting end block of the follow-up questionnaire.
setVirtualInput("counter_id", globals.counter);
Each time the first questionnaire is filled out, the counter is changed and the value saved. Then, if the follow-up questionnaire is filled out, the same value is also saved to that questionnaire, allowing to match up the corresponding lines across the datesets.