Block Tag
<block> tag is used to group related questions/pages so they can be controlled together.
Use a block when you want to:
keep a section of questions together,
apply one condition to many questions (
cond on the block),
randomize whole sections (
randomizeChildren="1" on a parent block),
organize survey flow cleanly.
Example:
<block label="Section_A" cond="Q1.r1">
<radio label="Q2">...</radio>
<text label="Q3">...</text>
</block>
This means Q2 and Q3 are shown as one section only when Q1.r1 is true.
1) Randomize 3 Sections and capture the order of the Sections
<block label="Parent" randomizeChildren="1">
<block label="child_1" randomize="1">
<html label="intro_A">Welcome to Section A.</html>
</block>
<block label="child_2" randomize="1">
<html label="intro_B">Welcome to Section B.</html>
</block>
<block label="child_3" randomize="1">
<html label="intro_C">Welcome to Section C.</html>
</block>
</block>
<suspend/>
<number
label="Section_Order"
rowLegend="left"
shuffle="rows"
size="1"
where="execute,survey,report">
<title>Hidden :: Order of Section Shown:</title>
<exec>
Section_Order.rows.order = Parent.order
c = 0
for x in Section_Order.rows.order:
Section_Order[x].val = c + 1
c+=1
</exec>
<row label="r1">Section A</row>
<row label="r2">Section B</row>
<row label="r3">Section C</row>
</number>
<suspend/>
Validation of Survey XML
In Decipher, validation means controlling response quality and enforcing survey rules before a respondent can continue.
1) Validation for percentage of "Desktop" and "Mobile" should be no more than 100%.
<stylevar name="ss:number"/>
<select
label="D5"
grouping="cols"
surveyDisplay="desktop">
<title>To the best of your knowledge, across the design process, what percentage of your interaction with creative tools / platforms is on desktop vs. mobile?</title>
<comment>(Select one per column.)</comment>
<validate>
for eachCh in D5.choices:
if eachCh.c1:
number1 = int(eachCh.styles.ss.number)
if eachCh.c2:
number2 = int(eachCh.styles.ss.number)
if (number1 + number2) gt 100:
error(res.D5Error)
</validate>
<col label="c1">Desktop</col>
<col label="c2">Mobile (e.g., App, tablet, etc.)</col>
<choice label="ch1" ss:number="0" value="1">0%</choice>
<choice label="ch2" ss:number="5" value="2">5%</choice>
<choice label="ch3" ss:number="10" value="3">10%</choice>
<choice label="ch4" ss:number="15" value="4">15%</choice>
<choice label="ch5" ss:number="20" value="5">20%</choice>
<choice label="ch6" ss:number="25" value="6">25%</choice>
<choice label="ch7" ss:number="30" value="7">30%</choice>
<choice label="ch8" ss:number="35" value="8">35%</choice>
<choice label="ch9" ss:number="40" value="9">40%</choice>
<choice label="ch10" ss:number="45" value="10">45%</choice>
<choice label="ch11" ss:number="50" value="11">50%</choice>
<choice label="ch12" ss:number="55" value="12">55%</choice>
<choice label="ch13" ss:number="60" value="13">60%</choice>
<choice label="ch14" ss:number="65" value="14">65%</choice>
<choice label="ch15" ss:number="70" value="15">70%</choice>
<choice label="ch16" ss:number="75" value="16">75%</choice>
<choice label="ch17" ss:number="80" value="17">80%</choice>
<choice label="ch18" ss:number="85" value="18">85%</choice>
<choice label="ch19" ss:number="90" value="19">90%</choice>
<choice label="ch20" ss:number="95" value="20">95%</choice>
<choice label="ch21" ss:number="100" value="21">100%</choice>
</select>
<suspend/>
2) Validate respondent if the input is not in this list ["one", "two", "three"] and should be unique response.
<text
label="D1"
grouping="cols"
optional="0">
<title>Please rate each item using the words "one", "two" or "three" based on the criteria provided:</title>
<validate>
eAns = set(["one", "two", "three"])
for eachCol in this.cols:
ansC = set([ ans.lower() for ans in eachCol.values ])
if (eAns.difference(ansC) != set()): # Instead of using the difference() method, you can directly subtract two sets i.e., if (eAns - ansC) != set(): || if both set are same return empty set() otherwise return {'missmatched value'}
error("Please rate each item using the digit 'one', 'two' and 'three' where '1' is the best.", col=eachCol)
</validate>
<row label="r1">Item 1</row>
<row label="r2">Item 2</row>
<row label="r3">Item 3</row>
<col label="c1">Best value</col>
<col label="c2">Best customer service</col>
</text>
<suspend/>
3) Validation to show error if same answer is selected in previous loop
<exec>
c = 0
reqArray = dN0.rows.order
print(reqArray)
v = [str(eR.attr('index')) for eR in reqArray if eR.displayed]
print(v)
for r in v:
dN0.rows[int(r)].val = c + 1
c = c + 1
</exec>
<number
label="dN0"
rowLegend="left"
shuffle="rows"
size="2"
where="execute,survey,report">
<title>Dummy to store order.</title>
<row label="r1" value="1">Privacy & Data Governance</row>
<row label="r2" value="2">Risk Management & Compliance (GRC)</row>
<row label="r3" value="3">Information Security</row>
<row label="r4" value="4">Ethics & Compliance</row>
<row label="r5" value="5">Diversity, Equity & Inclusion (DEI)</row>
</number>
<suspend/>
<exec>
LoopN0_expanded.order = dN0.rows.order
odr = LoopN0_expanded.order
print odr
</exec>
<loop label="LoopN0" vars="attr1">
<block label="B_N0">
<radio
label="N0_[loopvar: label]">
<title>Tell us more about your role in evaluating and selecting tools that help with managing <b>([loopvar: attr1])</b> initiatives (e.g., software, services, platforms).</title>
<comment>(Please select the answer that best applies to you.) <br /><br /><div align="center"><b>([loopvar: attr1])</b></div></comment>
<validate>
set_1 = set()
List_1 = []
count = 0
odr = LoopN0_expanded.order
for i in odr:
F = ['N0_{}'.format(i+1)]
L = eval(F[0])
for x in L.rows:
if x and (x.label not in set_1) and L.any:
set_1.add(x.label)
count+=1
List_1.append(L)
elif x and (x.label in set_1) and L.any:
List_2 = [q for q in List_1 if q.attr(x.label) ]
error("You have already selected option {} in loop {}".format(x.label, List_2[0].attr('label')))
</validate>
<row label="r1" value="1">I am not involved in this decision-making process</row>
<row label="r2" value="2">I am involved in decision-making discussions but donβt have a formal voice in the decision</row>
<row label="r3" value="3">I inform and influence the decision (e.g., by evaluating alternatives and/or making recommendations)</row>
<row label="r4" value="4">I am a decision maker in this area, either solely or as a member of the group that makes the decisions</row>
<row label="r5" value="5">I oversee those with decision making authority in this area</row>
</radio>
<suspend/>
</block>
<looprow label="1">
<loopvar name="attr1">Privacy & Data Governance</loopvar>
</looprow>
<looprow label="2">
<loopvar name="attr1">Risk Management & Compliance (GRC)</loopvar>
</looprow>
<looprow label="3">
<loopvar name="attr1">Information Security</loopvar>
</looprow>
<looprow label="4">
<loopvar name="attr1">Ethics & Compliance</loopvar>
</looprow>
<looprow label="5">
<loopvar name="attr1">Diversity, Equity & Inclusion (DEI)</loopvar>
</looprow>
</loop>
<suspend/>
4) Function to validate the respondent to input response in sequential order.
# Use seqOE() function to validate the respondent to input response in sequential order in Open-End question with multiple options.
def seqOE():
m = max([x.index for x in this.rows if x.val != ''])
for i in range(m):
if this.rows[i].val in ['',None]:
error("Please response in sequencial order.",this.rows[i])
<text
label="D2"
optional="0">
<title>Please input your response for each items:</title>
<validate>
seqOE()
</validate>
<row label="r1">Item 1</row>
<row label="r2" optional="1">Item 2</row>
<row label="r3" optional="1">Item 3</row>
<row label="r4" optional="1">Item 4</row>
<row label="r5" optional="1">Item 5</row>
<row label="r6" optional="1">Item 6</row>
</text>
<suspend/>
5) Validates if the response does not contain any alphabetic characters using regex.
def OE_Check(response):
if bool(re.search("[a-zA-Z]", response)) == 0:
error("Please review your response, and input any alphabetic characters.")
<textarea
label="D4a">
<title>Please provide your response (max 50 characters):</title>
<validate>
OE_Check(D4a.val)
</validate>
</textarea>
<suspend/>
Decipher Script Logic
Decipher script logic controls routing, personalization, derived variables, validation behavior, and survey flow decisions.
Where Script Logic Is Used
- Classifying respondents into segments.
- Showing/hiding questions with conditions.
- Piping prior responses into later questions.
- Applying custom validation beyond basic attributes.
- Controlling quota and quality logic.
1) Set the rows order of the question below to start with the row selected with the highest value from question D6.
<select
label="D6"
minRanks="5"
optional="1"
unique="none,cols"
surveyDisplay="desktop">
<title>Placeholder select question.</title>
<comment>Please click on the choices in order of your preference</comment>
<choice label="ch1" value="1">Very bad</choice>
<choice label="ch2" value="2">Bad</choice>
<choice label="ch3" value="3">Medium</choice>
<choice label="ch4" value="4">Good</choice>
<choice label="ch5" value="5">Very Good</choice>
<row label="r1">Brand 1</row>
<row label="r2">Brand 2</row>
<row label="r3">Brand 3</row>
<row label="r4">Brand 4</row>
<row label="r5">Brand 5</row>
</select>
<suspend/>
<!--
Set the rows order of the question below to start with the row selected with the highest value from question D6.
i.e. if Row 4 is selected with 5 (Very Good), show Row 4 first.
-->
<exec>
allChoice = [ (eachRow.val + 1) for eachRow in D6.rows] #[5,4,3,1,2]
ordRow = [allChoice.index(x) for x in range(5,0,-1) ] #[0, 1, 2, 5, 4]
print(ordRow)
print(range(5,0,-1))
D6Arrange.rows.order = ordRow
</exec>
<radio
label="D6Arrange"
shuffle="rows">
<title>Placeholder question for rows order.</title>
<comment>Select one</comment>
<row label="r1">Brand 1</row>
<row label="r2">Brand 2</row>
<row label="r3">Brand 3</row>
<row label="r4">Brand 4</row>
<row label="r5">Brand 5</row>
</radio>
<suspend/>
Advanced Survey XML Topics
1) Splitting a Grid Question(Z1) into 3 screens [options is showing Randomly].
<radio
label="Z1"
shuffle="rows"
where="report">
<title>Please rate your experience with each of the following brands.</title>
<comment>Select one in each row</comment>
<insert source="Brands"/>
<insert as="cols" source="ExpRate"/>
</radio>
<suspend/>
<radio
label="Z1_1"
rowCond="row.label in [x.label for x in Z1.rows.order[0:10]]"
shuffle="rows"
shuffleBy="Z1"
where="survey,notdp">
<title>Please rate your experience with each of the following brands.</title>
<comment>Select one in each row</comment>
<insert source="Brands"/>
<insert as="cols" source="ExpRate"/>
</radio>
<suspend/>
<radio
label="Z1_2"
rowCond="row.label in [x.label for x in Z1.rows.order[10:20]]"
shuffle="rows"
shuffleBy="Z1"
where="survey,notdp">
<title>Please rate your experience with each of the following brands.</title>
<comment>Select one in each row</comment>
<insert source="Brands"/>
<insert as="cols" source="ExpRate"/>
</radio>
<suspend/>
<radio
label="Z1_3"
rowCond="row.label in [x.label for x in Z1.rows.order[20:]]"
shuffle="rows"
shuffleBy="Z1"
where="survey,notdp">
<title>Please rate your experience with each of the following brands.</title>
<comment>Select one in each row</comment>
<insert source="Brands"/>
<insert as="cols" source="ExpRate"/>
</radio>
<suspend/>
<exec>
splitQuestion = [Z1_1, Z1_2, Z1_3]
for eQns in splitQuestion: # All three split question data is assigning in Z1.
for eRow in eQns.rows:
if eRow.displayed and (eRow.val is not None):
Z1[eRow].val = eRow.val
</exec>
<suspend/>
2) Splitting a Grid Question(Z3) into 3 screens, selected response from Z2 should be shown in Z3 [need to show only 10 row in 1 screen randomly]
<checkbox
label="Z2"
atleast="1"
shuffle="rows">
<title>Please select the following brands that you wanted to buy from.</title>
<comment>Select one all that apply</comment>
<insert source="Brands"/>
</checkbox>
<suspend/>
<checkbox
label="Z3"
where="report">
<title>Which of the following brand you would most prefer?</title>
<insert source="Brands"/>
</checkbox>
<suspend/>
<checkbox
label="Z3_1"
rowCond="row.label in [erw.label for erw in Z2.rows.order[0:10] if erw]"
shuffle="rows"
shuffleBy="Z2"
where="survey,notdp">
<title>Which of the following brand you would most prefer?</title>
<comment>Select one all that apply</comment>
<insert source="Brands"/>
</checkbox>
<suspend/>
<checkbox
label="Z3_2"
rowCond="row.label in [erw.label for erw in Z2.rows.order[10:20] if erw]"
shuffle="rows"
shuffleBy="Z2"
where="survey,notdp">
<title>Which of the following brand you would most prefer?</title>
<comment>Select one all that apply</comment>
<insert source="Brands"/>
</checkbox>
<suspend/>
<checkbox
label="Z3_3"
rowCond="row.label in [erw.label for erw in Z2.rows.order[20:] if erw]"
shuffle="rows"
shuffleBy="Z2"
where="survey,notdp">
<title>Which of the following brand you would most prefer?</title>
<comment>Select one all that apply</comment>
<insert source="Brands"/>
</checkbox>
<suspend/>
<exec>
qns = [Z3_1, Z3_2, Z3_3]
for eQ in qns: # All three split question data is assigning in Z3.
for erw in eQ.rows:
if (erw.displayed and erw):
Z3[erw.index].val = 1
print([x.label for x in Z2.rows if x])
</exec>
<suspend/>
Time Tracking Question
This pattern captures time spent on a specific question and on each page using Decipher's timing helpers.
1) Question Time Tracking Using preciseTimeSpent() Function
<text
label="Q1"
optional="0"
size="40"
ss:listDisplay="0">
<title>When you think of a hotel rewards program, what brands first come to mind?</title>
<row label="r1">brand 1</row>
<row label="r2">brand 2</row>
<row label="r3">brand 3</row>
<noanswer label="r99">No brands come to mind</noanswer>
</text>
<suspend/>
<note>==== Question Time Tracking ====</note>
<float
label="TimeQuestion"
size="10"
ss:listDisplay="0"
translateable="0"
where="execute,survey,report">
<title>Hidden: to capture time spent on each question below.</title>
<exec>
qid = TimeQuestion
for each in qid.rows:
each.val = 0
qid.Q1.val = preciseTimeSpent("Q1")
</exec>
<row label="Q1">Q1</row>
</float>
<suspend/>
2) Time Tracking Per Page in Virtual Question Using preciseTimePerPage() Function
<note>==== Time Tracking Per Page in virtual Question====</note>
<float
label="pagetime"
onLoad="preciseTimePerPage()">
<title>.</title>
<virtual>
preciseTimePerPageDelphi(data,this)
</virtual>
</float>
<suspend/>
Functions
preciseTimeSpent("Q1"): Returns time spent on question Q1.preciseTimePerPage(): Initializes per-page time tracking.preciseTimePerPageDelphi(data,this): Writes page time results into the variable.
Loop Question
This example shows how to capture loop order using a virtual question and a hidden order question.
1) Capture the order of the loop using the assignRandomOrder() function in the virtual question
<loop label="l1" randomizeChildren="1" vars="brand">
<title>L1 Loop</title>
<block label="b1">
<text
label="Q5_[loopvar: label]"
optional="0"
randomize="0"
size="50">
<title>What do you like about [loopvar: brand]?</title>
<comment>be specific</comment>
</text>
</block>
<looprow label="r1">
<loopvar name="brand">Brand 1</loopvar>
</looprow>
<looprow label="r2">
<loopvar name="brand">Brand 2</loopvar>
</looprow>
<looprow label="r3">
<loopvar name="brand">Brand 3</loopvar>
</looprow>
</loop>
<number
label="l1_LOOP_ORDER"
size="1">
<title>l1 Loop Order</title>
<virtual>
assignRandomOrder('l1_expanded', 'children')
</virtual>
<row label="l1_r1_expanded">Loop 1</row>
<row label="l1_r2_expanded">Loop 2</row>
<row label="l1_r3_expanded">Loop 3</row>
</number>
<suspend/>
2) Capture the order of the loop in hidden question
<exec>
#Assign Order to Hidden Question
for i, er in enumerate(N1_Order.rows.order, 1):
N1_Order[er].val = i
print(i,er)
</exec>
<number
label="N1_Order"
rowLegend="left"
shuffle="rows"
size="4"
where="execute,survey,report">
<title>HIDDEN :: To capture the randomized loop_N1 order of brands and store it in a hidden question (N1_Order).</title>
<row label="r1">Brand A</row>
<row label="r2">Brand B</row>
<row label="r3">Brand C</row>
<row label="r4">Brand D</row>
</number>
<suspend/>
<exec>
loop_N1_expanded.order = N1_Order.rows.order
</exec>
<loop label="loop_N1" randomizeChildren="1" vars="brand">
<block label="b2">
<exec>
p.loopindex = loop_N1.index+1
print p.loopindex
</exec>
<radio
label="N1_[loopvar: label]">
<title>Which brand do you like the most?</title>
<comment>Select one<br /><br /><div align="center" style="color:blue; font-size:15px; font-weight:bold">[loopvar: brand]</div></comment>
<col label="c1" value="1">1=Not at all likely</col>
<col label="c2" value="2">2</col>
<col label="c3" value="3">3</col>
<col label="c4" value="4">4</col>
<col label="c5" value="5">5</col>
<col label="c6" value="6">6</col>
<col label="c7" value="7">7=Very likely</col>
</radio>
<suspend/>
</block>
<looprow label="1">
<loopvar name="brand">Brand A</loopvar>
</looprow>
<looprow label="2">
<loopvar name="brand">Brand B</loopvar>
</looprow>
<looprow label="3">
<loopvar name="brand">Brand C</loopvar>
</looprow>
<looprow label="4">
<loopvar name="brand">Brand D</loopvar>
</looprow>
</loop>
<suspend/>
Datasource Tag: Pull Data From a Tab-Delimited File
The <datasource> element is a survey builder-friendly way of merging external data
from a tab-delimited file into your survey. The data is merged into the survey as each participant enters
and can be used to create additional logic or pipes from.
For example, given the following tab-delimited file named resp-data.txt:
id first last age
100 Johnny Tsunami 22
101 Danny California 30
102 Juniper Rose 20
We can create a <datasource> element to pull in this data and create a custom welcome
message for each participant:
<datasource label="Resp_DS"
title="Respondent first/last name and age"
filename="resp-data.txt"
ourKey="source"
datasourceKey="id"
normalizeKey="lower">
<text label="Q_RespData" optional="0" dataSource="Resp_DS" where="execute">
<title>
Respondent first/last name and age.
</title>
<row label="First" dataRef="first">First</row>
<row label="Last" dataRef="last">Last</row>
<row label="Age" dataRef="age">Age</row>
</text>
</datasource>
<html label="Introduction" where="survey">
Welcome to the survey, ${Q_RespData.First}! Please click "Continue".
</html>
Datasource - Randomize Loop with Datasource
This example shows how to capture a respondent ID, pull a rotation file via a datasource, and then use it to drive randomized loop order.
Download rotation.dat
<number
label="Q0"
optional="0"
size="3"
verify="range(1, 120)">
<title>Before you begin, please enter your respondent ID:</title>
<comment>Please enter a whole number</comment>
</number>
<suspend/>
<datasource label="Resp_DS" datasourceKey="ID" filename="rotation.dat" ourKey="Q0.val">
<title>Respondent first/last name and age</title>
<text
label="Q_RespData"
dataSource="Resp_DS"
optional="0"
rowLegend="left"
where="execute,survey,report">
<title>Respondent Product1/Product2/Product3.</title>
<row label="r1" dataRef="Product1"/>
<row label="r2" dataRef="Product2"/>
<row label="r3" dataRef="Product3"/>
</text>
</datasource>
<suspend/>
<exec>
Q_RespDataValue = [eachRow.val for eachRow in Q_RespData.rows]
print Q_RespDataValue
ordIndex = [OrderN100.attr(eachVal).index for eachVal in Q_RespDataValue]
print ordIndex
OrderN100.rows.order = ordIndex
for i, er in enumerate(OrderN100.rows.order, 1):
OrderN100[er].val = i
print(i,er)
</exec>
<number
label="OrderN100"
rowLegend="left"
shuffle="rows"
size="4"
where="execute,survey,report">
<title>HIDDEN : used for capturing the order for loop_N100</title>
<row label="W">LALA - Strawberry</row>
<row label="X">Chobani β Strawberry & Cream</row>
<row label="Y">Oikos β Strawberry Banana</row>
</number>
<suspend/>
<exec>
loop_N100_expanded.order = OrderN100.rows.order
</exec>
<loop label="loop_N100" randomizeChildren="1" vars="brand">
<block label="b1">
<exec>
p.loopindex = loop_N100.index
print p.loopindex
p.productCode = OrderN100[[loopvar: label]-1].label
print p.productCode
</exec>
<radio
label="Q1_[loopvar: label]">
<title>Which product are you evaluating?</title>
<comment>Select one</comment>
<row label="r1">LALA - Strawberry</row>
<row label="r2">Chobani β Strawberry & Cream</row>
<row label="r3">Oikos β Strawberry Banana</row>
</radio>
<suspend/>
</block>
<looprow label="1">
<loopvar name="brand">LALA - Strawberry</loopvar>
</looprow>
<looprow label="2">
<loopvar name="brand">Chobani β Strawberry & Cream</loopvar>
</looprow>
<looprow label="3">
<loopvar name="brand">Oikos β Strawberry Banana</loopvar>
</looprow>
</loop>
<suspend/>
Quota Fundamentals in Decipher
Quotas in Decipher are used to control, track, and monitor the number of qualified completes within each target segment of a survey (such as age, gender, region, or user type) and to prevent overfilling.
What a Quota Does
- Tracks completes against target cells.
- Assigns respondents to eligible quota cells by condition.
- Applies behavior when a cell is full (`terminate`, `noqual`, or project-specific action).
- Supports balanced sample design when used with groups and priorities.
Basic Quota Structure (A Simple Quota)
<radio label="Q1" optional="0">
<title>Are you...</title>
<row label="r1">Male</row>
<row label="r2">Female</row>
</radio>
<suspend/>
<quota label="quota_age" sheet="general" />
| A | B | C |
|---|---|---|
| 1 | qual | plus |
| 2 | Male | Q1.r1 |
| 3 | Female | Q1.r2 |
defines
| A | B | C |
|---|---|---|
| 1 | #2000=Gender | |
| 2 | Male | 50% |
| 3 | Female | 50% |
general
Least Fill Quota Logic (Lowest-Bucket Fill)
Least fill is a quota assignment strategy that routes an eligible respondent to the currently least-filled open cell, helping keep distribution balanced in real time.
If a participant qualifies for more than one marker, the system selects the marker with the lowest completion percentage.
How it works:
If all markers have the same limit, the system chooses the one with the lowest number of completes.
If markers have different limits, the system chooses the one with the lowest percentage of completion.
Example:
Marker A: 10 out of 100 completes (10%)
Marker B: 4 out of 5 completes (80%)
The system will choose Marker A because it has a lower completion percentage and needs more participants to reach its target.
When to Use Least Fill
- You need even distribution across similar quota cells.
- Cells have comparable incidence and similar business value.
- You want automatic balancing without manual re-routing.
Least Fill Assigning Single Markers
<quota label="quota_concept" sheet="concept" />
<radio label="vConcept" where="execute">
<title>HIDDEN: Assigned Concept</title>
<exec>
for x in xrange(3):
if hasMarker('/concept/Concept_{}'.format(x+1)):
vConcept.val = x
break
</exec>
<row label="r1">Concept 1</row>
<row label="r2">Concept 2</row>
<row label="r3">Concept 3</row>
</radio>
<suspend/>
<html label="Show_Concept">
Now you will be asked about [pipe: vConcept].
</html>
<suspend/>
| A | B | C |
|---|---|---|
| 1 | Concept_1 | plus |
| 2 | Concept_2 | plus |
| 3 | Concept_3 | plus |
defines
| A | B | C |
|---|---|---|
| 1 | #=Concept Pick | |
| 2 | Concept_1 | inf |
| 3 | Concept_2 | inf |
| 4 | Concept_3 | inf |
concept
Assigning Multiple Markers
<quota label="quota_concept" sheet="concept" />
<checkbox label="vConcept" exactly="2" shuffle="rows" where="execute">
<title>HIDDEN: Assigned Concept</title>
<exec>
for x in xrange(3):
if hasMarker('Concept_{}'.format(x+1)):
vConcept.rows[x].val = 1
</exec>
<row label="r1">Concept 1</row>
<row label="r2">Concept 2</row>
<row label="r3">Concept 3</row>
</checkbox>
<suspend/>
<exec>ConceptLoop_expanded.order = vConcept.rows.order</exec>
<loop label="ConceptLoop" vars="concept" randomizeChildren="1">
<block label="ConceptStage" >
<html label="Show_Concept_[loopvar: label]">
Now you will be asked about [loopvar: concept].
</html>
</block>
<looprow label="1" cond="vConcept.r1"><loopvar name="concept">CONCEPT 1</loopvar></looprow>
<looprow label="2" cond="vConcept.r2"><loopvar name="concept">CONCEPT 2</loopvar></looprow>
<looprow label="3" cond="vConcept.r3"><loopvar name="concept">CONCEPT 3</loopvar></looprow>
</loop>
| A | B | C |
|---|---|---|
| 1 | Concept_1 | plus |
| 2 | Concept_2 | plus |
| 3 | Concept_3 | plus |
defines
| A | B | C |
|---|---|---|
| 1 | #cells:2=Concept Pick | |
| 2 | Concept_1 | inf |
| 3 | Concept_2 | inf |
| 4 | Concept_3 | inf |
concept
Priority Quota Strategy
Priority quotas are used when one respondent qualifies for multiple quota cells. Decipher allocates the respondent to the highest-priority available cell first.
By default, quota markers have a priority level of 0 and they're all considered even. The level of quota marker priority/importance can be set per quota marker using a special syntax. "LIMIT:#"
Higher priority # markers will be assigned first. Markers without a priority number set default to 0, the lowest.
Priority Logic Concept
- 1. Evaluate eligible cells.
- 2. Check priority rank (higher number = higher priority).
- 3. Assign to highest-priority open cell.
- 4. If highest priority is full, try next eligible priority.
Priority Quota Example
<quota label="quota_concept" sheet="concept" />
<checkbox label="vConcept" exactly="2" shuffle="rows" where="execute">
<title>HIDDEN: Assigned Multiple Concept and Prioritized Concept_1.</title>
<exec>
for x in xrange(3):
if hasMarker('Concept_{}'.format(x+1)):
vConcept.rows[x].val = 1
</exec>
<row label="r1">Concept 1</row>
<row label="r2">Concept 2</row>
<row label="r3">Concept 3</row>
</checkbox>
<suspend/>
<exec>ConceptLoop_expanded.order = vConcept.rows.order</exec>
<loop label="ConceptLoop" vars="concept" randomizeChildren="1">
<block label="ConceptStage" >
<html label="Show_Concept_[loopvar: label]">
Now you will be asked about [loopvar: concept].
</html>
</block>
<looprow label="1" cond="hasMarker('Concept_1')"><loopvar name="concept">CONCEPT 1</loopvar></looprow>
<looprow label="2" cond="hasMarker('Concept_2')"><loopvar name="concept">CONCEPT 2</loopvar></looprow>
<looprow label="3" cond="hasMarker('Concept_3')"><loopvar name="concept">CONCEPT 3</loopvar></looprow>
</loop>
| A | B | C |
|---|---|---|
| 1 | Concept_1 | plus |
| 2 | Concept_2 | plus |
| 3 | Concept_3 | plus |
defines
| A | B | C |
|---|---|---|
| 1 | #cells:2=Concept Pick | |
| 2 | Concept_1 | inf:1 |
| 3 | Concept_2 | inf |
| 4 | Concept_3 | inf |
concept
Quota Hook Concepts
Quota hooks let you run custom logic around quota decisions, typically for controlled assignment, custom balancing, or external sample conditions.
Defining the Function.
<exec when="init">
def quota_hook(sheet, table, cells):
...
return cells
</exec>
1. Available Variables
-
You have access to the following variables within the quota_hook function:
- sheet: A string with the name of the current quota sheet. In the above example, "Sheet 1" would be returned.
- table: An object where you have access to information about the current quota table.
- table.cellCount: The amount of cells that will be selected in the current table. In the above example, cellCount would return 4. If cells:# is not defined, cellCount will default to 1. This number cannot be changed.
- table.description: A string with the description of the current table. In the above example, "Brand Quota" would be returned. If your table does not have a description defined, "Unnamed table #X" will be returned, where X is the numbered table from top to bottom on the sheet starting with the number 1.
- cells: A list where each item is information about the cells of a table. cells needs to be returned at the end of the quota hook but can be modified to remove unwanted items. Items in cells will be in order of least filled and priority, so items at the beginning of the list will be picked first. Each item in the cells variable has access to the following, where cell is an item inside of cells:
- cell[0]: The percent filled of the quota cell, including pending markers.
- cell[1]: A key used by the system.
- cell[2]: A string containing the marker name. In the above example quota, this would be "/Sheet 1/BrandA","/Sheet 1/BrandB","/Sheet 1/BrandC", or "/Sheet 1/BrandD"
Example: Punching 2 options, if respondent qualify for the marker based on Priorities in Quota hook.
<note># cells:2 - Punching 2 options, if respondent qualify for the marker based on Priorities in Quota hook.</note>
<radio
label="Q1">
<title>Please rate the brands based on satisfaction.</title>
<col label="c1">Very Unsatisfied</col>
<col label="c2">Unsatisfied</col>
<col label="c3">Neither</col>
<col label="c4">Satisfied</col>
<col label="c5">Very Satisfied</col>
<row label="r1">Household name brands</row>
<row label="r2">Name brands</row>
<row label="r3">Brands that are popular within my circle of friends</row>
<row label="r4">Local / regional brands</row>
<row label="r5">DTC brands</row>
<row label="r6">Social media brands</row>
<row label="r7">"Up and coming" brands</row>
<row label="r8">Value brands</row>
</radio>
<suspend/>
<exec when="init">
def quota_hook(sheet, table, cells):
if sheet == 'Q1_QuotaHook':
cellsOrdered = []
C1 = ['/Q1_hook/Q1_' + eachRow.label for eachRow in Q1.rows if eachRow.c1]
C2 = ['/Q1_hook/Q1_' + eachRow.label for eachRow in Q1.rows if eachRow.c2]
C3 = ['/Q1_hook/Q1_' + eachRow.label for eachRow in Q1.rows if eachRow.c3]
C4 = ['/Q1_hook/Q1_' + eachRow.label for eachRow in Q1.rows if eachRow.c4]
C5 = ['/Q1_hook/Q1_' + eachRow.label for eachRow in Q1.rows if eachRow.c5]
markerPriorities = [C5,C4,C3,C2,C1]
for eachPriority in markerPriorities:
for eachCell in cells:
if eachCell[2] in eachPriority:
cellsOrdered.append(eachCell)
return cellsOrdered
else:
return cells
</exec>
<quota label="quota_hook" overquota="noqual" sheet="Q1_hook"/>
<checkbox
label="dQ1"
where="execute,survey,report">
<title>Hidden:: Punching "Brands" based on satisfaction.</title>
<exec>
for erows in dQ1.rows:
if hasMarker('/Q1_QuotaHook/Q1_{}'.format(erows.label)):
erows.val = 1
</exec>
<row label="r1">Household name brands</row>
<row label="r2">Name brands</row>
<row label="r3">Brands that are popular within my circle of friends</row>
<row label="r4">Local / regional brands</row>
<row label="r5">DTC brands</row>
<row label="r6">Social media brands</row>
<row label="r7">"Up and coming" brands</row>
<row label="r8">Value brands</row>
</checkbox>
<suspend/>
MaxDiff Exercise
The MaxDiff - Indices question is a special question type that requires custom modifications to your survey to get up and running. To create this question in your survey, you need to:
- Setup the design file.
- Setup the quota sheet.
- Copy the MaxDiff - indices method template into your survey.
- Modify the template according to your design requirements.
You can follow along by downloading this design file and this quota.xls file.
Below is the exact Decipher Indices Method template used to create a MaxDiff question.
MaxDiff Indices Method (Production Template)
<note>MaxDiff Indices Template --Start--</note>
<exec when="init">
def setupMaxDiffFile(fname, fileDelimiter="\t"):
try:
f = open("%s/%s" % (gv.survey.path, fname))
mdObj = [ line.strip("\r\n").split(fileDelimiter) for line in f.readlines() ]
d = dict( ("v%s_t%s" % (row[0], row[1]), row[2:]) for row in mdObj )
except IOError:
d = {}
return d
def setupMaxDiffItemsI(d, vt, question):
item_index = dict( (r.o.label.strip("item"), r.index) for r in question.rows )
items = d[vt]
for r in question.rows:
if r.o.label.strip("item") not in items:
r.disabled = True
question.rows.order = [ item_index[i] for i in items ]
print "*****STAFF ONLY*****"
print "Version_Task: %s" % vt
for i in range(len(items)):
print "Item %s: %s" % (i+1,items[i])
</exec>
<exec when="init">Q24_md = setupMaxDiffFile("design.dat")</exec>
<quota label="Q24_quota" overquota="noqual" sheet="Q24_Maxdiff"/>
<number label="Q24_Version" size="3" optional="1" verify="range(1,10)" where="execute">
<title>Q24 - MaxDiff Version</title>
<exec>
print p.markers
for x in p.markers:
if "/Q24_Maxdiff/ver_" in x:
Q24_Version.val = int(x.split("_")[-1])
break
</exec>
</number>
<suspend/>
<exec>p.startTime = timeSpent()</exec>
<loop label="Q24_md_loop" vars="task" randomizeChildren="0">
<title>Q24 - MaxDiff Loop</title>
<block label="Q24_md_block" randomize="1">
<radio label="Q24_[loopvar: task]" adim="cols" grouping="cols" shuffle="rows" unique="1" ss:questionClassNames="Q24_maxdiff">
<title>Title update [MDcount]</title>
<comment>Select one</comment>
<exec>
setupMaxDiffItemsI( Q24_md, "v%d_t%d" % (Q24_Version.val, [loopvar: task]), Q24_[loopvar: task])
p.MDcount = str(Q24_md_loop_expanded.order.index([loopvar: task]-1)+1)
</exec>
<col label="best">Most Important</col>
<col label="worst">Least Important</col>
<row label="item1">Traditional</row>
<row label="item2">Innovative</row>
<row label="item3">Steady</row>
<row label="item4">Fast-paced</row>
<row label="item5">Technology-oriented</row>
<row label="item6">Community-focused</row>
<row label="item7">Industry leader</row>
<row label="item8">Expert</row>
<row label="item9">Consultative</row>
<row label="item10">Customer-focused</row>
<row label="item11">Revenue-focused</row>
<row label="item12">Proactive</row>
<style name="question.header" mode="before">
<![CDATA[
<style type="text/css">
.Q24_maxdiff tr.maxdiff-header-legend {
background-color: transparent;
border-bottom: 2px solid #d9d9d9;
}
.Q24_maxdiff tr.maxdiff-header-legend th.legend {
background-color: transparent;
border: none;
}
.Q24_maxdiff tr.maxdiff-row td.element {
border-left: none;
border-right: none;
border-top: none;
border-bottom: 1px solid #d9d9d9;
text-align: center;
}
.Q24_maxdiff tr.maxdiff-row th.row-legend {
background-color: transparent;
border-left: none;
border-right: none;
border-top: none;
border-bottom: 1px solid #d9d9d9;
text-align: center;
}
</style>
]]>
</style>
<style name="question.top-legend">
<![CDATA[
\@if ec.simpleList
$(legends)
\@else
<$(tag) class="maxdiff-header-legend row row-col-legends row-col-legends-top ${"mobile-top-row-legend " if mobileOnly else ""}${"GtTenColumns " if ec.colCount > 10 else ""}colCount-$(colCount)">
${"%s%s" % (legends.split("</th>")[0],"</th>")}
$(left)
${"%s%s" % (legends.split("</th>")[1],"</th>")}
</$(tag)>
\@if not simple
</tbody>
<tbody>
\@endif
\@endif
]]>
</style>
<style name="question.row">
<![CDATA[
\@if ec.simpleList
$(elements)
\@else
<$(tag) class="maxdiff-row row row-elements $(style) colCount-$(colCount)">
${"%s%s" % (elements.split("</td>")[0],"</td>")}
$(left)
${"%s%s" % (elements.split("</td>")[1],"</td>")}
</$(tag)>
\@endif
]]>
</style>
</radio>
</block>
<looprow label="1">
<loopvar name="task">1</loopvar>
</looprow>
<looprow label="2">
<loopvar name="task">2</loopvar>
</looprow>
<looprow label="3">
<loopvar name="task">3</loopvar>
</looprow>
<looprow label="4">
<loopvar name="task">4</loopvar>
</looprow>
<looprow label="5">
<loopvar name="task">5</loopvar>
</looprow>
<looprow label="6">
<loopvar name="task">6</loopvar>
</looprow>
<looprow label="7">
<loopvar name="task">7</loopvar>
</looprow>
<looprow label="8">
<loopvar name="task">8</loopvar>
</looprow>
<looprow label="9">
<loopvar name="task">9</loopvar>
</looprow>
<looprow label="10">
<loopvar name="task">10</loopvar>
</looprow>
<looprow label="11">
<loopvar name="task">11</loopvar>
</looprow>
<looprow label="12">
<loopvar name="task">12</loopvar>
</looprow>
</loop>
<float label="Q24_Timer" size="15" where="execute">
<title>Q1 - MaxDiff Timer (Minutes)</title>
<exec>Q1_Timer.val = (timeSpent() - p.startTime) / 60.0</exec>
</float>
<note>MaxDiff Indices Template --End--</note>
Discrete Choice Model (DCM) Conjoint Question
Use this full Decipher DCM template to set up design-based conjoint tasks with versioning, quota assignment, dynamic attribute piping, and timing capture.
To create this question in your survey, you need to:
- Setup the design file.
- Setup the quota sheet.
- Copy the DCM question template into the survey.
- Modify the template according to your design requirements.
You can follow along by downloading this design file: example_dcm_design
"Q24_DCM" Sheet with a "DCM Versions" table that randomly assigns each participant one of the fifty version markers. Click below to download this example quota file: dcm_example_quota
<note>DCM Question Template --Start--</note>
<exec when="init">
def setupDCMFile(fname, fileDelimiter="\t"):
f = open("%s/%s" % (gv.survey.path, fname))
dcmObj = [ line.strip("\r\n").split(fileDelimiter) for line in f.readlines() ]
d = {}
dcm_concepts = []
for i,row in enumerate(dcmObj):
if i:
d["v%s_t%s_c%s" % (row[0],row[1],row[2])] = row[3:]
if row[2] not in dcm_concepts:
dcm_concepts.append(row[2])
concepts = [ int(x) for x in dcm_concepts ]
concepts.sort()
d["concepts"] = dcm_concepts
return d
#set persistent items, format: p.concept#_att#
def setupDCMItems(d, vt, prefix="1"):
print "***** STAFF ONLY *****"
print "***** DCM Matrix *****"
print "Version_Task: %s" % vt
for concept in d.get("concepts"):
attributes = d[ "%s_c%s" % (vt,concept) ]
print "Concept %s: %s" % (concept,attributes)
for i,attr in enumerate(attributes):
p[ "concept%s_att%s" % (concept,i+1) ] = res[ "%s_att%s_level%s" % (prefix,i+1,attr) ]
p[ "dcmLegend_att%s" % (i+1) ] = res[ "%s_legend%s" % (prefix,i+1) ]
</exec>
<exec when="init">
Q24_dcm = setupDCMFile("design.dat")
</exec>
<quota label="Q24_quota" overquota="noqual" sheet="Q24_DCM"/>
<number label="Q24_Version" size="3" optional="1" verify="range(1,50)" where="execute">
<title>Q24 - DCM Version</title>
<exec>
print p.markers
for x in p.markers:
if "/Q24_DCM/ver_" in x:
Q24_Version.val = int(x.split("_")[-1])
break
</exec>
</number>
<suspend/>
<res label="Q24_legend1" >Genre</res>
<res label="Q24_legend2" >Additional Story Content</res>
<res label="Q24_legend3" >Additional Game Levels</res>
<res label="Q24_legend4" >Additional Characters</res>
<res label="Q24_legend5" >Additional In-Game Items</res>
<res label="Q24_legend6" >Additional Cosmetic Items</res>
<res label="Q24_legend7" >Additional Boosters</res>
<res label="Q24_legend8" >In-game Currency</res>
<res label="Q24_legend9" >Exclusive Items</res>
<res label="Q24_legend10">Ads</res>
<res label="Q24_legend11">Subscription</res>
<res label="Q24_legend12">Upfront Game Client Cost</res>
<res label="Q24_att1_level1">First-person shooter (FPS)</res>
<res label="Q24_att1_level2">Third-person shooter (TPS) / Action</res>
<res label="Q24_att1_level3">Role-playing (RPG)</res>
<res label="Q24_att1_level4">Strategy</res>
<res label="Q24_att1_level5">Simulation</res>
<res label="Q24_att1_level6">Casual / puzzle / card</res>
<res label="Q24_att1_level7">Real-time strategy</res>
<res label="Q24_att1_level8">Sports</res>
<res label="Q24_att1_level9">Music</res>
<res label="Q24_att1_level10">Fighting</res>
<res label="Q24_att1_level11">Massive online battle arena (MOBA)</res>
<res label="Q24_att2_level0"> </res>
<res label="Q24_att2_level1"> </res>
<res label="Q24_att2_level2">Additional story content is available for purchase in a cash shop</res>
<res label="Q24_att3_level0"> </res>
<res label="Q24_att3_level1"> </res>
<res label="Q24_att3_level2">Additional levels of gameplay are available for purchase in a cash shop</res>
<res label="Q24_att4_level0"> </res>
<res label="Q24_att4_level1"> </res>
<res label="Q24_att4_level2">Additional characters are available for purchase in a cash shop</res>
<res label="Q24_att5_level0"> </res>
<res label="Q24_att5_level1"> </res>
<res label="Q24_att5_level2">In-game items like weapons, vehicles and equipment are available for purchase in a cash shop</res>
<res label="Q24_att6_level0"> </res>
<res label="Q24_att6_level1"> </res>
<res label="Q24_att6_level2">Cosmetic items and customizations for you characters are available for purchase in a cash shop</res>
<res label="Q24_att7_level0"> </res>
<res label="Q24_att7_level1"> </res>
<res label="Q24_att7_level2">Boosters and power-ups are available for purchase in a cash shop</res>
<res label="Q24_att8_level0"> </res>
<res label="Q24_att8_level1">The game features in-game currency that can only be earned</res>
<res label="Q24_att8_level2">The game features in-game currency that can be earned or purchased with real money</res>
<res label="Q24_att8_level3">The game features an in-game currency that can only be earned and a separate currency that can only be purchased with real money</res>
<res label="Q24_att9_level0"> </res>
<res label="Q24_att9_level1"> </res>
<res label="Q24_att9_level2">The game features exclusive in-game items that can only be purchased with real money</res>
<res label="Q24_att9_level3">The game features exclusive in-game items that can be either earned or purchased with real money</res>
<res label="Q24_att10_level0"> </res>
<res label="Q24_att10_level1">The game is free of all advertising</res>
<res label="Q24_att10_level2">The game features advertising </res>
<res label="Q24_att11_level0"> </res>
<res label="Q24_att11_level1">There is no subscription required to play</res>
<res label="Q24_att11_level2">The game requires you to pay a subscription each month in order to play</res>
<res label="Q24_att12_level1">Game is free</res>
<res label="Q24_att12_level2">Game is available for $2.50</res>
<res label="Q24_att12_level3">Game is available for $5</res>
<res label="Q24_att12_level4">Game is available for $10</res>
<res label="Q24_att12_level5">Game is available for $15</res>
<res label="Q24_att12_level6">Game is available for $20</res>
<res label="Q24_att12_level7">Game is available for $25</res>
<res label="Q24_att12_level8">Game is available for $30</res>
<res label="Q24_att12_level9">Game is available for $40</res>
<res label="Q24_att12_level10">Game is available for $50</res>
<res label="NoneText">None of these</res>
<res label="TopText">Concepts</res>
<res label="rowText">Select one option</res>
<exec>p.startTime = timeSpent()</exec>
<loop label="Q24_dcm_loop" vars="task" randomizeChildren="0">
<title>Q24 - DCM Loop</title>
<block label="Q24_dcm_block" randomize="0">
<radio label="Q24_[loopvar: task]" optional="0" surveyDisplay="desktop"
ss:questionClassNames="Q24_dcm">
<title>DCM Title [DCMcount]</title>
<alt>DCM Task: [loopvar: task]</alt>
<comment>Select one</comment>
<exec>
setupDCMItems( Q24_dcm, "v%s_t%s" % (Q24_Version.val,"[loopvar: task]"),"Q24" )
p.DCMcount = "%d" % (Q24_dcm_loop_expanded.order.index([loopvar: task]-1) + 1)
</exec>
<col label="c1">Concept 1</col>
<col label="c2">Concept 2</col>
<col label="c3">Concept 3</col>
<col label="c4">Concept 4</col>
<col label="c5" alt="None of these"/>
<style name="question.header" mode="before">
<![CDATA[
<style type="text/css">
/* add this only if you have scrollbars in IE7,8
div.Q24_dcm {
overflow: hidden;
}
*/
.Q24_dcm tr.legend th.legend {
font-weight: bold;
width: auto;
}
.Q24_dcm th, .Q24_dcm td {
padding: 15px;
}
.Q24_dcm tr.dcm_even {
background-color: #FFFFFF;
}
.Q24_dcm tr.dcm_odd {
background-color: #EFEFEF;
}
.Q24_dcm td.dcm_legend {
font-weight: bold;
text-align: left;
width: 120px;
}
.Q24_dcm tr.dcm_even td.dcm_item, .Q24_dcm tr.dcm_odd td.dcm_item {
text-align: center;
width: 120px;
}
</style>
]]>
</style>
<style arg:addNoneColumn="1" arg:attributes="12" arg:noneText="${res.NoneText}" arg:top="${res.TopText}" arg:yeslegend="1" name="question.top-legend">
<![CDATA[
\@if this.styles.ss.colLegendHeight
<tr class="legend top-legend${" GtTenColumns" if ec.colCount > 10 else ""} $(colError)" style="height:${this.styles.ss.colLegendHeight};">
\@else
<tr class="legend top-legend${" GtTenColumns" if ec.colCount > 10 else ""} $(colError)">
\@endif
\@if yeslegend == '1'
<th class="dcm_legend2">$(top)</th>
\@endif
$(left)
$(legends)
$(right)
</tr>
\@for x in range(1,int(attributes)+1)
<tr class="${'dcm_%s' % ['odd','even'][x % 2]}">
\@if yeslegend == '1'
<td class="dcm_legend">${p.get('dcmLegend_att%d' % x)}</td>
\@endif
<td class="dcm_item">${p.get('concept%d_att%d' % ([c.index+1 for c in p.get('shuffle-Col-%d' % this.uid) or this.cols][0],x) )}</td>
<td class="dcm_item">${p.get('concept%d_att%d' % ([c.index+1 for c in p.get('shuffle-Col-%d' % this.uid) or this.cols][1],x) )}</td>
<td class="dcm_item">${p.get('concept%d_att%d' % ([c.index+1 for c in p.get('shuffle-Col-%d' % this.uid) or this.cols][2],x) )}</td>
<td class="dcm_item">${p.get('concept%d_att%d' % ([c.index+1 for c in p.get('shuffle-Col-%d' % this.uid) or this.cols][3],x) )}</td>
\@if addNoneColumn == '1'
\@if x == 1
<td rowspan="${int($(attributes))}" style="border-top: none; text-align: center;"><b>$(noneText)</b></td>
\@endif
\@endif
</tr>
\@end
<tbody>
]]>
</style>
<style arg:row="${res.rowText}" arg:yeslegend="1" name="question.row">
<![CDATA[
\@if this.styles.ss.rowHeight
<tr class="$(style) colCount-$(colCount)" style="height:${this.styles.ss.rowHeight};">
\@else
<tr class="$(style) colCount-$(colCount)">
\@endif
\@if yeslegend == '1'
<td class="dcm_legend">$(row)</td>
\@endif
$(left)
$(elements)
$(right)
</tr>
]]>
</style>
<style arg:addNoneColumn="1" name="question.top-legend-item">
<![CDATA[
\@if this.styles.ss.colWidth
<th id="$(this.label)_$(col.label)" class="dcm_legend legend survey-q-grid-collegend $(col.styles.ss.colClassNames) ${col.group.styles.ss.groupClassNames if col.group else ""}" style="width:${this.styles.ss.colWidth}; min-width:${this.styles.ss.colWidth}">
$(text)
</th>
\@else
\@if addNoneColumn == '1' and col.index == (ec.colCount - 1)
<th id="$(this.label)_$(col.label)" style="border-bottom: none; width: 125px;" >
$(text)
</th>
\@else
<th id="$(this.label)_$(col.label)" class="legend survey-q-grid-collegend $(col.styles.ss.colClassNames) ${col.group.styles.ss.groupClassNames if col.group else ""}">
$(text)
</th>
\@endif
\@endif
]]>
</style>
</radio>
<suspend/>
</block>
<looprow label="1">
<loopvar name="task">1</loopvar>
</looprow>
<looprow label="2">
<loopvar name="task">2</loopvar>
</looprow>
<looprow label="3">
<loopvar name="task">3</loopvar>
</looprow>
<looprow label="4">
<loopvar name="task">4</loopvar>
</looprow>
<looprow label="5">
<loopvar name="task">5</loopvar>
</looprow>
<looprow label="6">
<loopvar name="task">6</loopvar>
</looprow>
<looprow label="7">
<loopvar name="task">7</loopvar>
</looprow>
<looprow label="8">
<loopvar name="task">8</loopvar>
</looprow>
<looprow label="9">
<loopvar name="task">9</loopvar>
</looprow>
<looprow label="10">
<loopvar name="task">10</loopvar>
</looprow>
<looprow label="11">
<loopvar name="task">11</loopvar>
</looprow>
<looprow label="12">
<loopvar name="task">12</loopvar>
</looprow>
<looprow label="13">
<loopvar name="task">13</loopvar>
</looprow>
<looprow label="14">
<loopvar name="task">14</loopvar>
</looprow>
</loop>
<float label="Q24_Timer" size="15" where="execute">
<title>Q24 - DCM Timer (Minutes)</title>
<exec>Q24_Timer.val = (timeSpent() - p.startTime) / 60.0</exec>
</float>
<note>DCM Question Template --End--</note>