Jeremy

Forum Replies Created

Viewing 15 posts - 1,261 through 1,275 (of 1,522 total)
  • Author
    Posts
  • in reply to: Matching Participants on Race/Gender? #5569
    Jeremy
    Keymaster

    Hello Callie,

    All the trials are generated at the very beginning of the experiment, along with the trial sequence, so you will not be able to dynamically select which trials to run the way you are trying to do here.

    What I can suggest is a two-step design; it’s not ideal but it works well. The idea is to re-open the experiment after you participants have filled the initial form, but this time with the values they typed as parameters in the URL.

    I will be assuming a table with a column race and a column sex that list the values in your experiment. For the sake of illustration, I used this command:

    AddTable("practicedatasource.csv", `item,race,sex,sentence
    1,Black or African American,Female,Sentence 1 for Black or African American Female
    1,Asian or Pacific Islander,Female,Sentence 1 for Asian or Pacific Islander Female
    1,Hispanic or Latinx,Female,Sentence 1 for Hispanic or Latinx Female
    1,Other,Female,Sentence 1 for Other Female
    1,Black or African American,Male,Sentence 1 for Black or African American Male
    1,Asian or Pacific Islander,Male,Sentence 1 for Asian or Pacific Islander Male
    1,Hispanic or Latinx,Male,Sentence 1 for Hispanic or Latinx Male
    1,Other,Male,Sentence 1 for Other Male
    2,Black or African American,Female,Sentence 2 for Black or African American Female
    2,Asian or Pacific Islander,Female,Sentence 2 for Asian or Pacific Islander Female
    2,Hispanic or Latinx,Female,Sentence 2 for Hispanic or Latinx Female
    2,Other,Female,Sentence 2 for Other Female
    2,Black or African American,Male,Sentence 2 for Black or African American Male
    2,Asian or Pacific Islander,Male,Sentence 2 for Asian or Pacific Islander Male
    2,Hispanic or Latinx,Male,Sentence 2 for Hispanic or Latinx Male
    2,Other,Male,Sentence 2 for Other Male`)

    Then you need to define your welcome-screen-form trial to run just in case the URL does not provide you with the demographic information:

    if (!GetURLParameter("id")||!GetURLParameter("race")||!GetURLParameter("sex")){
        
        newTrial("ParticipantID",
            defaultText.center(),defaultDropDown.center()
            ,
            newText("<p>Welcome and thank you for helping us!</p>").print()
            ,
            newText("<p>Please select your race and sex. Then, please enter your ID in the box below. "+
                    "You can find your ID XXX, and then press enter.</p>")
                .print()
            ,
            newDropDown("race", "Please select the race that best describes you.")
                .add("Black or African American", "Asian or Pacific Islander", "White", "Hispanic or Latinx", "Other")
                .print()
                .wait()
            ,
            newDropDown("sex", "Please select the sex that best describes you.")
                .add("Female", "Male")
                .print()
                .wait()
            ,
            newTextInput("inputID").center().print().wait()
            ,
            newFunction( ()=>window.open("server.py?"+
                "race="+getDropDown("race").value+"&"+
                "sex="+getDropDown("sex").value+"&"+
                "id="+getTextInput("inputID").value
            ) ).call()
        )
        
        SendResults()
        
        newTrial("end", newText("Another window opened, you can now close this one.").print() , newButton().wait() )
        
    }

    And then define the experimental trials to run upon opening the experiment’s page with the values passed as URL parameters:

    if (GetURLParameter("id") && GetURLParameter("race") && GetURLParameter("sex")){
        
        race = decodeURI(GetURLParameter("race"))
        sex = decodeURI(GetURLParameter("sex"))
        
        Sequence("practicesequence","etc")
        
        Template(
            GetTable("practicedatasource.csv")
                .filter("race", race)
                .filter("sex", sex)
            ,
            row => newTrial( "practicesequence" ,
                newText(row.sentence).print()
                ,
                newButton("Next").print().wait()
            )
            .log( "ID" , GetURLParameter("id") )
            .log( "race" , race )
            .log( "sex" , sex )
        )
        
    }

    Feel free to create two separate script files for each of these to keep your code cleaner

    Jeremy

    in reply to: html code with embedded script #5566
    Jeremy
    Keymaster

    No as long as you have it in your scripts it will load, so you no longer need:

    var HeadphoneCheckScriptTag = document.createElement("script");
    HeadphoneCheckScriptTag.src = "https://s3.amazonaws.com/mcd-headphone-check/v1.0/src/HeadphoneCheck.min.js";
    document.head.appendChild( HeadphoneCheckScriptTag );

    But maybe move this to the top of HeadphoneCheck.js:

    jQuery.prototype.on = function(...args) { return jQuery.prototype.bind.apply(this, args); }
    jQuery.prototype.prop = function(...args) { return jQuery.prototype.attr.apply(this, args); }

    Jeremy

    in reply to: Variable Math and Text Concatenation #5564
    Jeremy
    Keymaster

    You can definitely do what you describe, e.g.:

    PreloadZip("https://my.server/path/to/folder/archive-"+GetURLParameter("withsquare")+".zip")

    This will fetch file https://my.server/path/to/folder/archive-2.zip if you run your experiment with withsquare=2

    Jeremy

    in reply to: html code with embedded script #5562
    Jeremy
    Keymaster

    You would need to upload HeadphoneCheck.js to your project and use that instead of directly loading it from S3. Then you could edit the text in HeadphoneCheck.js.

    Jeremy

    in reply to: Questionnaire with questions on the same page #5561
    Jeremy
    Keymaster

    Hi Daniela,

    I would need more detail about the whole setup of your experiment, but here is an example that picks names from a table and shows them all in a single trial in a random order:

    AddTable("myTable", `name,presented
    John,yes
    Lucrecia,no
    Maggie,yes
    Vincent,yes
    Robert,no
    Shay,yes
    George,no
    __,void`)
    
    var names = [];
    var test;
    function handleNames(row){
        if (row.name=="__"){
            names = names.sort(v=>Math.random()>=0.5);
            return newTrial(
                ...names.map( r => {
                    test = test || newFunction(v=>true).test.is(true);
                    test = test.and( getScale(r.name+'-scale-'+r.presented).test.selected() );
                    return newText(r.name+": ")
                        .after(
                            newScale(r.name+'-scale-'+r.presented, 5)
                                .before( newText(" I'm sure I NEVER saw this name ") )
                                .after( newText(" I'm sure I DID see this name") )
                                .log()
                                .print()
                        )
                        .print();
                })
                ,
                newButton("Send")
                    .print()
                    .wait( test )
            )
        }
        names.push(row);
        return [];
    }
    
    
    Template( "myTable", row => handleNames(row) )

    I inserted the value of presented in the name of the Scale so you can retrieve the value from the results file, but I think it would be cleaner to just log the Scale score in the results file and add presented back to it from your input table when you analyze your results (in R, for example). If you have the option of defining an array in a js file rather than looking up a CSV table, as in Andrea’s case, I would do that instead, you wouldn’t need all these tricks like adding a row with __ in name.

    I also had to use a trick to make sure “Send” would only proceed if all Scale elements are selected.

    Jeremy

    in reply to: Audio timestamps #5557
    Jeremy
    Keymaster

    We can’t be sure that the delays are due to buffering, since unfortunately the result lines do not give us that information. It could be that no participant ever experienced buffering issues, but only slowdowns due to their browser’s poor performance at the time they took the experiment.

    For the same reason, using the end time will not give you more accurate measures, because you don’t know what exactly caused the delay.

    I realize this is a really frustrating situation, and I apologize for it—you expect the experimental software that you use to give you accurate measures, and PennController clearly failed to do so in this case. I will do my best to improve PennController’s performance.

    Then again, many other factors can impact the quality of one participant’s data, including the browser they use and how they use that browser. Safari for example is known to handle media elements differently from Chrome or Firefox (which I use to develop PennController). If you see that only some participants’ data manifest this kind of delays, it could be indicative that their specific configuration (eg. using a specific browser) contributed to the delays, and hopefully you could decide whether to filter them out for analysis purposes.

    Jeremy

    in reply to: Audio timestamps #5554
    Jeremy
    Keymaster

    Hi Angelica,

    If you compare the play lines to the end lines, you’ll notice that you have a 0 for play where you have a “value for the length of the audio file” for end—in both cases, it actually corresponds to the position in the audio file when the event is detected. So in your case, you always have an offset of 0, which means that the play event is triggered while the audio hasn’t started playing yet.

    I’m afraid that the delays you are seeing mean that execution was slow for at least some of your participants. Unfortunately, you should see lines in your results file for any buffering that happened, but I just noticed that there is a typo in the code and those won’t end up in the results file… I’ll fix this in the next release.

    If you see that the delays are systematically greater for some participants, I would say this is good indication that the conditions were not optimal for those, either because of a browser slowdown (for example due to many tabs open in parallel executing various scripts) or because the audio stream that’s normally cached got lost at some point and needed to buffer again.

    Jeremy

    in reply to: Setting up a slider #5551
    Jeremy
    Keymaster

    Hello Kazuko,

    I have unfortunately not gotten around to enabling label display with a slider, but you can sort of work around it using a Canvas. The command size applies to every visible element, including Scale elements. The command default allows you to set the value chosen on the scale when you first display it.

    Take a look a this code and let me know if you have questions:

    newTrial(
        newScale("test", 100)
            .size("500px","1em")
            .slider()
            .default(0)
        ,  
        newCanvas("container", "500px", "2.25em")
            .add( "left at 0%" , 0 , newText("0%") )
            .add( "center at 50%" , 0 , newText("50%") )
            .add( "right at 100%" , 0 , newText("100%") )
            .add( "center at 50%" , "bottom at 100%" , getScale("test") )
            .print()
        ,
        getScale("test").wait()
    )

    Jeremy

    in reply to: Behavioural task score #5545
    Jeremy
    Keymaster

    Hi Zoë,

    You code indeed shows the feedback text for me, but the code I gave also does so. Did you make sure to update to PennController 1.8? There was a bug with testing non-waitedKey elements in prior versions.

    And as you said, all of this is just to make sure you see the feedback text, but the code in this message still increments the Var element as expected (with 1.8 at least)

    Jeremy

    in reply to: basic installation #5544
    Jeremy
    Keymaster

    Hi Achim,

    Did you make sure to place your resource files (eg. 2fishRoundTank.png) in the www folder, as mentioned here? Using chunk_includes will only work for CSV tables and HTML files.

    Jeremy

    in reply to: DashedSentence in a PennController trial #5543
    Jeremy
    Keymaster

    Hello Max,

    Keep in mind that the original Ibex DashedSentence controller already comes with a mode that lets you show one word at a time. See the Ibex documentation.

    I apologize for the functions, I wrote them to be as compact as possible, but it’s not great for readability. Let me rewrite them more clearly:

    showWord = (arrayOfWords,showIndex) => "<p style='font-family: monospace;'>" + 
      arrayOfWords.map( (word,n) => {
        const letters = word.replace(/^\s*(\w+).*$/,"$1");  // this only keeps letters (and numbers)
        const punctuation = word.replace(/^\s*\w+/,"");  // this only keeps the punctuation characters
        if (n==showIndex) return "<span>"+letters+"</span>"+punctuation;
        else return "<span style='visibility: hidden;'>"+letters+"</span>"+punctuation;
      } ).join(" ") + "</p>";
    dashed = (name, sentence) => {
        const words = sentence.split(" ");  // Use space to break string into array
        return [  // Return an array of key.wait + text.print commands
            [ newText(name,showWord(words)).print() ], // First reveal no word
            ...words.map( (word,index) => [
                newKey(name+"-"+index+"-"+word," ").log().wait()
                , 
                getText(name).text( showWord(words,index) ) // reveal INDEXth word
            ]),
            [ newKey(name+"-last"," ").log().wait() ]
        ].flat(1);
    }

    As you can see, it’s the regular experessions in showWord that take care of separating the punctuation characters from the rest.

    Jeremy

    in reply to: Special marks in words #5537
    Jeremy
    Keymaster

    Hi,

    You can edit your files locally in a text editor, make sure to save them with a utf-8 encoding and then upload them to the PCIbex Farm, or replace your characters with HTML entities like: &oacute; &ntilde; etc.

    A list here.

    Jeremy

    in reply to: DashedSentence in a PennController trial #5504
    Jeremy
    Keymaster

    Hi Sander,

    If you go back to my first message, you will see that I edited it after introducing the Controller element in PennController 1.7. So you should probably try this:

    Sequence( "practice" , rshuffle("trial") )
    
    newTrial("practice",
      newController("DashedSentence", {s: "This is a practice item so that you know what the task looks like"})
        .print()
        .wait()
    )
    
    Template( "CSV_Template.csv", row =>
      newTrial( "trial" ,
         newController("DashedSentence", {s: row.Sentence})
            .print()
            .wait()
            .remove()
         ,
         newText("CompQ", row.CompQ)
           .print()
         ,
         newText("Negative feedback", "Not correct!")
        ,
        // This is a dirty javascript trick to randomize the answers
        newScale("result", ...[row.Results1, row.Results2].sort(()=>0.5-Math.random()) )
            .labelsPosition("right")
            .print()
            // We wait until the participant selects the right answer
            .wait(
              getScale("result").test.selected(row.Results1)
                    .failure( getText("Negative feedback").print() )
            )
      )
    )

    Jeremy

    in reply to: basic installation #5470
    Jeremy
    Keymaster

    Hello,

    PennController is activated as soon as you add PennController.js to the js_includes folder. If you didn’t change anything in the main script file of your experiment, you won’t see any difference, as PennController should be 100% compatible with native Ibex.

    Jeremy

    in reply to: Questionnaire with questions on the same page #5468
    Jeremy
    Keymaster

    Hello Andrea,

    I wrote my message under the assumption that you had multiple Number* and Question* columns in your table, and that for each row of your table, you wanted to print a new form containing as many questions as you have columns in your table.

    I suspect that it was a misunderstanding though, and that what you want to do is create a single form using multiple rows from your table, which contains a single Number and a single Question columns. There is no straightforward way of doing exactly this using PennController’s table+Template method, as it was designed to create multiple trials for your experiment, all based on the same template but filling the variable values by looking up a table.

    If all you have in your wbsi.csv table is a list of 15 rows with a Number column going from 1 to 15, and a Question column listing your different questions, you could do this instead:

    1. Create a file named wbsiQuestions.js in your Controllers folder and set a variable there to contain an array of your questions, like this (only two questions in this example—add the other ones, and don’t forget the line-ending commas):

    var wbsiQuestions = [
      "How do you feel about cheating?",
      "How do you feel about skipping class?"
    ];

    2. Keep the definition of the question function above in your main script file, and do this:

    newTrial("WBSI",
        ...wbsiQuestions.map( (v,i) => question(i,v) )
        ,
        newButton("next", "Next")
            .print()
            .wait()
    )

    Jeremy

Viewing 15 posts - 1,261 through 1,275 (of 1,522 total)