Route Section
Table of contents
- Introduction
- ASK to display a question
- Conditional routing
- Computation/calculations
- Messages, Warnings & Checks
- Canvas
- Operations with lists
- Randomise/Rotate list of choices and question order
- Playing audio and video files
- Printing individual responses to a pdf file
Introduction
The ROUTE section can be used to define:
- the order in which questions are asked
- conditions under which questions asked.
- display options
- checks
- computations
Actions in the ROUTE section are defined using keywords and functions some of which are used in the examples that follow. See list of dsc keywords and functions for descriptions of the keywords and functions that can be used.
The route section begins with the ROUTE reserved word in the dsc file.
Example - ask question q1 then ask question q2 if the answer to q1 is yes
ROUTE
NEWPAGE()
ASK(Q1)
ENDPAGE()
IF (Q1 IN {yes}) THEN {
NEWPAGE()
ASK(Q2)
ENDPAGE()
}
ASK To display a question
NEWPAGE()
ASK(Q1)
ENDPAGE()
The ASK causes the question to be displayed. NEWPAGE() and ENDPAGE() delimit the contents of the page. One or more questions can be displayed on each page.
ASK Parameters
Parameters can be added to the ASK to modify the display of the question. Some examples are shown below.
Adding text
Text can be added to the left or right of the entry box.
txtr adds text to the right of the response box:
ASK(Q1,"txtr=@NS$years")
[ ] years
txtl adds text to the left of the response box.
ASK(Q1,"txtl=You are@NS$")
You are [ ]
@NS$ in the above examples inserts a space to improve the display.
Note: Question attributes in the question definition should be preferred
Styles for response categories
ASK(Q4,'classrows=even:odd')
This will modify the display of odd and even rows using the styles defined for the class classrows in the css file.
Shuffle rows and columns of questions
ASK(Q6,'shufflechoices=Q6')
ASK(Q7,'shuffleitems=Q7')
To randomise the order of display of the rows of question Q6 and the columns of Q7 where Q7 is a grid question.
It can also randomise the order of choices or items within each family. By default, the order of the families is also shuffled. This can be overridden by setting the "shufflechoicefamilies" or "shuffleitem[0-9]families" parameters to "no", "false" or 0. Existing behaviour of the "anchor" attribute is kept - i.e. modalities or families are shuffled in blocks between the anchored elements.
Example
DEFINITIONS
places = {
[austria:'Austria']
1 "Igls",
2 "Mayrhofen",
3 "Ischgl",
4 "Kirchberg",
5 "Kitzbuhel",
6 "Lermoos",
7 "Niederau",
8 "Obergurgl",
9 "Saalbach",
10 "Soll",
11 "Zell Am See" @[anchor='yes'],
|[france:'France']
12 "Val D'Isere",
13 "Les Deux Alpes",
14 "Tignes",
|[italy:'Italy']
15 "Cervinia",
16 "Courmayeur",
|[other:'Other'] @[anchor='yes']
99 "Other"
}
QUESTIONS
resortd "Can you tell me which resort you went to on your 1990/91 ski holiday?" : places{*}
resortt[places] "How many times have you been to each resort?" : INTEGER
ROUTE
NEWPAGE()
ASK(resortd,'shufflechoices=yes,showchoicetags=yes,shufflechoicefamilies=no')
ASK(resortt,'shuffleitems=yes,shuffleitemfamilies=no,showitemnames=yes')
ENDPAGE()
Transpose a question
ASK(Q7,'transpose=yes')
Assuming Q7 is a grid question then the items usually displayed as rows will be displayed as columns.
Asking individual elements of array questions
Array questions defined with one dimension are often displayed as grids with the array elements used as the rows of the grid.
It is also possible to ASK the question with each array element asked individually.
Example to ask the question for the 4th element in the array question Qgrid:
ASK(Qgrid[4])
The example below involves a 2 dimensional question. It will display 4 pages, one for each fruit. Each page will be a grid with days of the week as rows and yes/no as columns.
DEFINITIONS
lst_fruits = { 1 "Apples",2 "Oranges",3 "Bananas",4 "Grapes"}
lst_Days = {1 "Mon",2 "Tue",3 "Wed",4 "Thu",5 "Fri",6 "Sat",7 "Sun"}
VARIABLES
i_fruit : lst_fruits
QUESTIONS
ftoday[lst_fruits][lst_Days] "Please record whether you ate @ITEXT$ on each day?" : {yes,no}
ROUTE
FOREACH i_fruit IN lst_fruits DO {
NEWPAGE()
ASK(ftoday[i_fruit][*]) //[*] to indicate that all days should be displayed
ENDPAGE()
}
Note: i_fruit in the FOREACH command above will loop through the four fruits and @ITEXT$ in the question text for ftoday will be substituted by the text for each fruit.
First of the four questions displayed:
Conditional Routing
Conditions are logical expressions that can be used to control when questions are asked and to program other computations in the ROUTE section.
Example 1: Only ask Q3 if the answer to Q2 is 'Yes'.
Q2 "Do you have children?" {
Y "Yes",
N "No"
} REQUIRED
Q3 "How many children do you have ?" : 1..20 REQUIRED
ROUTE
NEWPAGE()
ASK(Q2)
ENDPAGE()
IF(Q2 IN {Y}) THEN {
NEWPAGE()
ASK(Q3)
ENDPAGE()
}
Example 2: To screen out an individual. The interview stops for anybody aged less than 18 years of age. (txtr=@NS$years displays the text "years" next to the entry box).
Q1 "How old are you?" : 0..99 REQUIRED
ROUTE
NEWPAGE()
ASK(Q1,"txtr=@NS$years")
ENDPAGE()
IF(Q1<18) THEN STOP
Example 3: Routing within a page
Note that in Example 1 above the routing condition (IF(Q2 IN {Y}) THEN {) is outside NEWPAGE()/ENDPAGE() which bracket questions to be displayed on the same page. It is possible to control which questions are displayed within a page. In the following example the page will first display only cr1 and then either cr2 or cr3 will be displayed depending on the answer to cr1.
cr1 "Did you attend last week?" : {yes,no}
cr2 "How useful did you find it?" : {
very "very useful",
mod "moderately useful",
not "not useful"
} : 'if=cr1{yes}'
cr3 "Why did you not attend?" : OPENTEXT : 'if=cr1{no}'
ROUTE
NEWPAGE()
ASK(cr1)
ASK(cr2)
ASK(cr3)
ENDPAGE()
A list of conditions can be defined and a question asked if ANY of those conditions is true. For example:
HowMany "How many?" : 1..10 : 'if=qa{1}:qb{1}:qc{1}'
Operators
The operators listed below can be used to define conditions for subsequent actions to be performed. For example whether a question should be asked.
== | equal to |
<> | not equal to |
IN | included/contains |
> < | greater than, less than |
>= <= | greater than or equal, less than or equal |
AND && | logical AND |
OR || | logical OR |
NOT | logical NOT |
=~ | contains |
!~ | does not contain |
= | assignment |
Computations and calculations
Complex programming and computations can be done in the scripting language.
Program language features include:
- IF - test if a condition is true. click here for syntax
- ELSE - used in conjunction with IF. click here for syntax
- FOR - loop through integer values click here for syntax
- FOREACH - loop through elements of a list click here for syntax
The following arithmetic operators can be used in computations:
+ | plus |
- | minus |
* | multiplication |
/ | division |
% | modulo |
^ | exponent |
A number of functions are also available to perform useful computations or other tasks. For example the SETQTXT function can be used to change the text of a question.
Messages, Warnings & Checks
There are two types of message :
- Information "I am now going to ask you some questions about your job"
- Warnings "Please fill in the 'other' response."
Information messages
NEWPAGE()
MESSAGE("We now will show you a series of products. Please look at them carefully.")
ENDPAGE()
Warnings and Checks
Checks can be added to ensure the respondent has entered complete and/or consistent responses. A CHECK usually involves a condition and a warning message. If the condition is not satisfied the warning message is displayed and the respondent cannot move on to the following page until the error is corrected.
Example
NEWPAGE()
ASK(Q1)
ENDPAGE()
NEWPAGE()
ASK(Q2)
CHECK : {
IF(Q1 IN {Yes} AND Q2 IN {None}) THEN WARNING(Q2,"You selected Yes at the previous question so cannot select None at this question")
}
ENDPAGE()
Canvas
The canvas (table) provides a way of referring to and operating on the different elements of question responses (the response labels and data entry/selection buttons).
The elements of question responses can be treated as if laid out in a table and the elements referred to using the row and column numbers of that table.
Amongst other things the canvas can be used to:
- move rows or columns
- prevent rows and/or columns from being displayed
- to move and/or copy table cells
- set styles for particular elements
To make use of the canvas, declare a canvas variable in the variables section and add BEGINCANVAS() and ENDCANVAS() around the ASK as shown below. Then add the actions to be performed on the CANVAS (skiprows in the example below) and use DISPLAYCANVAS() to display the result.
VARIABLES
c : CANVAS
ROUTE
NEWPAGE()
BEGINCANVAS(c)
ASK(Q1)
ENDCANVAS(c)
SETBLOCK(c:11,0,11,1,'skiprow=yes')
DISPLAYCANVAS(c)
ENDPAGE()
Canvas operations can be defined to act on a single cell or blocks of cells. The cells to which the action should apply can be specified as follows:
SETBLOCK(canvas_name:row number of first cell, column number of first cell, row number of last cell, column number of last cell).
The row and column indexes start at 0
Example :
cell_00 | cell_01 | cell_02 | cell_03 |
cell_10 | cell_11 | cell_12 | cell_13 |
cell_20 | cell_21 | cell_22 | cell_23 |
To select the first row: SETBLOCK(c:0,0,0,3)
To select the two middle cells: SETBLOCK(c:1,1,1,2)
To select the last column: SETBLOCK(c:0,3,2,3)
Note that if asking a grid variable then the first row will contain the column headers. If multiple questions are asked on the same page then canvas treats the whole page as single table. So, for example, the second of 2 questions could be moved to be displayed alongside the first question.
The line below can be included in the project .ini file as an aid to developing and checking canvas operations.
%DEBUGCANVAS%=1
This will display canvas coordinates when the cursor hovers over a cell.
Once selected cells can be randomised, hidden or have styles and classes changed using canvas parameters.
Examples:
SETBLOCK(c:0,2,25,2,'skipcol=yes') - do not display column 2
SETBLOCK(c:11,0,11,1,'skiprow=yes') - do not display row 11
List of CANVAS parameters:
- 'class=even' assign a css class
- 'classrows=even:odd' set different classes for even and odd row. css can then be used to set different styles.
- 'copy=yes' copy an element and not move
- 'disabled=yes' do not allow contents of selected cells to be changed
- 'display=none' do not display selected cells
- 'id=form1' assign an id to an element
- 'merge=@NS$' merge (usually to merge 2 columns into 1)
- 'inscols=yes' insert a new column before the selected column
- 'insrows=yes' insert a new row before the selected row
- 'shufflecols=yes' columns specified will be displayed in random order for the rows specified.
- 'shufflefullcols=yes' column randomisation for all rows
- 'shufflerows=yes' Row randomisation is done on the columns specified.
- 'shufflefullrows=yes' randomise row order of all columns
- 'skipcol=yes' to prevent display of column 5 of the canvas table
- 'skiprow=yes' to prevent display of row 4 of the canvas table
When using SETBLOCK() for a canvas all the parameters which are not known canvas parameters are interpreted as css style instructions. For example, the following code emboldens and right aligns the selected cells:
SETBLOCK(c:1,0,10,3,'font-weight=bold,text-align=right')
Example: to insert row header text:
SETBLOCK(c:6,0,6,1,'insrows=yes')
SETBLOCK(c:6,1,6,1,"Group 3")
SETBLOCK(c:3,0,3,1,'insrows=yes')
SETBLOCK(c:3,1,3,1,"Group 2")
SETBLOCK(c:0,0,0,1,'insrows=yes')
SETBLOCK(c:0,1,0,1,"Group 1")
Here the inserts are done from the bottom upwards so that row references above the insert remain the same.
Blocks of cells can be copied using the COPYBLOCK command. For example to move row 1 to row 10:
COPYBLOCK(c:1,0,1,0,c:10,0,10,0)
'copy=yes' can be added to copy cells and leave the original cells unchanged.
SETCANVAS can be used to defines the cells that will be displayed.
For example to display the 9 cells in rows 0-2 and columns 3-5
SETCANVAS(c:0,3,2,5)
Operations with lists
Many surveys display a list (e.g. brands, services, devices, appliances) to respondents and then display the list or a subset of the list in subsequent questions.
Functions SETQLIST and SETQARRAY can be used to control which subsets of lists are displayed.
https://scrolldemo.sda-ltd.com/testing/Demos/UsingLists is a simple questionnaire demonstrating the use of lists.
Lists are defined in the DEFINITIONS section.
DEFINITIONS
lst_brands = {
1 "Brand 1",
2 "Brand 2",
3 "Brand 3",
4 "Brand 4",
5 "Brand 5",
6 "Brand 6",
None "None of these"
}
The list can then be used to define the response categories for a questions.
QUESTIONS
Q1 "Which brand do you prefer?" : lst_brands
Q2 "Any other brands used?" : lst_brands {*}
List operators and functions
List operators can be used to control which items in a list or lists are displayed at a question. The operators available to build lists are shown below. The examples that follow show how these can be used in practice.
| | set union of lists | {a,b,c} | {b,d} = {a,b,c,d} |
& | intersection of lists | {a,b,c} & {b,d} = {b) |
\ | set difference between lists | {a,b,c} \ {b,d} = {a,c} |
SHUFFLE | randomise list | SHUFFLE({a,b,c}) = {b,a,c} for example |
ROTATE | rotate list | ROTATE({a,b,c,d,e}) = {c,d,e,a,b} for example |
REPLACE | replace item in a list with another list | REPLACE(list1,list2,list3) |
? | element number | lst_items?3 for 3rd item in list lst_items |
SUBLIST | returns part of a list | sublist=SUBLIST(lst_items,1,2) for the first 2 items in list lst_items |
Examples : list operators for list of choices questions
The examples below show how list operators and the SETQLIST command can be used to control which choices in a list are displayed at each question.
The examples are based on questions all defined with lst_brands for the response categories.
First declare variables used in the examples below
VARIABLES
i_brands "To be used for loops" : lst_brands
just_brands "To define brand list excluding none" : lst_brands
select_brands "To be used when creating selection lists" : lst_brands
i : INTEGER
c : CANVAS
q1: Brand list with 'None of these' not displayed. (The CHECK is in this example computes the response selected into the next question and is not related to SETQLIST)
SETQLIST(Q1,{1,2,3,4,5,6})
ASK(Q1)
CHECK: {
IF NOT(Q2 IN Q1) THEN Q2=Q1
}
Note: The same filter on Q1 could be done with the two examples below.
SETQLIST(Q1,lst_brands \ {None})
just_brands = lst_brands \ {None}
SETQLIST(Q1,just_brands)
Q2: Prefilled with response from Q1. CANVAS used to prevent respondent from unchecking the code that has been copied in from Q1.
BEGINCANVAS(c)
ASK(Q2)
ENDCANVAS(c)
i = 0
FOREACH i_brands IN lst_brands DO {
IF (Q1 IN i_brands) THEN SETBLOCK(c:i,0,i,1,'disabled=yes')
i += 1
}
DISPLAYCANVAS(c)
Q3: Only display brands selected at Q2
SETQLIST(Q3,Q2)
ASK(Q3)
Q4: Display brands NOT selected at Q2
SETQLIST(Q4,(lst_brands \ Q2))
ASK(Q4)
Q5: Brands selected at either Q3 OR Q4
SETQLIST(Q5,(Q3 | Q4))
ASK(Q5)
Q6: Brands selected at either Q3 OR Q4 with brand order preserved
SETQLIST(Q6,(lst_brands & (Q3 | Q4)))
ASK(Q6)
Q7: Brands selected at both Q5 AND Q6. With condition to ensure at least one brand to display
select_brands = (lst_brands & (Q5 & Q6))
IF (COUNT(select_brands) > 0) THEN {
NEWPAGE()
select_brands = (lst_brands & (Q5 & Q6))
SETQLIST(Q7,select_brands)
ASK(Q7)
ENDPAGE()
}
Note: select_brands recomputed inside NEWPAGE()/ENDPAGE(). This is because select_brand is reused in later questions and needs to be set correctly if the back/previous button is used.
Q8: Brands selected at Q7 but with Brands 1 & 2 always displayed as well.
select_brands = (lst_brands & (Q7 | {1,2}))
SETQLIST(Q8,select_brands)
ASK(Q8)
Q9: Shuffle brands but use REPLACE function to keep brands 3 & 4 together
select_brands = SHUFFLE(select_brands \ {4}) // Shuffle all brands apart from 4
select_brands = REPLACE(select_brands,{3},SHUFFLE({3,4})) // Replace brand 3 with SHUFFLE({3,4})
SETQLIST(Q9,select_brands)
ASK(Q9)
See REPLACE function for more examples
Q10: Display the first 5 brands from the previous rotation
selectbrands = SUBLIST(selectbrands,1,5)
SEQLIST(Q10,select_brands)
ASK(Q10)
See SUBLIST function for more examples
Example : SETQARRAY for grid questions
If the question being displayed is a grid then use SETQARRAY to control which elements in the list should be displayed.
QUESTIONS
Q9 "Which of these did you use in the last month?" : lst_brands{*}
Q10 [lst_brands] "On how many days did you use each of these brands" : 1..7
Then in the ROUTE section:
SETQARRAY(Q10,0,Q9)
ASK(Q10)
Example using FOREACH to create a list from responses to a grid question.
QUESTIONS
Q11[lst_brands] "How often do you buy these brands" : {Never,Sometimes,Often}
Q12 "Of the brands bought sometimes/often which brand will you buy next?"
Then in the ROUTE section:
just_brands = lst_brands \ {None}
SETQARRAY(Q11,0,just_brands)
ASK(Q11)
select_brands = {}
FOREACH i_brands IN lst_brands DO {
IF (Q11[i_brands] IN {Sometimes,Often}) THEN {
select_brands = (select_brands | i_brands)
}
}
SETQLIST(Q12,select_brands)
ASK(Q12)
Randomise/Rotate list of choices and question order
In some surveys the lists of choices or a group of questions have to be presented in random order to different respondents. For example to avoid potential bias because of the order in which response choices or questions are presented.
There are parameters that can be used to do simple randomisation of lists of choices with the canvas and in the ASK statement. More complex randomisation can be done with the list SHUFFLE function as in the example below.
In some surveys response choices have to be rotated. This is like cutting a deck of cards into 2 piles and putting the bottom pile on top. So one choice is chosen at random to be the first and other choices follow in the original order. For example:
item1, item2, item3, item4, item5 -> item3, item4, item5, item1, item2
The examples below use the SHUFFLE function but the same syntax can be used with the ROTATE function for rotation instead of random ordering.
Examples: SHUFFLE to randomise the order of lists
The first example below randomises the order of display of a list of items, ensuring that the 'Other','Dont Know' options remain at the bottom of the list.
DEFINITIONS
lst_items = {
1 "Item 1",
2 "Item 2",
3 "Item 3",
4 "Item 4",
5 "Item 5",
6 "Item 6",
7 "Item 7",
91 "Other, please specify" + OTH : TEXT[40] : 'size=40',
99 "Don't know"
}
QUESTIONS
Q1 "Which is your preferred item?": lst_items
Q2[lst_items] "How many of each of the items did you use last week" : INTEGER
VARIABLES
rand_items : lst_items
ROUTE
IF (NOANSWER(rand_items)) THEN rand_items = SHUFFLE(lst_items \ {91,99}) | {91,99}
NEWPAGE()
SETQLIST(Q1,rand_items)
ASK(Q1)
ENDPAGE()
NEWPAGE()
SETQARRAY(Q2,0,rand_items)
ASK(Q2)
ENDPAGE()
Example to shuffle when the items belong in 2 groups to be kept together.
rand_items = (SHUFFLE({1,2,3,4}) | SHUFFLE({5,6,7}) | {91,99})
Example: SHUFFLE and XASK to randomise the order in which questions are asked
This example computes a random order for questions Q1, Q2 and Q3 to be asked.
It uses XASK which can use a computed string for the name of the question to be asked.
//Define the list of question names:
DEFINITIONS
lst_questions = { "Q1", "Q2", "Q3" }
yesno = { 1 "Yes", 2 "No" }
//Define the questions:
QUESTIONS
Q1 "Question about brand 1" : yesno
Q2 "Question about brand 2" : yesno
Q3 "Question about brand 3" : yesno
//Define the VARIABLE needed to hold the list of questions in random order.
VARIABLES
question_list : lst_questions
//Create the list of questions in random order and use XASK to ask in that order. The ? operator is used to get the 1st, 2nd, and 3rd elements of the shuffled list of question names.
ROUTE
IF (NOANSWER(question_list)) THEN question_list = SHUFFLE(lst_questions)
NEWPAGE()
XASK(question_list?1)
XASK(question_list?2)
XASK(question_list?3)
ENDPAGE()
Playing audio and video files
Audio and video files can be played during the interview.
audio : play an audio file
The @AUDIO:myclip$ tag, where myclip is the name of an audio file, can be used to add an audio clip in native HTML5 format. This standard format is preferable to other audio support options.
Copy myclip.mp4 and/or myclip.webm and/or myclip.ogg (or myclip.oga) and/or myclip.aac and/or myclip.wav files to the .docs directory for the project. (click here for information about the .docs directory.)
For example to play myclip.mp3 specify the file name and controls in a MESSAGE.
MESSAGE('@AUDIO:myclip:controls=1:autoplay=yes:download=no$')
The parameters are separated by the ':' character, and the first parameter is the name of the audio file.
The value 'yes' can be replaced by '1' or 'true'.
The value 'no' can be replaced by '0' or 'false' or 'none'.
The supported parameters are:
- autoplay = yes: automatically start the clip
- loop = yes: loop the clip
- muted = yes: disabling the clip sound
- controls = yes: displaying clip controls
- download = no: remove the 'download audio' control
- remoteplayback = no: remove remoteplayback control
Other parameters are interpreted as CSS attributes: for example, 'width=100%' becomes "style='width: 100%;'".
video : play a video file
The @VIDEO:myclip$ tag, where myvideo is the name of an video file, can be used to add an video clip in native HTML5 format. This standard format is preferable to other video support options.
Copy myvideo.mp4 and/or myvideo.webm and/or myvideo.ogg (or myvideo.ogv) files to the .docs directory for the project. (click here for information about the .docs directory.)
video.online-convert can be used to convert video files.
For example, to play myvideo.mp3 specify the file name and controls in a MESSAGE.
MESSAGE('@VIDEO:myvideo:width=100%:controls=1:autoplay=no:download=no:muted=true:fullscreen=0:poster=gide.png$')
The parameters are separated by the ':' character, and the first parameter is the name of the video.
The value 'yes' can be replaced by '1' or 'true'.
The value 'no' can be replaced by '0' or 'false' or 'none'.
The supported parameters are:
- autoplay = yes: automatically start the video
- loop=yes: loop video
- muted=yes: disable video sound
- controls=yes: display video controls
- fullscreen=no: remove the 'fullscreen' control
- download=no: remove the 'download video' control
- remoteplayback=no: remove remoteplayback control
- poster=mypicture.png: display the image mypicture.png (in project.docs folder) before starting the video
Other parameters are interpreted as CSS attributes: for example, 'width=100%' becomes "style='width: 100%;'".
Printing individual responses to a pdf file
Respondents can be given the option of downloading a pdf report of their responses when they have completed the survey. The example below shows how the content of this pdf can be controlled.
PRINTING
PAGE()
BEGINCANVAS(c)
ASK(Q1) // NOTE: Use ASK() inside a canvas, not PRINT()
ENDCANVAS(c)
// Use normal canvas commands - SETBLOCK etc
// Example below sets relative column widths, in this case allowing more space for text in the first column.
PRINTCANVAS(c,'Q1','ft=430:50:50')
PRINT(Q2,'ft=430:50:50')
// Start a second page
PAGE()
PRINT(Q3)
PRINT(Q4)