IAT Py -> JS Pavlovia Help

URL of experiment: https://gitlab.pavlovia.org/Lrivera/iat-hopefully-in-js

Description of the problem:
Hi all,

I am writing to ask if anyone has been able to successfully alter their PsychoPy programs in order to get them to work on pavlovia. I am inexperienced with JavaScript and I’ve only really gotten as far as uploading my experiment to pavlovia.

Right now, I cannot seem to test the experiment to see if it works. Having a functional iat work on pavlovia is a part of a very large project that I am working on, and that is why I am trying to get this to work.

Here are a few things I’ve tried:
converting my py code into js code
trying to rewrite my py code to the best of my ability in js

What might you all consider I try?

Kindest Regards,
Laur

Hi, please post some of your Python code snippets and attempted JavaScript translations, and hopefully someone will be able to put you on the right track.

Thank you for the idea Michael - I thought the code might appear via git link (I’m very new to using github/pavlovia)

Upon starting the experiment, I use this code (Py):

# Dependencies
import itertools  # for flattening lists of lists into lists
import random
import math  # for math.ceil() rounding up

# Import stimuli exemplars
exemplars_filename = 'stimuli.xlsx'
exemplars = data.importConditions(exemplars_filename)# Import stimuli exemplars

# Determine rows of examplars (i.e., max number of rows)
"""
This method creates a fully counterbalanced presentation of exemplars when there are 5
of them, but it will not present each one an equal number of times it the n diverges from 5.
"""
n_exemplars = len(exemplars)
list_multiplier = int(math.ceil(10/n_exemplars))  # math.ceil() rounds up. 10 is the derived from way block lengths are calculated. Admittedly, this comment doensn't adequately document why it's ten. Honestly, I have to work it out of my fingers every time and can't explain it.

# Trial generation function
def generate_trials(trial_type_column, multiplier):
    """Generate a shuffled list of stimuli exemplars from a column in an excel stimuli file""" 
    a = dict()  # declare a dict to be populated
    for i in range(len(exemplars)):
        a[i] = [exemplars[i][trial_type_column]] * multiplier  # populate the dict from vertical reads of the conditions
    a = a.values()  # extract only values (and not keys) from the list of dicts
    a = list(itertools.chain(*a))  # flatten the list of dicts into a list
    random.shuffle(a)  # shuffle this list, so that it can be drawn from by the trials
    return a

# declare trial rows (not sure if necessary, but can't be bothered to removed and test)
trial_rows = ""

# set block order based on participant code
participantNumber = int(expInfo['participant'])
if (participantNumber % 2) == 1: 
    block_order = 1
elif (participantNumber % 2) == 0:
    block_order = 2
else:
    print ("****condition file error: please enter a numeric participant code****")

___My attempt at writing it in JavaScript

csv = 'stimuli.csv'

var exemplars = .csv.toObjects(csv):


# loaded in stimuli (above)
var n_exemplars = exemplars.length;

list_multiplier = parseInt(math.ceil(10/n_exemplars))


function generate_trials(trial_type_column, multiplier) {
    var a = {};
    

    var i;
for (i = 0; i < n_exemplars; i++) { 
  a[i] = [exemplars[i][trial_type_column]] * multiplier 
}
    
    //#randomizing values in a
    var rand = a[Math.floor(Math.random() * a.length)];
    
}

trial_rows = ""


participantNumber = parseInt(expInfo['participant'])
if (participantNumber % 2) == 1: 
    block_order = 1
elif (participantNumber % 2) == 0:
    block_order = 2
else:
    print ("****condition file error: please enter a numeric participant code****")

___________________________________________
Upon starting each Block, this is the Py code:

# set the block length and the rows to pull from based on the current block 
# this layout follows Nosek et al. 2007, "The Implicit Association Test at age 7: A methodological and conceptual review"
if blocks.thisN == 0:
    trial_rows = "0:2" 
    n_block_repeats = 10   #2*10 = 20 trials
    modified_list_multiplier = list_multiplier
elif blocks.thisN == 1:
    trial_rows = "2:4" 
    n_block_repeats = 10   #2*10 = 20 trials
    modified_list_multiplier = list_multiplier
elif blocks.thisN == 2:
    trial_rows = "0:4" 
    n_block_repeats = 5   #4*5 = 20 trials
    modified_list_multiplier = list_multiplier
elif blocks.thisN == 3:
    trial_rows = "0:4" 
    n_block_repeats = 10   #4*10 = 40 trials
    modified_list_multiplier = list_multiplier
elif blocks.thisN == 4:
    trial_rows = "0:2" 
    n_block_repeats = 20   #2*20 = 40 trials
    modified_list_multiplier = list_multiplier * 2  # because this block has a different trials:categories ratio
elif blocks.thisN == 5:
    trial_rows = "0:4" 
    n_block_repeats = 5   #4*5 = 20 trials
    modified_list_multiplier = list_multiplier
elif blocks.thisN == 6:
    trial_rows = "0:4" 
    n_block_repeats = 10   #4*10 = 40 trials
    modified_list_multiplier = list_multiplier

# Generate list of stimuli for the block
text_trial_type_1_trials = generate_trials('text_trial_type_1_exemplars', modified_list_multiplier)  # function and variable determined at begin exp.
text_trial_type_2_trials = generate_trials('text_trial_type_2_exemplars', modified_list_multiplier)
text_trial_type_3_trials = generate_trials('text_trial_type_3_exemplars', modified_list_multiplier)
text_trial_type_4_trials = generate_trials('text_trial_type_4_exemplars', modified_list_multiplier)
img_trial_type_1_trials = generate_trials('img_trial_type_1_exemplars', modified_list_multiplier)
img_trial_type_2_trials = generate_trials('img_trial_type_2_exemplars', modified_list_multiplier)
img_trial_type_3_trials = generate_trials('img_trial_type_3_exemplars', modified_list_multiplier)
img_trial_type_4_trials = generate_trials('img_trial_type_4_exemplars', modified_list_multiplier)

# set category and attribute labels based on the block order and current block
if block_order == 1 and blocks.thisN <= 3:
    leftCategory = categoryA
    rightCategory = categoryB
elif block_order == 1 and blocks.thisN > 3:
    leftCategory = categoryB
    rightCategory = categoryA
elif block_order == 2 and blocks.thisN <= 3:
    leftCategory = categoryB
    rightCategory = categoryA
elif block_order == 2 and blocks.thisN > 3:
    leftCategory = categoryA
    rightCategory = categoryB

This is my attempt for the start of each block in JavaScript:

if blocks.thisN == 0:
    trial_rows = "0:2" 
    n_block_repeats = 10   //#2*10 = 20 trials
    modified_list_multiplier = list_multiplier
else if blocks.thisN == 1:
    trial_rows = "2:4" 
    n_block_repeats = 10   //#2*10 = 20 trials
    modified_list_multiplier = list_multiplier
else if blocks.thisN == 2:
    trial_rows = "0:4" 
    n_block_repeats = 5   //#4*5 = 20 trials
    modified_list_multiplier = list_multiplier
else if blocks.thisN == 3:
    trial_rows = "0:4" 
    n_block_repeats = 10   //#4*10 = 40 trials
    modified_list_multiplier = list_multiplier
else if blocks.thisN == 4:
    trial_rows = "0:2" 
    n_block_repeats = 20   //#2*20 = 40 trials
    modified_list_multiplier = list_multiplier * 2  # because this block has a different trials:categories ratio
else if blocks.thisN == 5:
    trial_rows = "0:4" 
    n_block_repeats = 5   //#4*5 = 20 trials
    modified_list_multiplier = list_multiplier
else if blocks.thisN == 6:
    trial_rows = "0:4" 
    n_block_repeats = 10   //#4*10 = 40 trials
    modified_list_multiplier = list_multiplier

//# Generate list of stimuli for the block
text_trial_type_1_trials = generate_trials('text_trial_type_1_exemplars', modified_list_multiplier)  # function and variable determined at begin exp.
text_trial_type_2_trials = generate_trials('text_trial_type_2_exemplars', modified_list_multiplier)
text_trial_type_3_trials = generate_trials('text_trial_type_3_exemplars', modified_list_multiplier)
text_trial_type_4_trials = generate_trials('text_trial_type_4_exemplars', modified_list_multiplier)
img_trial_type_1_trials = generate_trials('img_trial_type_1_exemplars', modified_list_multiplier)
img_trial_type_2_trials = generate_trials('img_trial_type_2_exemplars', modified_list_multiplier)
img_trial_type_3_trials = generate_trials('img_trial_type_3_exemplars', modified_list_multiplier)
img_trial_type_4_trials = generate_trials('img_trial_type_4_exemplars', modified_list_multiplier)

//# set category and attribute labels based on the block order and current block
if block_order == 1 and blocks.thisN <= 3:
    leftCategory = categoryA
    rightCategory = categoryB
else if block_order == 1 and blocks.thisN > 3:
    leftCategory = categoryB
    rightCategory = categoryA
else if block_order == 2 and blocks.thisN <= 3:
    leftCategory = categoryB
    rightCategory = categoryA
else if block_order == 2 and blocks.thisN > 3:
    leftCategory = categoryA
    rightCategory = categoryB

Both the Py code and JavaScript Code start the experiment with this line of code:

#declare accuracy feedback message variable
msg=""

Py code for the start of each trial:

# choose a random exemplar from the appropriate trial type list
if trial_type == 1:
    text_stimulus = text_trial_type_1_trials.pop()
    image_stimulus = img_trial_type_1_trials.pop()
elif trial_type == 2:
    text_stimulus = text_trial_type_2_trials.pop()
    image_stimulus = img_trial_type_2_trials.pop()
elif trial_type == 3:
    text_stimulus = text_trial_type_3_trials.pop()
    image_stimulus = img_trial_type_3_trials.pop()
elif trial_type == 4:
    text_stimulus = text_trial_type_4_trials.pop()
    image_stimulus = img_trial_type_4_trials.pop()

# set stimulus colors based on trial type
if trial_type == 1 or trial_type == 2:
     stimulusColor = [1, 1, 1]
elif trial_type >2:
     stimulusColor = [-1, 1, -1]

# set required and feedback responses
# attributes are invariate across blocks so can be determined based on trial type only
if trial_type == 3: #pos
    requiredAllowed = "i"
    requiredCorrect = "i"
    feedbackAllowed = "e"
    feedbackCorrect = "e"
elif trial_type == 4: #neg
    requiredAllowed = "e"
    requiredCorrect = "e"
    feedbackAllowed = "i"
    feedbackCorrect = "i"
# categories depend on block order, current block and trial type
if block_order == 1: 
    if blocks.thisN <= 3:
        if trial_type == 1: #flowers
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
        elif trial_type == 2: #insects
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"
    elif blocks.thisN >= 3:
        if trial_type == 1: #flowers
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"
        elif trial_type == 2: #insects
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
elif block_order == 2: 
    if blocks.thisN <= 3:
        if trial_type == 1: #flowers
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"
        elif trial_type == 2: #insects
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
    elif blocks.thisN >= 3:
        if trial_type == 1: #flowers
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
        elif trial_type == 2: #insects
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"

JavaScript for the start of each trial:

//# choose a random exemplar from the appropriate trial type list
if trial_type == 1:
    text_stimulus = text_trial_type_1_trials.pop()
    image_stimulus = img_trial_type_1_trials.pop()
else if trial_type == 2:
    text_stimulus = text_trial_type_2_trials.pop()
    image_stimulus = img_trial_type_2_trials.pop()
else if trial_type == 3:
    text_stimulus = text_trial_type_3_trials.pop()
    image_stimulus = img_trial_type_3_trials.pop()
else if trial_type == 4:
    text_stimulus = text_trial_type_4_trials.pop()
    image_stimulus = img_trial_type_4_trials.pop()

//# set stimulus colors based on trial type
if trial_type == 1 or trial_type == 2:
     stimulusColor = [1, 1, 1]
else if trial_type >2:
     stimulusColor = [-1, 1, -1]

//# set required and feedback responses
//# attributes are invariate across blocks so can be determined based on trial type only
if trial_type == 3: //#pos
    requiredAllowed = "i"
    requiredCorrect = "i"
    feedbackAllowed = "e"
    feedbackCorrect = "e"
else if trial_type == 4: //#neg
    requiredAllowed = "e"
    requiredCorrect = "e"
    feedbackAllowed = "i"
    feedbackCorrect = "i"
//# categories depend on block order, current block and trial type
if block_order == 1: 
    if blocks.thisN <= 3:
        if trial_type == 1: //#flowers
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
        else if trial_type == 2: //#insects
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"
    else if blocks.thisN >= 3:
        if trial_type == 1: //#flowers
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"
        else if trial_type == 2: //#insects
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
else if block_order == 2: 
    if blocks.thisN <= 3:
        if trial_type == 1: //#flowers
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"
        else if trial_type == 2: //#insects
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
    else if blocks.thisN >= 3:
        if trial_type == 1: //#flowers
            requiredAllowed = "i"
            requiredCorrect = "i"
            feedbackAllowed = "e"
            feedbackCorrect = "e"
        else if trial_type == 2: //#insects
            requiredAllowed = "e"
            requiredCorrect = "e"
            feedbackAllowed = "i"
            feedbackCorrect = "i"

finally I use this code for each frame for every trial:

Py -

if len(feedbackResponse.keys)<1:
    msg=""
else:
    msg="X"
JavaScript- 
if len(feedbackResponse.event.keycode)<1:
    msg=""
else:
    msg="X"

Hello @Laur_R,

Converting a PsychoPy experiment to one running online on pavlovia.org can be done almost entirely automatically from within PsychoPy3, without your having to write the code yourself. You will find a comprehensive documentation here: https://www.psychopy.org/online/online.html
Best of luck!

Alain