Forum Replies Created
-
AuthorPosts
-
Jeremy
KeymasterHi,
I don’t immediately see a problem with your code. Would you be able to share a link to your experiment with me (even if it’s not on the PCIbex Farm), here or at support@pcibex.net so I can take a closer look?
Jeremy
Jeremy
KeymasterHi Juliana,
Unfortunately the Question controller was written in a way that it reports the content of the selected answer, not its index. However, since you are logging the content of both questions, you can compare the content of the answer to those columns and determine the index of the chosen answer, which you can then compare to the expected index. For example, here is a line from a test-run of mine, with a header line with column names before it for clarity:
ReceptionTime,MD5,Controller,Item,Element,Label,Group,PennElementType,PennElementName,Parameter,Value,EventTime,Frase1,Frase2,Condition,Item,Group,Expected,Correct,Time,Comments 752,REDACTED,PennController,11,0,item,NULL,Controller-Question,Question,NULL,Ou hoje ou amanhã fomos à praia.,1684831742806,Ou hoje ou amanhã iremos à praia.,Ou hoje ou amanhã fomos à praia.,TREINO,1,U,1,NULL,1967
In R for example, and assuming
results
only contain rows for that Question controller, I would do something like this:results$AnswerIndex <- 1 + (results$Value == results$Frase2) results$Accurate <- results$AnswerIndex == results$Expected
Note, however, that the Question controller already comes with a
hasCorrect
option, so you could donewController("Question", {q: "", as: [variable.Frase1, variable.Frase2], hasCorrect: parseInt(variable.Expected)-1})
. Then you'd have a 1 reported for correct answers and a 0 for incorrect answersJeremy
Jeremy
KeymasterHi,
You have two problematic file references in your table:
- 34c_schoolbuls.mp3
- 36c_tableware.mp3
The former contains a typo (which I highlighted in boldface) the latter is absent from the zip file
Jeremy
May 19, 2023 at 12:21 pm in reply to: Randomize blocks and trials but keep breaks in the same order #10603Jeremy
KeymasterIn that case use two
randomize
s inseq
, like this:var blocks = [ seq(randomize("trials_study"),randomize("trials_test")), // ...
Jeremy
May 19, 2023 at 11:03 am in reply to: Randomize blocks and trials but keep breaks in the same order #10601Jeremy
KeymasterHi Yev,
If your first block consists of all the trials labeled “trials_study” and those named “trials_test” and you want them randomly (unevenly) shuffled, you could replace
randomize("block_1")
from the code above withrandomize(anyOf("trials_study","trials_test"))
. If you’d rather have them evenly shuffled, usershuffle("trials_study","trials_test")
Jeremy
Jeremy
KeymasterHello Lorrainy,
The error messages are correct: your table contains no columns named “Pergunta” or “Frase”. They do, however, contain two columns named “pergunta” and “frase”
That being said, your table uses
;
as a separator, which is not currently supported by PennController. Replace them with,
(commas) and, along with the two edits I just hinted at, your experiment will run smoothlyJeremy
Jeremy
KeymasterHello,
You’re using
AddHost
in place ofPreloadZip
Also delete the trailing slash after
.zip
in the zip file’s URLJeremy
Jeremy
KeymasterYou could test the value of the Var element right after you increment it in the
callback
to see if the updated index goes beyond the number of words, and if so, wait for a key press on K to stop the timer:newKey("NEXT", " ") .callback( getVar("nextWordStartsAt").test.is(v=>v<row.Words.split('_').length).success( getVar("listOfWords") .set(getVar("nextWordStartsAt")) .set(v=>row.Words.split('_').slice(v,v+10).join("<br>")) , getText("words").text( getVar("listOfWords") ) , getVar("nextWordStartsAt") .set(v=>v+10) // update the index .test.is(v=>v>=row.Words.split('_').length).success( // if the index is greater than the # of words newKey("K").wait() // wait for a key press on K , getTimer("recording").stop() // stop the timer ) ) )
Jeremy
Jeremy
KeymasterHi,
Just add
.failure( getTimer("recording").stop() )
to the test on the index for the next 10 words:newKey("NEXT", " ") .callback( getVar("nextWordStartsAt").test.is(v=>v<row.Words.split('_').length).success( getVar("listOfWords") .set(getVar("nextWordStartsAt")) .set(v=>row.Words.split('_').slice(v,v+10).join("<br>")) , getText("words").text( getVar("listOfWords") ) , getVar("nextWordStartsAt").set(v=>v+10) ) // stop the timer if all the words have been exhausted .failure( getTimer("recording").stop() )
Jeremy
Jeremy
KeymasterHello Shayne,
My bad, the command is
.consent
, not.setConsent
Jeremy
Jeremy
KeymasterHi Hailin,
You could use the custom
pick
function. Assuming all your critical items have labels that start withexp.
and all your filler items have labels that start withfil.
it would look something like this:var shuffleSequence = seq( shuffle( pick( randomize(startsWith("exp.")) , 10), pick( randomize(startsWith("fil.")) , 30) ) )
Jeremy
Jeremy
KeymasterHi,
In that case what I would do is list all the words in a single cell (named “Words” for example) and use Var elements to dynamically update the content of the Text element upon pressing the key:
Template("new--Items_Wide.csv", row => newTrial("listOfWords", fullscreen() , newMediaRecorder("recorder", "audio").log().record() , newTimer("recording", 3000).log().start() , newText("words", row.Words.split('_').slice(0,10).join("<br>")) // use slice to get the 10 first words .cssContainer({ padding: '0.5em', 'text-align': 'center', "justify-content": 'center', "align-items": 'center' , 'font-size': '50px', }) .print() , newVar("listOfWords", ""), // we'll set this Var element to the next list of words newVar("nextWordStartsAt").set(10) // this is the index where the next 10 words start , newKey("NEXT", " ") .callback( // first make sure that the index is within bounds getVar("nextWordStartsAt").test.is(v=>v<row.Words.split('_').length).success( // set listOfWords to the next words getVar("listOfWords") .set(getVar("nextWordStartsAt")) // first look up the index .set(v=>row.Words.split('_').slice(v,v+10).join("<br>")) // then use the index+slice to get the next 10 words , getText("words").text( getVar("listOfWords") ) // update the content of the Text element , getVar("nextWordStartsAt").set(v=>v+10) // the next list of 10 words starts 10 words further ) ) , getTimer("recording").wait() , getMediaRecorder("recorder").stop() , getText("words").remove() , newText('time-is-up-msg', "Time is up!").print() , newButton('btn--finish--game', 'Finish Game').print().wait() ) )
Jeremy
Jeremy
Keymaster[ EDIT : changed
setConsent
forconsent
]Hi,
You can use
.consent
onInitiateRecorder()
to change the consent message:InitiateRecorder("https://myserver/uploadVoices/saveRecordings.php" , "<b><center>RESEARCHER, click on the button below to <b style=color:red;>authorize</b> audio recording in the browser 👇</b></center>" ).label("first-trial") .consent("I Consent")
Regarding the error message, you could use the hack described in this post to replace the message
Jeremy
-
This reply was modified 2 years, 1 month ago by
Jeremy. Reason: changed setConsent for consent
Jeremy
KeymasterHi,
You cannot change the error message, as it is hard-coded in the MediaRecorder element. What you are doing is create a new trial, and printing a Text element in that trial depending on whether the upload succeeded or failed (note that the trial needs to come after the upload attempt for
uploadRecordingsError
to be set)You can change/set the consent text in the
InitiateRecorder
command, but you cannot omit that trial, because it is when the browser requests access to the recording device: without it it would simply be impossible to record anythingJeremy
Jeremy
KeymasterHello,
Template
scans the table and produces one output per row. So the idea is, you useTemplate( row => newTrial
to generate one trial per row. Listing one word per row is not the ideal way to go if you want to display several of them at the same time on the page, ie. during the same trialWhat you could do is store one list of words per cell, separated with a character of your choice, say
.
. Here is what it would look like:Template("items.csv", row => newTrial("listOfWords", newMediaRecorder("recorder").log().record() , newTimer("recording", 3000).log().start() // Start the timer now , newText("words", row.words1.split('.').join("<br>")) // separate each word with a <br> tag (new line) .cssContainer({ "border-radius": '25px', border: '2px solid #73AD21', padding: '0.5em' // add some to not get too close to the border }) .print() , newButton("NEXT", "Next words!") .callback( // use a callback to execute this in parallel to waiting for the timer getButton("NEXT").remove() // remove the NEXT button , getText("words").text( row.words2.split('.').join("<br>") ) // update the displayed list of words ) .print() // do not wait for a click, otherwise the timer might elapse before the click , getTimer("recording").wait() // wait for the timer to end , getMediaRecorder("recorder").stop() , getButton("NEXT").remove() // make sure to remove the button, in case the participant didn't get to the second list , getText("words").remove() , newText('a', "Time is up!").print() , newButton('btn--finish--game', 'Finish Game').print().wait() ) )
Example items.csv:
words1,words2 hello.world,bye.earth ibex.is.awesome,pcibex.rocks
Jeremy
-
AuthorPosts