PennController for IBEX › Forums › Support › Questionnaire with questions on the same page
- This topic has 22 replies, 5 voices, and was last updated 1 year, 4 months ago by
martaponciano.
-
AuthorPosts
-
May 27, 2020 at 10:06 am #5434
ReaToth
ParticipantHello,
I am trying to design a research which would include a questionnaire, however, I have difficulties displaying all the questions on the same page. I am using scale as a measurement for responses while also using a table to import the resources.
Here is my current code:Template("wbsi.csv", variable => newTrial("WBSI", newText(variable.Number1, variable.Question1) .print() , newScale("Rating", "Strongly Disagree", "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree") .log() .labelsPosition("top") .before( getText(variable.Number1) ) .size("auto") .print() .wait() , newButton("next", "Next") .print() .wait() ))
However, this code displayed the questions one by one on a separate page. I would like to have all the questions displayed simultaneously on the same page with the ‘Next’ button on the bottom of the page.
I have managed to make it work when I am not using a table, but it would make the code extremely long if I were to type in all the questions separately. Here is the code for this:newTrial("WBSI", newText("1", "how do you feel about cheating?") , newScale("Rating", "Strongly Disagree", "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree") .log() .labelsPosition("top") .before( getText("1") ) .size("auto") .print() , newText("2", "how do you feel about skipping class?") , newScale("Rating", "Strongly Disagree", "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree") .log() .labelsPosition("top") .before( getText("2") ) .size("auto") .print() , newButton("next", "Next") .print() .wait() )
Thank you for your help in advance,
AndreaMay 27, 2020 at 10:50 am #5435Jeremy
KeymasterHello Andrea,
Since you basically want to repeat the same series of commands multiple times, you can create a function that takes your number/question as an input and outputs the commands, like this:
question = (number,text) => [ newText("Question-"+number, text) , newScale("Rating-"+number, "Strongly Disagree", "Disagree", "Neither Agree or Disagree", "Agree", "Strongly Agree") .log() .labelsPosition("top") .before( getText("Question-"+number) ) .size("auto") .print() ]
Once this is in place, you can use it like this:
Template("wbsi.csv", variable => newTrial("WBSI", ...question(variable.Number1, variable.Question1) , ...question(variable.Number2, variable.Question2) , ...question(variable.Number3, variable.Question3) , newButton("next", "Next") .print() .wait() ) )
Jeremy
May 28, 2020 at 7:13 am #5465ReaToth
ParticipantHello Jeremy,
Thank you for your quick response. Unfortunately, the code doesn’t seem to work for me. I have try to change it but couldn’t manage to figure it out. I think it has to do with my table. I have 2 columns: ‘Number’ and ‘Question’. The rows of ‘Number’ column goes from 1 to 15 while the rows of ‘Question’ column has the actual questions. In the Debug, the error message comes up: ‘No column named ‘Question3’ found in table wbsi.csv’. And the same for Question2, and Number1 and Number2.
Apologies if the answer is obvious, but I have been trying with different codes since yesterday with no luck.Thank you for your help in advance,
AndreaMay 28, 2020 at 4:17 pm #5468Jeremy
KeymasterHello 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
May 29, 2020 at 5:59 am #5539ReaToth
ParticipantHello,
Thank you for your help, it’s working perfectly now.
Best,
AndreaJune 2, 2020 at 8:12 am #5559daniela
ParticipantHi Jeremy,
I’ve got a similar issue at the moment, in that we want to present a post-experiment memory task in which 50 names are presented as a list. We want to log which names are selected (presumably along a scale with radio buttons), but we also want to log other variables related to each name (from other columns in the .csv. E.g., indicating whether the name was presented in the experiment or not). We’d also like to be able to randomise the presentation of these names.
Our question is basically: is it at all possible to present multiple values of a single column as a list, and to also be able to log whether each value was selected, while also logging variables related to each value (e.g., experimental or decoy item)?
Best,
DanielaJune 2, 2020 at 1:03 pm #5561Jeremy
KeymasterHi 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
June 5, 2020 at 5:14 am #5595Philipp
ParticipantHi Jeremy,
thanks for your reply. Adding on to what you wrote, I have three questions:
1) How would you assign a label to this function such that you can assign it a place in the sequence, i.e. have it appear after the main experiment?
2) Is it possible to include a text element on the same screen, so we can remind participants of what their task is?
3) Could the individual items be arranged into columns instead of one long list?
Thank you in advance!
PhilippJune 5, 2020 at 9:56 am #5597Jeremy
KeymasterHi Philipp,
1) You can always assign a label as the first argument of newTrial, so you could replace the line return newTrial( with return newTrial( "label" ,
2) Yes, you can use newText("my text").print() inside the same newTrial command, either just before ...names.map( r => { or just before newButton("Send")
3) Do you mean rearrange the table? In that case it wouldn’t make sense to use the Template command, as you would only have one row—use the method from this post instead, which is a cleaner way of creating a single trial
Jeremy
June 8, 2020 at 6:07 am #5600daniela
ParticipantHi Jeremy,
thank you for your replies, the
handleNames
function worked great!To clarify Philipp’s third question (we’re working on the same project together): we have 50 names we want presented during this post-experiment task. We would ideally present the names simultaneously in separate columns, rather than in one long list, so that participants don’t need to scroll. I imagine we could do this somehow using
newCanvas
and creating separate functions (e.g.,handleNames1
,handleNames2
, etc.) in whichif (row.name=="__")
could be changed to only access the first 17 names (forhandleNames1
), then the next 17 (handleNames2
), and so on. Would this be the best course of action?We also noticed that the randomisation doesn’t seem to work in Chrome (but does in Chrome mobile, and Firefox). Is there a way to get the randomisation to also work with Chrome?
Thanks again for your help so far!
Best,
DanielaJune 8, 2020 at 11:20 am #5601Jeremy
KeymasterHi Daniela,
Not sure what’s happening with Chrome, but use this to sort names instead:
names = names.sort(v=>1-2*(Math.random()>=0.5));
I don’t think using multiple functions is the way to go: the idea is to handle the multiple lines within a single function, defining multiple of them sort of defeats the purpose. One option would be to revise the content of newTrial like this:
newCanvas("screen", "100vw", "100vh") .add( 0 , 0 , newCanvas("names-1", "30%", "100%") ) .add( "center at 50%" , 0 , newCanvas("names-2", "30%", "100%") ) .add( "right at 100%" , 0 , newCanvas("names-3", "30%", "100%") ) .print( "center at 50vw" , "middle at 50vh" ) , ...names.map( (r,i) => { 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) .log() .print() ) .print( "2em" , parseInt(3+(i%3)*3)+"em" , getCanvas("names-"+parseInt(1+i/3)) ); }) , newButton("Send") .print( "center at 50%" , "bottom at 95%" , getCanvas("screen") ) .wait( test )
Notice the i variable in the map function, that lets us keep track of the index of the name we are parsing. I use it to determine in which of 3 Canvas elements, laid out as columns on a full-page Canvas, I will add the name. I also use it to calculate an appropriate Y coordinate. In this example, I use i%3 because I want columns of 3 names, but of course this is something to adjust to your needs (eg. i%17).
Hope this helps, let me know if you have questions
Jeremy
June 9, 2020 at 5:44 am #5603daniela
ParticipantHi Jeremy,
wow, thank you so much, that’s exactly what we were looking for. You’ve worked your magic yet again!!! Very much appreciated 🙂
Best,
Daniela (and Philipp)June 9, 2020 at 12:19 pm #5606ReaToth
ParticipantHello Jeremy,
Another question came up during my experiment design. I’m using the code you wrote down and it works perfectly. However, how would you make sure the participants selected an option for every question before moving on?
I have used this code, but it moves on as long as the first question is answered.newTrial(“WBSI”,
…wbsiQuestions.map( (v,i) => question(i,v) )
,
newButton(“next”, “Next”)
.print()
.wait(getScale(“Rating”).test.selected())Thank you for your help in advance.
June 9, 2020 at 12:55 pm #5607Jeremy
KeymasterHello Andrea,
You would need to use the same method that I used in this message above:
var test; newTrial("WBSI", ...wbsiQuestions.map( (v,i) => { if (i==0) test = getScale("Rating-"+i).test.selected(); else test = test.and( getScale("Rating-"+i).test.selected() ); return question(i,v); }) , newButton("next", "Next") .print() .wait( test ) )
June 9, 2020 at 1:19 pm #5610ReaToth
ParticipantHello Jeremy,
Thank you for the quick solution. It is working perfectly 🙂 Last question: if I would like to add a message for the participants in case they haven’t answered all the questions, such as: ‘Please make sure you answered every question’, where would I have to insert it?
Thank you,
Andrea -
AuthorPosts
- You must be logged in to reply to this topic.