Skip to main content

FV Decipher Support

All the topics, resources needed for FV Decipher.

 
FocusVision Knowledge Base

Exec Tag: Execute Python Code

1:  Overview

The <exec> tag executes Python code. It is commonly used to set question values, modify the contents of question elements, print debugging information, create persistent functions, variables and data structures to be used within the survey, and perform various tasks related to quotas and multi-language surveys.

The Exec Element is available in the survey builder, too!

Take a look at the example code below to see how the <exec> tag can be used.

<exec when="init">
# function to check if age is 18 or older
def is_18_or_older(age_question):
    return 1 if age_question.ival ge 18 else 0
</exec>

<number label="Q1" size="3" verify="range(1, 120)">
  <title>
    Please enter your age below.
  </title>
</number>
<suspend/>


<radio label="vAge" where="execute">
  <exec>
for eachRow in vAge.rows:
    age_range = eachRow.o.alt # never set value using .o
    if Q1.check(age_range):
        vAge.val = eachRow.index
        break
  </exec>
  <title>
    Age Group (Hidden Question)
  </title>
  <row label="r1" alt="1-17">1-17</row>
  <row label="r2" alt="18-24">18-24</row>
  <row label="r3" alt="25-34">25-34</row>
  <row label="r4" alt="35-44">35-44</row>
  <row label="r5" alt="45-54">45-54</row>
  <row label="r6" alt="55-64">55-64</row>
  <row label="r7" alt="65-120">65-120</row>
</radio>

<exec>vOver18.yes.val = is_18_or_older(Q1)</exec>
<checkbox label="vOver18" atleast="0" where="execute">
  <title>
    Respondent is Over 18? (Hidden Question)
  </title>
  <row label="yes">Yes</row>
</checkbox>
<suspend/>

<exec cond="vOver18.yes">
setMarker('Adult')
</exec>

In the code above, we used an <exec> tag to create the function is_18_or_older(), set the values for the hidden question vAge and vOver18, and set a marker called "Adult" which only applies to those who specified they were 18 or older.

2:  Available <exec> Attributes

The <exec> tag has two optional attributes:

2.1:  when - Control When the Exec Should Run

The when attribute is used to control when the <exec> tag should be executed. This can be to set to one of the following: survey, started, init, virtualInit, finished, returning, verified, virtual, flow, sqlTransfer or sqlTransferInit.

Continue reading to learn when each of these settings will run the <exec> element.

2.1.1:  when="survey"

This is the default setting for the when attribute, so you do not need to include it. By default, the <exec> tag will execute just like any other question, when the respondent reaches its location within the survey. Consider the example below:

<checkbox label="Q1" atleast="1">
  <title>
    Hello
  </title>
  <row label="r1">World</row>
</checkbox>
<suspend/>

<exec>
Q1.title = "Goodbye"
Q2.title = "Goodbye"
</exec>

<checkbox label="Q2" atleast="1">
  <title>
    Hello again
  </title>
  <row label="r1">World</row>
</checkbox>

In the example code above, the <exec> block appears just after the first <suspend/> tag. The code executed updates the title text for both questions Q1 and Q2. However, when testing this code, you'll notice that the Q1 title text is not visibly altered, but Q2's title text is updated to "Goodbye". This is because when="survey" is set and the <exec> tag is not executed until the second page of the survey (after Q1).

2.1.2:  when="started"

If when="started" is specified, the <exec> tag will execute as soon as the respondent enters the survey. It does not matter where the <exec> is located within the survey.xml. Let's apply this attribute to the same example as shown above:

<checkbox label="Q1" atleast="1">
  <title>
    Hello
  </title>
  <row label="r1">World</row>
</checkbox>
<suspend/>

<exec when="started">
Q1.title = "Goodbye"
Q2.title = "Goodbye"
</exec>

<checkbox label="Q2" atleast="1">
  <title>
    Hello again
  </title>
  <row label="r1">World</row>
</checkbox>

Since we've specified when="started", both Q1 and Q2's title text will be updated and you will see the title text for both questions visibly altered. This is because the <exec when="started"> block is executed as soon as the survey is loaded by the respondent.

Strings declared inside an <exec> tag will not be detected by the language system and will not get translated (e.g. "Goodbye" will always be seen in English). It is best practice to use a <res> tag for any text you will be reusing.

2.1.3:  when="init"

If when="init" is specified, the <exec> tag is executed once and only once when the survey is loaded. There is no respondent information at this point, so you cannot access extra variables nor can you modify or access question content. The previous example (updating question title text) would not work when when="init" is specified. However, this attribute setting is commonly used to initialize databases, declare persistent variables and create functions that you wish to use across your entire survey for all respondents. Consider the following example:

<exec when="init">
myDatabase = File("dbfile.dat", "source")
badZipCodes = ["93611", "93720", "90210"]
</exec>

<text label="Q1" optional="0" verify="zipcode">
  <title>What is your ZIP code?</title>
</text>

<term cond="Q1.val in badZipCodes">Q1: Bad Zip Code</term>

<pipe label="respName">
  <case label="c1" cond="myDatabase.get(source)">${myDatabase.get(source)["name"]}</case>
  <case label="c2" cond="1">friend</case>
</pipe>

<html label="Introduction" where="survey">
  Welcome to the survey, [pipe: respName]!
</html>

In the example above, the <exec when="init"> block is ran when the survey is loaded into the system. Variables declared here will apply to all respondents and accessible throughout the entire survey.

In the <exec when="init"> block above, we created two variables: one to reference a static database file using the File() function and the other to contain a list of invalid zip codes. These static variables are now ready to be used for the entire survey.

A persistent variable is stored inside a Python dictionary and can be set or read using normal Python dictionary syntax. Persistent variables will be retained in memory and accessible throughout the duration of the survey.

2.1.4:  when="virtualInit"

If when="virtualInit" is specified, the <exec> tag is executed when running reports and is used to populate virtual questions. Similar to when="init", when="virtualInit" code will be usable for virtual questions across your entire survey. This is commonly used to merge additional files into the survey, populate virtual questions based on post-hoc data, clean up data by repopulating virtual questions and to create custom reports. Consider the following example:

<exec when="virtualInit">
# Custom Report TERMINATE Specifications
TERM_MARKERS = ('term:(SA.r1)','term:(not SD1.r2)')

def is_valid_term():
    marker_string = ''.join(markers)
    if 'term:' in marker_string:
        for tm in TERM_MARKERS:
            if tm in marker_string:
                return True  # valid term
        return False  # invalid term
    return True  # no term
</exec>

<radio label="vGenderAge">
<virtual>
if 'OQ' not in markers and is_valid_term():
    #male respondent:
    if SB.r2:
        if SC.check('13-17'):
            vGenderAge.val = vGenderAge.r1.index
        elif SC.check('18-24'):
            vGenderAge.val = vGenderAge.r2.index
        elif SC.check('25-34'):
            vGenderAge.val = vGenderAge.r3.index
        elif SC.check('35-44'):
            vGenderAge.val = vGenderAge.r4.index
        elif SC.check('45-54'):
            vGenderAge.val = vGenderAge.r5.index
    #female respondent:
    elif SB.r1:
        if SC.check('13-17'):
            vGenderAge.val = vGenderAge.r6.index
        elif SC.check('18-24'):
            vGenderAge.val = vGenderAge.r7.index
        elif SC.check('25-34'):
            vGenderAge.val = vGenderAge.r8.index
        elif SC.check('35-44'):
            vGenderAge.val = vGenderAge.r9.index
        elif SC.check('45-54'):
            vGenderAge.val = vGenderAge.r10.index
</virtual>
  <title>
    Gender / Age
  </title>
  <row label="r1">Male 18-24</row>
  <row label="r2">Male 25-34</row>
  <row label="r3">Male 35-44</row>
  <row label="r4">Male 45-54</row>
  <row label="r5">Male 55-65</row>
  <row label="r6">Female 18-24</row>
  <row label="r7">Female 25-34</row>
  <row label="r8">Female 35-44</row>
  <row label="r9">Female 45-54</row>
  <row label="r10">Female 55-65</row>
</radio>

If you've ever had to create a custom report for the "Custom" tab found in the field report, then the code above may look familiar. The <exec when="virtualInit"> block at the beginning creates a function, is_valid_term(), that is used at the vGenderAge virtual question.

The function, is_valid_term() checks the markers string against the strings provided in TERM_MARKERS and returns True if the respondent did terminate based on the criteria provided or did not terminate at all. If the respondent is terminate who terminated other than what's shown in TERM_MARKERS, then False is returned.

The question vGenderAge is an accumulation of all respondents split into their gender and age groups. Question SB is a gender question and SC is an age question. The virtual question references these questions to populate the radio question according to the data they provided.

2.1.5:  when="finished"

If when="finished" is specified, the <exec> block will execute once just after the respondent has submitted the survey and before the results are written to the disk. This is often used to add final data to a database and record any remaining data you may want to store (e.g. total time the respondent spent taking the survey, IP address, etc...). Consider the example below:

<html label="Introduction" where="survey">
    <p>Welcome to the survey! Please continue.</p>
</html>
<suspend/>

<text label="User_ID" size="10" optional="0" verify="len(3,3)">
    <title>You should have received an ID number in the email sent to you. Please enter it below.</title>
</text>
<suspend/>

<exec when="init">
dbUserIDs = Database("user_ids.txt")
</exec>

<html final="1" cond="not dbUserIDs.valid(User_ID.val)" label="invalid_id_provided" where="survey">
    <p>${hlang.get("invited.not")}</p>
</html>

<html final="1" cond="dbUserIDs.has(User_ID.val)" label="id_already_completed" where="survey">
    <p>${hlang.get("invited.used")}</p>
</html>
<suspend/>

<html label="Screener_Introduction" where="survey">
    <p>Thank you! We have a few questions for you...</p>
</html>
<suspend/>

<exec when="finished">
dbUserIDS.add(User_ID.val)
</exec>

In the example above, we use a database to validate the respondent's ID entered at question User_ID. Using <exec when="init">, we initialize a database called dbUserIDs that references a text file named "users_ids.txt". In this file are the numbers 100 - 199 on separate lines. At the end, the <exec when="finished"> block adds the ID number the respondent provided at the User_ID question.

The <exec when="finished"> can be placed anywhere in the XML. It's called only when the respondent submits the survey.

This means that as soon as the respondent clicks the "Finish" button, the ID number provided will be stored in the database and no other respondents will be allowed to enter using this value. The <html> elements in between check whether the respondent provided an acceptable value (present in the "user_ids.txt" file) or whether someone has already completed using that ID. If True to either check, the respondent will be blocked from taking the survey (e.g. final="1").

2.1.6:  when="returning"

If when="returning" is specified, the <exec> block will execute when the respondent is returning after being redirected using a suspended redirect. This is especially useful when the respondent leaves the survey to answer to take a separate Sawtooth survey and returns to complete the original survey. Consider the following example:

<html label="Intro_Redirect" where="survey">
  <p>
    Please hit "Continue" to be redirected.
  </p>
</html>
<suspend/>

<suspend url="./proj1234?variable=foo" />

<html label="Welcome_Back" where="survey">
    <p>
      Welcome back!
    </p>
</html>

<radio label="Q1">
  <title>
    Test Question (Auto Punched when="returning")
  </title>
  <comment>Please select one</comment>
  <row label="r1">1</row>
  <row label="r2">2</row>
  <row label="r3">3</row>
</radio>

<textarea label="Q2" optional="0">
  <title>
    Variable's Value (Auto Populated when="returning")
  </title>
</textarea>
<suspend/>


<exec when="returning">
Q1.val = Q1.r2.index
Q2.val = str([(x, urllib.quote(y[0])) for (x, y) in gv.request.rawVariables.items()])
</exec>

In this example, the <exec when="returning"> block is executed after the respondent returns from being redirected out of the survey. For demonstration purposes, the redirect leads back to the same survey. When the respondent enters the survey (again), the <exec> block executes, auto-populating Q1.r2 and Q2 with values for all variables passed back through the URL.

More often than not, variables from the external survey are passed back through the URL and you can use this information to create additional logic for the current survey.

2.1.7:  when="verified"

If when="verified" is specified, the <exec> block will execute each time the respondent has successfully submitted verified data. This is helpful if you're making calls to an external API each time a question has been answered properly. Consider the following example:

<exec>
p.questionsAnswered = 0
</exec>

<exec when="verified">
p.questionsAnswered += 1
</exec>

<text label="Q1" optional="0">
  <title>
    Test (Questions answered = ${p.questionsAnswered})
  </title>
</text>
<suspend/>

<text label="Q2" optional="0">
  <title>
    Test (Questions answered = ${p.questionsAnswered})
  </title>
</text>
<suspend/>

<text label="Q3" optional="0">
  <title>
    Test (Questions answered = ${p.questionsAnswered})
  </title>
</text>
<suspend/>

In the example above, questionsAnswered is set to 0 when the survey is loaded. After each successful submission, the number will increment and display in the question title. It works as expected, displaying the title "Test (Questions answered = 2)" at Q3.

2.1.8:  when="virtual"

If when="virtual" is specified, the <exec> block will run once for each respondent when calculating <virtual> questions. This is often used to set up common code and variables to be shared between multiple virtual questions. Consider the following example:

<radio label="Q1">
  <title>
    Yes or No?
  </title>
  <comment>Please select one</comment>
  <row label="r1">Yes</row>
  <row label="r2">No</row>
</radio>
<suspend/>

<exec when="virtual">
attitude = "Positive" if Q1.r1 else "Negative"
</exec>

<text label="vQ1" optional="0">
  <title>
    Respondent appears to be very...
  </title>
  <virtual>vQ1.val = attitude</virtual>
</text>

<radio label="vQ1_Counts">
  <title>
    Attitude Counts
  </title>
  <row label="r1">Positive</row>
  <row label="r2">Negative</row>
  <virtual>
for eachrow in vQ1_Counts.rows:
    if eachrow.text == attitude:
        vQ1_Counts.val = eachrow.index
  </virtual>
</radio>

In this example, both vQ1 and vQ1_Counts use the same variable declared in the when="virtual" <exec> block. The where="virtual" <exec> block does have access to survey questions and variables, making it super useful for adding additional logic to your <virtual> questions. The where="virtualInit" <exec> block, on the other hand, does not have access to question variables or data, and can only be used to setup variables and structures to be used in <virtual> questions directly.

2.1.9:  when="flow"

If when="flow" is specified, the <exec> block will execute when "One Page" mode has been toggled. This is often used to set default values for persistent variables declared in other <exec> blocks that would cause errors to the survey when in "flow" mode. Consider the example below:

<exec>
logStuff = False
</exec>

<exec when="flow">
logStuff = True
</exec>

<style name='respview.client.js' mode="after" wrap="ready"> <![CDATA[
\@if logStuff
console.log("Stuff");
\@endif
]]></style>

<html label="Introduction" where="survey">
  <p>
    Hello!
  </p>
</html>

In the example above, "Stuff" is logged to the console only when "One Page" mode has been toggled on. Since not all browsers support logging to the console, this is one way we can provide helpful debugging information about the survey.

If your QA team gets an error in flow mode, it's likely that you'll need to declare default values for persistent variables using when="flow".

2.1.10:  when="sqlTransfer"

This is only used for very specific panel surveys.

If when="sqlTransfer" is specified, the <exec> block will execute when copying the data from the survey's SQL database into results.bin. Consider the following example:

<exec when="sqlTransfer">
# Only active members are qualified
markers = [x for x in markers.split(',') if x != 'qualified']
if status == 'active':
  markers += ['qualified']
markers = ','.join(markers)
</exec>

In this example, the markers are modified to only qualify those who are active members of the panel.

2.1.11:  when="sqlTransferInit"

This is only used for very specific panel surveys.

If when="sqlTransferInit" is specified, the <exec> block will be executed to initialize data for the "sqlTransfer" <exec> block mentioned above. For example, to fetch all subpanel membership status. Consider the following example:

<exec when="sqlTransferInit">
from hermes import database
panelid = database.fetchonecol("SELECT id FROM pm.panel WHERE name='panelName'")
</exec>

In this example, we create a list of id's from the panel database to be used later on in a "sqlTransfer" <exec> block.

2.1.12:  when="submit"

If when="submit" is specified, the <exec> block will execute each time the "Continue"/"Finish" button is pressed and after a <validate> runs within a question element.

For example, if we wanted to correct a response that was over the acceptable limit to the maximum amount, we could using the when="submit" attribute:

<number label="Q1" size="3">
  <title>How many hours in a week do you read?</title>
  <validate>
if Q1.val gt 168:
    error("Value must be no greater than 168 (you entered %d)" % Q1.val)
  </validate>
</number>

<exec when="submit">
if Q1.val gt 168:
    Q1.val = 168
</exec>

In the example above, the <validate> ensures that the number supplied is not greater than 168. If the value is greater than 168, the <exec when="submit"> block will reset the value to the maximum amount available.

2.2:  cond - Set the Condition to Run the Code

The cond attribute controls when the <exec> block should be ran. If the condition evaluates to True, the <exec> block will be executed. For example:

<exec cond="1">
Q1.title = "Overridden by block #1"
</exec>

<exec cond="0">
Q1.title = "Overridden by block #2"
</exec>

<text label="Q1" optional="0" size="10" title="Default title" />

<exec cond="Q1.val == 'Something'">
setMarker("saidSomething")
</exec>

As you may have guessed, Q1's title will display as "Overridden by block #1" since the second block is not ran due to the False (0) condition. The third <exec> block will only set the marker "saidSomething" if Q1's value is "Something".

Use the cond attribute to run <exec> blocks based on external conditions.

3:  What's Next?

Take a look at these various Python Functions that are available to use within <exec> blocks.

Learn more about the Exec Element available in the survey builder.