Jeremy

Forum Replies Created

Viewing 15 posts - 946 through 960 (of 1,522 total)
  • Author
    Posts
  • in reply to: How to assign participants to two sessions #6643
    Jeremy
    Keymaster

    Hi Nan,

    The whole const/switch part of your code is executed at the very beginning of your experiment, at which point your Var element named day has not been set to the participant’s input yet. What’s tricky is that you do need to run Sequence at the very beginning of your experiment, because the engine won’t accept modifications to the sequence after it has started running the first trial.

    You can pass the session number in the URL, and then retrieve it from your script using GetURLParameter. If you’re already using the URL to control which group your participants are assigned, you could have your links end with server.py?withsquare=1&day=1 and do something like this:

    const group = Number(GetURLParameter("withsquare"));
    const day = Number(GetURLParameter("day"));
    
    switch (day){
      case 1:
        switch(group){
          case 1:
            Sequence( "test",  randomize("experiment") , SendResults() , "bye" );    
            break;
          case 2:
          default:
            Sequence( "test2",  randomize("experiment") , SendResults() , "bye" );    
            break;
        }
        break;
      case 2:
      default:
        switch (group) {
          case 1:
            Sequence( "test2",  randomize("experiment") , SendResults() , "bye" );    
            break;
          case 2:
          default:
            Sequence( "test",  randomize("experiment") , SendResults() , "bye" );    
            break;
        }
        break;
    }

    If you need to ask your participant to type an ID and/or a day number, I would actually add a statement to the switch checking that day is not set, and in that case execute Sequence("form")—with a trial labeled "form" which should generate a link with the appropriate parameters in the URL. Here’s an example (assuming participant IDs end with a digit and you assign group based on it being odd vs even):

    newTrial( "form" ,
      newText("Is this your first or your second session?").print()
      ,
      newScale("day", "1st", "2nd")
        .default("1st")
        .labelsPosition("right")
        .print()
      ,
      newText("Please type in your ID below").print()
      ,
      newTextInput("ID").print()
      ,
      newButton("Submit")
        .print()
        .wait( 
            getTextInput("ID").test.text(/\d/).failure( newText("Please type your ID").print() ) 
        )
      ,
      clear()
      ,
      getTextInput("ID").test.text(/[02468]$/)
        .success(
            getScale("day").test.selected("1st")
              .success( newText("<a href='server.py?withsquare=1&day=1'>Start the experiment</a>").print() )
              .failure( newText("<a href='server.py?withsquare=1&day=2'>Start the experiment</a>").print() )
        )
        .failure(
            getScale("day").test.selected("1st")
              .success( newText("<a href='server.py?withsquare=2&day=1'>Start the experiment</a>").print() )
              .failure( newText("<a href='server.py?withsquare=2&day=2'>Start the experiment</a>").print() )
        )
      ,
      newButton().wait() // Wait until a click on the link refreshes the page
    )

    Let me know if you have questions

    Jeremy

    in reply to: Some final issues before the experiment #6638
    Jeremy
    Keymaster

    Hello Danil,

    1. I’m a little confused: you’re printing the sentence both as part of your newText element and as part of your HTML document. Why would you not expect it to appear twice, then?

    2. How do you upload the recordings? Do you insert asynchronous UploadedRecordings trials regularly? You can make them synchronous if you want to make sure that the experiment does not proceed to the next trial until the recordings have been uploaded (although your participants might have to wait a while after each trial)
    I’m not sure why you wouldn’t get a line for the last zip file, but it could be that the specific way your experiment is coded makes it send results to the CSV file before it can run the last UploadedRecordings trial

    3. Not sure what is happening with the resource preloading, it will in part depend on your specific setup. As the number of files your experiment needs to retrieve increases, it becomes a good idea to group them into ZIP files. Some servers do not like the same IP sending too many requests in a short period of time, which could be one explanation for your experience
    Regarding group/list, the default behavior is to increase the internal counter only after (and whenever) receiving a full submission. Use SetCounter if you want to increase the counter earlier in your experiment

    4. Unfortunately we never got around to sorting out your experiment editing problem. May I suggest you try out the new farm where, hopefully, you should no longer experience the same issue

    Jeremy

    in reply to: Having trouble with uploading .csv files #6636
    Jeremy
    Keymaster

    Dear Ana,

    You have these two lines in your code (PennController. prefix removed):

    Template( GetTable("master_spr.csv")
                             .filter("type" , ("critical", "filler"))

    The filter command can only take one string or one regular expression as its second argument. In your case, you could use the regular expression /^(critical|filler)$/

    Note that grouping a pair of strings in parentheses the way you did is not standard JavaScript syntax. I was surprised that that bit of code did not throw an error, and I had to do some testing to understand how JavaScript processes those parenthesis entities. It seems that it systematically returns the last member only, so in your case it would simply return "filler", which would explain why you end up with 80 items (there are exactly 80 fillers in your table).

    Jeremy

    in reply to: Having trouble with uploading .csv files #6634
    Jeremy
    Keymaster

    Hello Ana,

    Double-check the labels you reference in your Sequence command and the ones you assign your trials in your Template command. Feel free to send me the link to your experiment, either here or at support@pcibex.net, if you’d like me to have a look

    Jeremy

    in reply to: preloading audio files #6632
    Jeremy
    Keymaster

    Yes, if your design makes it possible, and if you don’t mind having your participants possibly wait before each block (although that becomes less likely later in the experiment, since there will have been more time for resources to preload). Just make sure to pass the right label(s) to CheckPreloaded, as illustrated on the documentation page

    Jeremy

    in reply to: Randomize DashedSentence #6629
    Jeremy
    Keymaster

    Hi,

    Yes, it is possible, but the proper syntax is:

    Template(row => [
      "trials",
      "PennController", newTrial("trial-begin", newAudio("trial-begin", "trial-begin.mp3").play().wait() ),
      "DashedSentence", {s: row.sentence},
      // ... 
    ])

    Also, in case you missed it, you can inject native-Ibex controllers into PennController trials since version 1.7

    Jeremy

    in reply to: preloading audio files #6627
    Jeremy
    Keymaster

    Hi,

    200 audio files is a lot, so make sure you do need all of them. For example, if you have 10 different groups of participants and each group needs only 20 audio files, take a look at this topic.

    In any case, you should probably consolidate your audio files into ZIP files, if that’s not already the case. There’s a tradeoff in deciding between a single big zip file vs a few lighter zip files: one big zip file ensures that all the audio files are received once download has completed, but it takes time, which also leaves a longer time-window for download interruption to happen; several zip files could be downloaded in parallel, but it means multiple requests, so the likelihood of at least one failing to go through is of course higher.

    Whatever you end up doing, you need to use CheckPreloaded to control when to check that the resources have preloaded. So if you want your experiment to proceed only after all your resources have preloaded, insert a global CheckPreloaded trial at the desired point in your Sequence. You could give a ridiculously high number as a delay if you want to really ensure that all your resources have preloaded before moving on, but your participants probably will not like that, that’s why by default CheckPreloaded still moves on after a delay regardless. What I suggest, if applicable, is to insert different CheckPreloaded trials before different blocks of trials, so as to minimize waiting time while still ensuring that all the resources for the next block of trials have preloaded before starting that block.

    The documentation gives one example on how to do that, but let me know if you have questions

    Jeremy

    in reply to: Randomly get item from certain conditions #6624
    Jeremy
    Keymaster

    Hello KJ,

    The rshuffle function will always exhaust the trials whose labels are referenced. If you want to show different subsets of items to different participants, you could consider using a table for a Group/List design

    Jeremy

    in reply to: Conditionals in self=paced reading #6623
    Jeremy
    Keymaster

    Hello,

    The particular case you are describing is easy enough to implement:

    newTrial(
        newController("DashedSentence", {s: "This is an example sentence"})
            .print()
            .log()
            .wait()
            .remove()
        ,
        newText("Do you want to read the sentence again?")
            .print()
        ,
        newButton("Yes")
            .after( newButton("No").callback(end()) )
            .print()
            .wait()
        ,
        clear()
        ,
        newController("DashedSentence", {s: "This is an example sentence"})
            .print()
            .log()
            .wait()
            .remove()
    )

    Note that this only gives the option to read the sentence again once. Giving the option to re-read it as many times as the participant wants would be a little more complicated

    Jeremy

    in reply to: moving to the new farm – preload issue #6617
    Jeremy
    Keymaster

    If you took your experiment in non-incognito mode before you set the right CORS configuration, your browser might have remembered that the requests failed and not bothered sending them again. Clearing the cache made it send new requests, which were successful now that you have updated the CORS settings

    Jeremy

    in reply to: Preloading resources by experiment list #6613
    Jeremy
    Keymaster

    My bad, I tested my code at https://farm.pcibex.net/, where __counter_value_from_server__ is accessible from the start, which is not the case at https://expt.pcibex.net.

    If you’re running your study on the expt Farm (or on the original Ibex Farm for that matter) you’ll need a little trick to detect when __counter_value_from_server__ is being set:

    let myOwnCounter = 0, preloadZipSent = false;
    Object.defineProperty(window, '__counter_value_from_server__', {
        set(v){
            myOwnCounter = v;
            if (preloadZipSent) return;
            preloadZipSent = true;
            switch (v % 4) {
              case 0:
                PreloadZip("https://my.server/path/to/zip_a.zip");
                break;
              case 1:
                PreloadZip("https://my.server/path/to/zip_b.zip");
                break;
              case 2:
                PreloadZip("https://my.server/path/to/zip_c.zip")
                break;
              default:
              case 3:
                PreloadZip("https://my.server/path/to/zip_d.zip")
                break;
            }
        }, 
        get(){
            return myOwnCounter;
        }
    });

    Jeremy

    in reply to: Preloading resources by experiment list #6611
    Jeremy
    Keymaster

    Hi Emily,

    The switch statement from my previous message lets you execute different PreloadZip commands depending of the value of __counter_value_from_server__ (which goes to show that it is available from outside Template). This is a rather specific need

    The Template function readily implements group designs by looking up a Group/List column in your table and selecting a subset of rows that share a single value for that column, so you don’t need to manually check __counter_value_from_server__. Since you should have the option of specifying the name of the audio file you want to play directly in a column in your table, I am not sure why you would need to access __counter_value_from_server__ from within the Template command

    Jeremy

    in reply to: moving to the new farm – preload issue #6609
    Jeremy
    Keymaster

    Dear Peiyao,

    Did you need to set some CORS settings with your AWS S3 bucket? If you allowed the origin https://expt.pcibex.net, then you should now also allow the origin https://farm.pcibex.net

    Jeremy

    in reply to: Saving Recordings with Appropriate Filename #6605
    Jeremy
    Keymaster

    Hello Danil,

    The audio files are named after the MediaRecorder element you created to record them. So if you have newMediaRecorder("recorder","audio") then your files will be named something like recorder.webm. If you have more than one file recorded via a MediaRecorder element called recorder then PennController will automatically append a number to generate unique names, for example recorder-48.webm. Note that the extension in your case is webm and not wav, because no browser natively supports WAV as a format to save audio streams recorded via the MediaRecorder API.

    In order to name the files as you describe, you need to do something like

    newMediaRecorder([variable.Type,variable.ConditionLabel,variable.BaseFormOfTargetNoun,variable.Numeral,variable.Group].join('_'), "audio")

    Of course this is a big long, so I’d suggest you add a column to your table in which you generate the filename you want to use for the recording file for each row. Say you name that column RecordingFile, then you can simply do:

    newMediaRecorder(variable.RecordingFile, "audio")

    which makes it easier to refer back to your element later, for example:

    getMediarecorder(variable.RecordingFile).stop()

    Note that I didn’t include a participant ID in those filenames, because the strings you mention are MD5 hashes generated at the end of the experiment. There is no easy way to add a unique participant ID to the filenames, unless if you pass it as part of your experiment’s URL, in which case you can use GetURLParameter to retrieve it, and you can do things like:

    newMediaRecorder(variable.RecordingFile+'_'+GetURLParameter("id"), "audio")

    and

    getMediarecorder(variable.RecordingFile+'_'+GetURLParameter("id")).stop()

    Let me know if you have any questions

    Jeremy

    in reply to: InitiateRecorder #6599
    Jeremy
    Keymaster

    Hi Giorgio,

    Honestly I’m surprised your code worked anywhere: as explained in the documentation, InitiateRecorder creates a new trial, it shouldn’t be inserted inside a trial.

    Try this instead:

    Header(
        defaultText.css("font-size","1.5vw")
        ,
        defaultTextInput.css("font-size", "1.5vw")
        ,
        defaultButton.css("font-size","1.5vw")
    )
    
    newTrial("mic",
        newVar("participantsName").global()
        ,
        newText("nombre", "\u00BF Cu\u00E1l es tu nombre o cu\u00E1les son las iniciales de tu nombre y apellido?")
            .print("center at 50%", "middle at 50%")
        ,
        newText("Pulsa Enter")
            .print("center at 50%", "middle at 85%")
        ,
        newTextInput("name")
            .once()
            .print("center at 50%", "middle at 70%")
            .wait()
            .setVar( "participantsName" )
    )
    .log("Participant", getVar("participantsName"))
    
    InitiateRecorder("https://pcibex.bcbl.eu/hablacon/mediarecorder.php").label("mic")
    
    newTrial("mic",
        newText("initials")
            .before( newText("Muy bien ") )
            .text( getVar("participantsName") )
            .after( newText(", ahora entramos en modo de pantalla completa") )
            .print()
        ,
        newButton("Vale")
            .print("center at 50%", "middle at 50%")
            .wait(),
            
        fullscreen()
        ,
        clear()
        ,
        newText("mm","Ahora di algo y escúchalo para verificar que el micrófono es compatible con el programa. ")
            .print()
        ,
        newMediaRecorder("audio")
            .css({'font-size':'1.3vw'})
            .print()
            .wait()
            .play()
            .wait("playback")
        ,
        newText("s", "¿Se escucha bien?").print()
        ,
        newButton("yes","SI")
            .print("center at 50%", "middle at 40%")
            .wait()
    )

    NB: I use defaultElement commands in the Header to play with the default font sizes, but you should really use a CSS file instead.

    Jeremy

Viewing 15 posts - 946 through 960 (of 1,522 total)