Jeremy

Forum Replies Created

Viewing 15 posts - 211 through 225 (of 1,522 total)
  • Author
    Posts
  • in reply to: Logging randomly generated IDs for MTurkers #9879
    Jeremy
    Keymaster

    Hi Jun,

    What version of PennController are you using? Also, what does your HTML file look like?

    Using PennController 2.0 and this HTML code, things work just fine for me:

    <script>
        function newCode() {
            const randomCode = [...Array(8)].map(()=>Math.round(Math.random()*10)).join('');
            document.querySelector("#code").value = randomCode;
            document.querySelector("#codeDisplay").innerText = randomCode;
        }
    </script>
    
    <button onclick="newCode()">Generate code</button>
    
    <input id='code' type='text' name='code' style='display:none;' class='obligatory' />
    
    <p>Your code is <span id='codeDisplay'></span></p>

    Jeremy

    in reply to: Dot-memory test #9878
    Jeremy
    Keymaster

    Hi Ebru,

    You could get there by using the Selector element, if you created one per cell, but the code would quickly become cumbersome. A more concise solution would be to use three Scale elements, one per row, that you set as checkboxes. Then you manipulate the aesthetics to make a checked option appear as a black square, and an unchecked option as a white square

    The test part in wait is a little tricky, because up to PennController 2.0, .test.selected does not behave as expected on a checkbox Scale. However, one can write some concise javascript code to test that the right boxes are (un)selected

    Here’s the script:

    Template("dotmemorization.csv", row =>
        newTrial("dot-pattern",
            defaultScale.checkbox().center().log().print()
            ,
            newScale("row1", " ", " ", " "),newScale("row2", " ", " ", " "),newScale("row3", " ", " ", " ")
            ,
            getScale("row1").select(row.r1c1==1?0:-1).select(row.r1c2==1?1:-1).select(row.r1c3==1?2:-1),
            getScale("row2").select(row.r2c1==1?0:-1).select(row.r2c2==1?1:-1).select(row.r2c3==1?2:-1),
            getScale("row3").select(row.r3c1==1?0:-1).select(row.r3c2==1?1:-1).select(row.r3c3==1?2:-1)
            ,
            newTimer(850).start().wait()
            ,
            getScale("row1").unselect(0,"log").unselect(1,"log").unselect(2,"log").remove(),
            getScale("row2").unselect(0,"log").unselect(1,"log").unselect(2,"log").remove(),
            getScale("row3").unselect(0,"log").unselect(1,"log").unselect(2,"log").remove()
            ,
            newText('inst', 'Now produce the pattern').css("margin-bottom","20px").print().center()
            ,
            getScale("row1").print(),getScale("row2").print(),getScale("row3").print()
            ,
            newButton("press")
                .print()
                .wait( 
                    newFunction( 
                        ()=>[...document.querySelectorAll(".PennController-Scale input")].map(c=>Number(c.checked)).join('') 
                    )
                    .test.is([row.r1c1,row.r1c2,row.r1c3,row.r2c1,row.r2c2,row.r2c3,row.r3c1,row.r3c2,row.r3c3].join('') )
                    .failure( newText("Try again!").print() )
                )
                .center()
        )
    )

    And here’s what to put in global_main.css to make the checkboxes appear as white and black boxes:

    .PennController-row1 input, .PennController-row2 input, .PennController-row3 input {
        display: none;
    }
    .PennController-row1 input + label, .PennController-row2 input + label, .PennController-row3 input + label {
        display: inline-block;
        width: 40px;
        height: 40px;
        background-color: white;
        border: solid 1px grey;
    }
    .PennController-row1 input:checked + label, .PennController-row2 input:checked + label, .PennController-row3 input:checked + label {
        background-color: black;
    }

    Jeremy

    in reply to: Problems running experiment & downloading results #9876
    Jeremy
    Keymaster

    Hi,

    I apologize for the issue: I brought some modifications to the server’s configuration yesterday and mistakenly cut communication between the farm and the database

    Things should be back to normal now

    Jeremy

    in reply to: Audio replay only once #9863
    Jeremy
    Keymaster

    Hi,

    My bad, the code should have been

    newTimer("callback",1)
      .callback( getAudio("audio").wait().remove() , getText("replay").remove() )
      .start()

    Jeremy

    • This reply was modified 2 years, 5 months ago by Jeremy. Reason: fix order
    in reply to: Accessing variables and adding new text fields #9860
    Jeremy
    Keymaster

    Hi,

    1) You can not use getVar inside a newX command. Instead, you need to use it inside the Text element’s text command, like this: newText("sentence", "").text( getVar("segmentation") )

    2) The easiest solution would be to create as many TextInput elements that you will allow the user to add, and dynamically add/remove them to/from the page. Here is an example with the possibility of adding up to 5 extra input boxes: https://farm.pcibex.net/r/kZmhKa/

    Jeremy

    in reply to: Displaying texts in columns #9857
    Jeremy
    Keymaster

    Hi Laia,

    Your code actually works pretty well already, the only problem is that the text on the left overflows onto the text on the right. This is because you didn’t explicitly set a width to the text element, so it inherits the width of its container, the Canvas element

    Just give an explicit width to your Text elements and you’ll be good:

    getText(oneFirst?"sentence1":"sentence2").size(300,"auto").print( 20,0,getCanvas("frases")),
    getText(oneFirst?"sentence2":"sentence1").size(300,"auto").print(350,0,getCanvas("frases")),

    Jeremy

    in reply to: Audio replay only once #9854
    Jeremy
    Keymaster

    Hi,

    Remove the Text element in the same callback, and don’t wait for it (you cannot wait for Text elements):

    newTimer("callback",1)
      .callback( getText("replay").remove() , getAudio("audio").wait().remove() )
      .start()

    Jeremy

    in reply to: Presenting self-defined regions in self-paced reading #9853
    Jeremy
    Keymaster

    Hi,

    PennController is a simple extension to IBEX, so you can still do what you used to do. For example, this is still valid:

    items = [
      ["label", "DashedSentence", {s: ["This is", "what", "I", "have", "in mind"]}]
    ];

    However, you can only store strings in a CSV file, you cannot directly store a javascript array (which is what ["This is", "what", "I", "have", "in mind"] is) so to accomplish the same using a string from a CSV file, you need to split it into an array, using a character of your choice to serve as the splitting character. Say you choose to use ~, then you can have This is~what~I~have~in mind in your CSV file, and then do this in your Template command:

    Template( row =>
      newTrial( 
        newController("DashedSentence", {s: row.sentence.split("~")})
            .print()
            .log()
            .wait()
      )
    )

    Jeremy

    in reply to: Troubleshooting #9848
    Jeremy
    Keymaster

    Hi Yev,

    PennController always gives focus to the most recently added TextInput element. If at one point in your trial, you want focus to go to a given TextInput element, you can use the Function element to execute some javascript code during runtime:

    newTrial(
        newTextInput("first").print()
        ,
        newTextInput("second").print()
        ,
        newFunction( ()=>document.querySelector(".PennController-first").focus() ).call()
        ,
        newButton("Next").print().wait()
    )

    Jeremy

    in reply to: Randomizing two different experiments #9847
    Jeremy
    Keymaster

    Hi Nico,

    As long as you have the exact same list of groups in your two tables, you can use a Template command for one table and another command for the other table, and participants will be assigned the same group across tables. So this is how I would do things:

    Template("Mix.csv", row =>
        newTrial("Global",
            // etc.
    
    Template("CT.csv", row =>
        newTrial("Random",
            // etc.
    
    Sequence(
      ...( Math.random() > 0.5 
        ? [ randomize("Global") , randomize("Random") ]
        : [ randomize("Random") , randomize("Global") ]
      )
    )
    

    Jeremy

    in reply to: Randomizing the sentences in a self-paced reading experiment #9846
    Jeremy
    Keymaster

    Hello,

    If you want to use the method in that project, you would just need to repeat the lines from var count=2; to newArray.push(originalArray[i]); using new variable names (eg. count2, newArray2, originalArray2) and in your newTrial command, reference the new newArray variable in place of the sentences followed by a question, just like you’re currently doing with the other ones, eg. newController("DashedSentence", {s : newArray2[0]})

    However you may be interested in creating one trial per sentence(/question) instead of including them all in a single trial. To do so, you would list your sentences and questions in a CSV file and use Template to create as many trial as you have rows in the CSV file. You can conditionally include a question in the trial by looking up the value of a column, as described in this post. Then you can simply use randomize or rshuffle in Sequence to randomize the order in which the sentences(+questions) appear

    Jeremy

    Jeremy
    Keymaster

    Hi Dari,

    The method used by the function in that post is too brute force to efficiently return a sequence every time the experiment is run

    Use this instead:

    function rnmt(toFill,fillFrom,n){
      if (fillFrom.length<1) return toFill;
      let lst = toFill[toFill.length-1], nxt = fillFrom[0];
      if (toFill.length>=n) {
        if (nxt.type == lst.type) {
          let lstN = 0;
          for (let i=toFill.length-1; i>=0 && toFill[i].type==lst.type; i--) lstN += 1;
          if (lstN>=n) {
            for (let i=0; i<fillFrom.length && nxt.type==lst.type; i++) {
              fillFrom = [...fillFrom.slice(1,),fillFrom[0]];
              nxt = fillFrom[0];
            }
            if (nxt.type==lst.type) return false;
          }
        }
      }
      return rnmt([...toFill,nxt],fillFrom.slice(1,),n);
    }
    
    function RandomizeNoMoreThan(predicate,n) {
      this.args = [predicate];
      this.run = function(arrays) {
        let ret = false;
        while (!ret){
          fisherYates(arrays[0]);
          ret = rnmt([],[...arrays[0]],n); 
        }
        return ret
      };
    }
    function randomizeNoMoreThan(predicate, n) {
      return new RandomizeNoMoreThan(predicate,n);
    }

    Jeremy

    in reply to: Get list of Sentences from CSV #9837
    Jeremy
    Keymaster

    Hi,

    You have two options. One is to use one column per sentence, so if you have two sentences you need two columns instead of one for the sentences in your CSV file. Let’s say you name your first column text_1 and the second column text_2, then you would do newController("DashedSentence", {s : [row.text_1, row.text_2]})

    Another option is to use javascript to split the string coming from the table into multiple sub-strings. Assuming you are separating your sentences with the comma character (meaning no comma will ever occur as an actual punctuation in a sentence) then you would proceed like this: newController("DashedSentence", {s : row.text_1.split(',') })

    Jeremy

    in reply to: adding comprehension quesiton to self paced reading task #9836
    Jeremy
    Keymaster

    Hi Doğan,

    Apologies for the late reply. The native Ibex controller called DashedAcceptabilityJudgment readily provides these two options. Here’s an example of a PennController trial using it:

    newTrial(
        newController("DashedAcceptabilityJudgment", {
            s: "While Alex was watching TV, Sue was washing the dishes",
            mode: "self-paced reading",
            q: "Who was watching TV?",
            as: ["Alex", "Sue"]
        })
            .log()
            .print()
            .wait()
    )

    Jeremy

    in reply to: Force DownloadRecordingButton #9813
    Jeremy
    Keymaster

    Hi Emiel,

    If you want to prevent the experiment from sending the results when the recordings fail to upload (even though the two things are separate, and as you found out results can still be saved even though recordings aren’t uploaded) then you can do something like this:

    Sequence(
        "init",
        sepWith("async", rshuffle("letter","picture")),
        "sync",
        "last"
    )
    
    UploadRecordings("sync")
    
    newTrial("last",
        newFunction("check upload", ()=>PennController.UploadRecordingsError)
            .test.is(undefined)
            .failure(
                newText("error", "There was a problem sending the recordings to the server. ")
                    .color("red")
                    .print()
                ,
                newButton().wait()
            )
    )

    The idea is you manually insert a synchronous UploadRecordings in your Sequence and check in the next trial whether PennController.UploadRecordingsError reports an error: if so, then you use newButton().wait() to prevent the trial from completing and proceeding to the end of the experiment (=sending the results)

    Jeremy

Viewing 15 posts - 211 through 225 (of 1,522 total)