Task runs as expected on my local machine but code components are generating errors when uploaded to Pavlovia

OS: MacOS 15.3.2 (24D81)
PsychoPy version: 2024.2.4
Standard Standalone Installation? Yes
URL of experiment: Pavlovia
Do you want it to also run locally? Yes
Link to similar issue: The experiment works locally, but transferring online reports errors

What are you trying to achieve?

I am fairly new to PsychoPy/Pavlovia, but I managed to program a standard value-directed remembering (VDR) task in PsychoPy Builder. You can find specific details about the task structure and source files on my GitLab Repository (I included a detailed README file).

The task runs as expected on my local machine. However, when piloting the uploaded version on Pavlovia, I encounter an error (screenshot attached) right before the distractor phase begins for the practice lists (distractor_loop). For context, this distractor task occurs between each study and test phase, where participants solve a series of six multiplication problems, each presented individually on-screen. They have 5 seconds to submit an answer for each trial before the screen automatically advances. I suspect a similar issue will occur during the distractor phase for the experimental trials (distract_loop_val_l1).

To implement this functionality, I added Python code (“distract_code”) across multiple code component sections (e.g., Begin Experiment, Begin Routine, Each Frame). I assume some of this code is not properly converting to JavaScript when uploaded to Pavlovia. Could someone please help debug the issue so the task runs online?

Additionally, for each recall phase (recall_words_prac and recall_words_val), I included code (“recall_val_code”) to score and store participants’ recall output (is_correct) and point totals (block_total_points). Note that points are only relevant in the experimental trials, but this functionality is essential for providing feedback throughout the task (see pt_feedback routine and corresponding feedback_msg code). Since these code components are more complex than those for the distractor task, I anticipate potential JavaScript conversion issues there as well…. :downcast_face_with_sweat:

core.Clock() gets correctly translated in an Auto code component.

Thank you so much for the prompt response!! I updated my code to consistently use Auto → JS, and now all of my Python code appears to translate successfully into JavaScript (i.e., I do not receive the message: /* Syntax Error: Fix Python code */). However, I now encounter a new error (attached) immediately upon starting the task. This error seems to relate to the portion of the code that scores participants’ responses (correct vs. incorrect) and provides feedback.

The specific Python code that I used is the following:

timer = core.Clock()

# Load your CSV once:
study_list_file = "lists/recall_stim_words.csv"
study_df = data.importConditions(study_list_file)

# Organize correct words and types per block
block_study_words = {}
block_trial_types = {}

for row in study_df:
    blk = row["block"]
    wd = row["word"]
    t_type = row["trial_type"]
    
    if blk not in block_study_words:
        block_study_words[blk] = set()
        block_trial_types[blk] = t_type  # assumes consistent within block
        
    block_study_words[blk].add(wd.lower())

print(block_study_words)
print(block_trial_types)

# Levenshtein functions
def levenshtein_distance(s1, s2):
    if len(s1) < len(s2):
        s1, s2 = s2, s1  # Swap without recursion
    
    if len(s2) == 0:
        return len(s1)
    
    previous_row = list(range(len(s2) + 1))
    
    for i in range(len(s1)):
        c1 = s1[i]
        current_row = [i + 1]
        
        for j in range(len(s2)):
            c2 = s2[j]
            
            # Explicitly separate indices (JS compatible)
            idx_insertions = j + 1
            idx_deletions = j

            insertions = previous_row[idx_insertions] + 1
            deletions = current_row[idx_deletions] + 1
            
            # Explicit conditional
            if c1 == c2:
                substitutions = previous_row[j] + 0
            else:
                substitutions = previous_row[j] + 1

            current_row.append(min([insertions, deletions, substitutions]))

        previous_row = current_row

    return previous_row[-1]
  
def is_fuzzy_match(typed_word, correct_words, threshold=0.75):
    correct_words_list = list(correct_words)  # Ensure JS compatibility by explicitly converting to list
    for i in range(len(correct_words_list)):  # explicitly looping by index
        correct_word = correct_words_list[i]
        max_len = max(len(typed_word), len(correct_word))
        if max_len == 0:
            continue
        similarity = 1 - (levenshtein_distance(typed_word, correct_word) / max_len)
        if similarity >= threshold:
            return True
    return False
    

This Python code generated the following JavaScript:

var _pj;
function _pj_snippets(container) {
    function in_es6(left, right) {
        if (((right instanceof Array) || ((typeof right) === "string"))) {
            return (right.indexOf(left) > (- 1));
        } else {
            if (((right instanceof Map) || (right instanceof Set) || (right instanceof WeakMap) || (right instanceof WeakSet))) {
                return right.has(left);
            } else {
                return (left in right);
            }
        }
    }
    container["in_es6"] = in_es6;
    return container;
}
_pj = {};
_pj_snippets(_pj);
timer = new util.Clock();
study_list_file = "lists/recall_stim_words.csv";
study_df = data.importConditions(study_list_file);
block_study_words = {};
block_trial_types = {};
for (var row, _pj_c = 0, _pj_a = study_df, _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
    row = _pj_a[_pj_c];
    blk = row["block"];
    wd = row["word"];
    t_type = row["trial_type"];
    if ((! _pj.in_es6(blk, block_study_words))) {
        block_study_words[blk] = set();
        block_trial_types[blk] = t_type;
    }
    block_study_words[blk].add(wd.toLowerCase());
}
console.log(block_study_words);
console.log(block_trial_types);
function levenshtein_distance(s1, s2) {
    var c1, c2, current_row, deletions, idx_deletions, idx_insertions, insertions, previous_row, substitutions;
    if ((s1.length < s2.length)) {
        [s1, s2] = [s2, s1];
    }
    if ((s2.length === 0)) {
        return s1.length;
    }
    previous_row = list(util.range((s2.length + 1)));
    for (var i, _pj_c = 0, _pj_a = util.range(s1.length), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
        i = _pj_a[_pj_c];
        c1 = s1[i];
        current_row = [(i + 1)];
        for (var j, _pj_f = 0, _pj_d = util.range(s2.length), _pj_e = _pj_d.length; (_pj_f < _pj_e); _pj_f += 1) {
            j = _pj_d[_pj_f];
            c2 = s2[j];
            idx_insertions = (j + 1);
            idx_deletions = j;
            insertions = (previous_row[idx_insertions] + 1);
            deletions = (current_row[idx_deletions] + 1);
            if ((c1 === c2)) {
                substitutions = (previous_row[j] + 0);
            } else {
                substitutions = (previous_row[j] + 1);
            }
            current_row.push(Math.min([insertions, deletions, substitutions]));
        }
        previous_row = current_row;
    }
    return previous_row.slice((- 1))[0];
}
function is_fuzzy_match(typed_word, correct_words, threshold = 0.75) {
    var correct_word, correct_words_list, max_len, similarity;
    correct_words_list = list(correct_words);
    for (var i, _pj_c = 0, _pj_a = util.range(correct_words_list.length), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
        i = _pj_a[_pj_c];
        correct_word = correct_words_list[i];
        max_len = Math.max(typed_word.length, correct_word.length);
        if ((max_len === 0)) {
            continue;
        }
        similarity = (1 - (levenshtein_distance(typed_word, correct_word) / max_len));
        if ((similarity >= threshold)) {
            return true;
        }
    }
    return false;
}

The # Load your CSV once: section will need to be in a Both code component.

Move your function definitions to Before Experiment