Conditional skipping of routine

OS: MacOS Ventura 13.4.1 (22F82)
PsychoPy version: v2023.2.2
Standard Standalone: yes
What are you trying to achieve?: To skip a given routine only if a condition has been met.

What did you try to make it work?:
I defined the following variables earlier in the task:

condition_1_met = false;
condition_2_met = false;
condition_3_met = false;
thresh_1 = 15;
thresh_2 = 30;
thresh_3 = 50;
trials_done_1 = (total_trial_count >= thresh_1);
trials_done_2 = (total_trial_count >= thresh_2);
trials_done_3 = (total_trial_count >= thresh_3);

// As well as
total_trial_count = 0;

And In the end routine code component in the chooseItem routine (purple, left of screenshot below), total_trial_count is updated like this:

total_trial_count = (total_trial_count + 1);


In red (right) is the ā€œrecalibration_taskā€ routine I want to skip if the conditions aren’t met:

In this recalibration_task there I have the following begin routine code component:

// Check the conditions
if (trials_done_1 && !condition_1_met) {
    // Code for when total_trial_number is greater than or equal to thresh_1
    continueRoutine = true;
    recalibration_loop_2.finished = false;

    // Update the variable to indicate the condition has been met
    condition_1_met = true;
}

if (trials_done_2 && !condition_2_met) {
    // Code for when total_trial_number is greater than or equal to thresh_2
    continueRoutine = true;
    recalibration_loop_2.finished = false;

    // Update the variable to indicate the condition has been met
    condition_2_met = true;
}

if (trials_done_3 && !condition_3_met) {
    // Code for when total_trial_number is greater than or equal to thresh_3
    continueRoutine = true;
    recalibration_loop_2.finished = false;

    // Update the variable to indicate the condition has been met
    condition_3_met = true;
}

What specifically went wrong when you tried that?:
With this current version of the code, the routine is not skipped and is run every time I finish a block.

(Editing the task layout and dividing it in 3, interleaved with calibration routines, would be way too much work, hopefully there is a way to do it using a few if statements?)

Thanks a lot!

Hi @Tristan_White,

Firstly, this might sound like an obvious question but this is only to be ran online I assume since you’re using JS rather than Python? Also, do you want to skip the whole recalibration_loop_2 or just one instance of the recalibration_task routine?

Either way, a couple of things might help:

  1. continueRoutine is set to true by default, therefore to use conditional statements you’ll need to use continueRoutine = false to skip the routine.
  2. The same logic applies to loop.finished. If the loop is there, Builder assumes it should be run. Therefore you can only change the default behaviour by either using loop.finished = true or setting the number of loop reps to 0.

Thanks,

Kim

Hi Kimberly, thanks for the reply.

  • This is only to be run online, yes.
  • And I want to skip the whole ā€˜recalibration_loop_2’ loop if the conditions are not met.

Thanks for reminding me that I hadn’t delt with the default condition, so now I’ve got this loop, with the final ā€˜else’ statement that is meant to take care of the situations where the none of the 3 condition sets are met:

// Check the conditions and set continueRoutine and recalibration_loop_2.finished based on conditions
if (trials_done_1 && !condition_1_met) {
    // Code for when total_trial_number is greater than or equal to threshold 1 
    condition_1_met = true;
    continueRoutine = true;
    recalibration_loop_2.finished = false;
} else if (trials_done_2 && !condition_2_met) {
    // Code for when total_trial_number is greater than or equal to threshold 2
    condition_2_met = true;
    continueRoutine = true;
    recalibration_loop_2.finished = false;
} else if (trials_done_3 && !condition_3_met) {
    // Code for when total_trial_number is greater than or equal to threshold 3
    condition_3_met = true;
    continueRoutine = true;
    recalibration_loop_2.finished = false;
} else {
    continueRoutine = false;
    recalibration_loop_2.finished = true;
}

Sadly it’s still not working, the calibration is skipped every time, even when 1 of the 3 condition sets is met.
It’s possible that my code is flawed, I’m quite new at coding in JS.
What do you think ?

Thanks again

Tristan

I strongly recommend coding in Python auto components and only making changes to the JS when absolutely necessary.

To debug you need to add some print / console.log statements to check the values of trials_done_x and condition_x_met before the checks.

https://psychopy.org/online/psychoJSCodingDebugging.html

1 Like

I completely agree with @wakecarter here @Tristan_White, especially if (like me!) you’re not too familiar with JS.

If you weren’t already aware, setting the code type of a code component to Auto>JS allows you to type Python code on the left hand side and have it automatically transpiled to JS. This means you can check your logic works locally, using the print statements that Wake’s mentioned before testing online, which is generally a much quicker way of debugging.

My guess here, if the routine is now being skipped every time, is that all 3 of your if statements are returning false. Leaving your else statement to be the only thing that’s executed.

Hi both,
Thanks a lot for the replies.
Following your advice I’ve written the code component in python (Auto>JS), and with some more tweaks I’ve got the calibration routine to be run only 3 times, each after reaching their respective threshold conditions.

There’s still one thing, it doesn’t loop, the routine is only ran once (instead of 8 in this case).

Here’s the code (the commented out recalibration_loop_2.finished = False/True code doesn’t seem to change anything):

# Check the conditions
if total_trial_count >= thresh_1 and not routine_run_1:
    # Code for when total_trial_number is greater than or equal to 15
    routine_run_1 = True
    continueRoutine = True
#    recalibration_loop_2.finished = False

elif total_trial_count >= thresh_2 and not routine_run_2:
    # Code for when total_trial_number is greater than or equal to 30
    routine_run_2 = True
    continueRoutine = True
#    recalibration_loop_2.finished = False

elif total_trial_count >= thresh_3 and not routine_run_3:
    # Code for when total_trial_number is greater than or equal to 50
    routine_run_3 = True
    continueRoutine = True
#    recalibration_loop_2.finished = False

else:
    # Code for when none of the conditions are met
    continueRoutine = False
#    recalibration_loop_2.finished = True

I think there’s some specific psychopy knowledge that I don’t have that could help which is why I’m coming back to you guys, I don’t fully understand how the loop system works.

I’ll post the working code once it works!

Thanks again,
Tristan

Great to hear you’re making progress!

Could you show us how your loop is set up?

Thanks,

Kim

Sure, here are a couple screenshots, and the ā€˜calibration_code_2’ component, do you need something else?

The xlsx file:
Screenshot 2023-11-23 at 14.11.31

And the ā€˜calibration_code_2’ component:

// Turn off mouse event listener
//window.webgazer.removeMouseEventListeners();
// Re-enable mouse events 
window.webgazer.addMouseEventListeners();

// Hide video feed and container behind it, they prevent clicking on calibration dots behind them
//document.getElementById('webgazerFaceFeedbackBox').style.display = 'none';
//document.getElementById('webgazerVideoFeed').style.display = 'none';
//document.getElementById('webgazerVideoContainer').style.display = 'none';

// Save experiment window size
psychoJS.experiment.addData('window_width', psychoJS.window.size[0]);
psychoJS.experiment.addData('window_height', psychoJS.window.size[1]);

// Position calibration_square using width and height of window
//var canvas = psychoJS.window.size;
var scaling = [
  canvas[0] <= canvas[1]? 1: canvas[0] / canvas[1],
  canvas[1] <= canvas[0]? 1: canvas[1] / canvas[0]
];
var newPos = [
    calibration_x * scaling[0],
    calibration_y * scaling[1]
];
console.log(newPos);  //calibration_square coordinates displayed in console and on screen might be slightly different (0.01 error margin by the looks of it)
recalibration_stim_2.setPos(newPos);

Thanks,
Tristan

Hi @Kimberley_Dundas, since I haven’t found a fix to this yet I was wondering if you had some input, let me know if you need something else.
Thanks again,
Tristan W

Hi @Tristan_White,

The conditional statements you have look like they’re set up so that the routine will run as long as at least one of those 3 conditions are not met. As you’re not resetting anything, after routine_run_1/2/3 have happened, all of those if statements you have will not be met and the else statement will always be the thing that’s run. Therefore the routine will not be ran again. Depending on what you want to happen, you’ll need to set routine_run_1/2/3 to be back to false at some point. You can check these things yourself as Wake suggested earlier by using print statements to check your logic.

Thanks,

Kim

For the record and to close the thread, I figured it out in the end, here is the code:

# Check the conditions
if total_trial_count + 1 >= maxTrials:
    recalibration_loop_2.finished=True
    accuracy_loop_3.finished=True
    continueRoutine = False

elif total_trial_count >= thresh_1 and not routine_run_1:
    # Code for when total_trial_number is greater than or equal to thresh 1
    routine_run_1_counter += 1
    if routine_run_1_counter >= n_reps_cal:
        routine_run_1 = True
        continueRoutine = True

    # Check if WG needs to be reset (on every rep before routine_run_1_counter reaches n_reps_cal)
    if not WG_reset1:
        # Clear WG data
        window.webgazer.clearData()
        # Re-enable mouse events 
        window.webgazer.addMouseEventListeners()
        # Set WG_reset to True
        WG_reset1 = True

elif total_trial_count >= thresh_2 and not routine_run_2:
    # Code for when total_trial_number is greater than or equal to thresh 2
    routine_run_2_counter += 1
    if routine_run_2_counter >= n_reps_cal:
        routine_run_2 = True
        continueRoutine = True
        # Check if WG needs to be reset (only on first rep)
    if not WG_reset2:
        # Clear WG data
        window.webgazer.clearData()
        # Re-enable mouse events 
        window.webgazer.addMouseEventListeners()
        # Set WG_reset to True
        WG_reset2 = True

elif total_trial_count >= thresh_3 and not routine_run_3:
    # Code for when total_trial_number is greater than or equal to thresh 3
    routine_run_3_counter += 1
    if routine_run_3_counter >= n_reps_cal:
        routine_run_3 = True
        continueRoutine = True
        # Check if WG needs to be reset (only on first rep)
    if not WG_reset3:
        # Clear WG data
        window.webgazer.clearData()
        # Re-enable mouse events 
        window.webgazer.addMouseEventListeners()
        # Set WG_reset to True
        WG_reset3 = True

else:
    # Code for when none of the conditions are met
    continueRoutine = False
    recalibration_loop_2.finished = True

Thanks for the help!
Tristan