How-to: LUA scripting in EFS



LUA scripting in EFS Survey filters and the LUA question type offer users with programming skills more flexibility and convenience. The LUA question type, found in "Advanced", provides an efficient scripting interface to the survey environment, allowing users easily recoding of the survey's variables, execution of complex calculations, verification or processing of user input and extended quota calculations. The question type provides two code boxes, the first one is executed before sending the page to the participant, the second code box is executed when a user submits the page containing the LUA question type. You can also include your own function libraries from the media library (global or project specific, text files with the extension .txt or .lua), allowing you to reuse frequently used functions across projects. Hiding conditions can be employed and you may define whether the script should be executed every time the page is called or only once (e.g. when using a page trigger or the back button).

Many filters which need a lot of effort when working with EFS standard filter definitions or alternative filter conditions can be realized more easily with the LUA filters. E.g. a function is available which simplifies handling of system missings in filter conditions.

This manual presents examples of frequent scenarios when programming with LUA in EFS and is not intended to be a general introduction to LUA, for which several good resources exist [https://www.lua.org/manual/5.1/, https://www.lua.org/pil/contents.html].

EFS Survey uses LUA Version 5.1 in its LUA filters and LUA question type and provides access to almost all default LUA libraries. For security reasons, functions dofile, load, loadfile, print, require, rawequal, rawget and rawest and the os library are not available in the LUA environment. The functions os.date and os.time are available as date and time. The function mb_strlen is available as string.len.

Access to the Lrexlib is possible and allows advanced Regular Expressions within the LUA environment to search, validate or extract user input, for example, rex_pcre.find (v_1, "\\bhate") will search for words starting with hate and return false if none is found. The full documentation on rex_pcre can be found in the Lrexlib reference manual.

Survey variables are available within the LUA environment, which is accessible under the _G global table. Most variables are therefore accessible through this global table, e.g. by using _G["v_1"] or _G["code"]. Survey variables (v_xxx) are also injected into the LUA sandbox as regular variables, therefore _G["v_1"] == v_1.

LUA Cheat Sheet

Please see https://www.lua.org/manual/5.1/ and https://www.lua.org/pil/contents.html for full documentation.

 

Comments

-- comment --[[ Multiline comment ]]

Variables

local x = 2 x, y = 2, 4

Tables / arrays

t = {} t = { a = 1, b = 2 } t.a = function() ... end -- tables can also contain functions t = { ["hello"] = 200 } t.hello -- arrays are also tables: array = { "a", "b", "c", "d" } setVariableValue("v_1",array[2]) -- "b" setVariableValue("v_2",#array) -- returns size of array (here: 4)

Conditions

Loops

Functions

Useful global functions

String API

Math API



EFS specific functions in LUA filters and LUA question type

Please note: Loop variables are not supported within the LUA environment.

setVariableValue(varName, varValue)

Assigns the given value to the specified v_, c_ or p_ variable. The variable can be anywhere in the questionnaire, shown or hidden. The LUA question type itself provides variables, which can be used to store information.

Data types

  • varName string - variable name

  • varValue string - variable value

setMasterDataValue(varName, varValue)

This function only executes in panel surveys and master data surveys. It assigns or overwrites the given value to the specified master-data variable of the current participant and stores it in the data base.

Data types

  • varName string - variable name

  • varValue string - variable value

setQuestionOutput(text)

Sets the output of the LUA-question. This function should only be used once, as subsequent calls overwrite previous values, also this function only generates output as part of code executed during rendering of the question.

Data types

  • text string - string to be shown in LUA-question

remove_sys_miss(varValue) 

Converts the system missing (code -77) to 0 in order to ease arithmetic operations. For example, when using the calculation based LUA filter "return v_1+v_2+v_3>10", System Missings (Code -77) will negatively affect the calculations, therefore "return remove_sys_miss(v_1)+remove_sys_miss(v_2)+remove_sys_miss(v_3)>10" can be used to avoid this issue.

Data types

  • varValue mixed - provided answer of a variable, e.g. remove_sys_missing(_G["v_1"])

  • returns int

get_range_count(values, min_code, max_code)

Returns how many times the values specified are in the range provided by min_code and max_code. For example, the condition return get_range_count({v_1,v_2,v_3,v_4,v_5},1,3)>2 is true, if the participant answered more than 2 times in the first three categories of the matrix question represented by v_1-v_5.

Data types

  • values array - List of values, e.g. {v_1, v_2, v_3, v_4, v_5}

  • min_code mixed

  • max_code mixed

  • returns int

getQuotaDebitValue(id)

Returns the quota's debit value.

Data types

  • id int - quota ID

  • returns int

getQuotaCurrentValue(id)

Returns the quota's current value, e.g. setVariableValue("v_1", getQuotaCurrentValue(1))

Data types

  • id int - quota ID

  • returns int

getQuotaFillingDegree(id)

Returns the quota's filling degree. E.g. setVariableValue("v_1", getQuotaFillingDegree(1)*100 .. " %")

Data types

  • id int - quota ID

  • returns double

getQuotaIds()

Returns an array of available quota IDs.

Data types

  • returns array

count(condition) 

Returns the number of participants which match the specified condition and can be used to display statistics to participants or dynamically routing in filters depending on answers given by other participants. WARNING! This function has impact on the performance of EFS and should only be used on surveys with low participations. For filing quotas, please use the built-in quota functionality in EFS as these are more efficient for this task.

Data types

  • condition string - conditional expression e.g. (v_1=1 AND v_2=2)

  • returns int or bool

in_list(list_id)

Checks if the current list_element is also contained in the specified list.

Data types

  • list_id string - Id of a list, e.g. "l_2"

  • returns bool

is_repeated_participation()

This function checks whether the participant has already taken part in the survey and is only applicable in Panel surveys (PA and MD).

Data types

  • returns int, 0 if false or 1 if true

is_assigned_in_or_below(check_metaname, metanames)

Checks whether the participant is part of the specified units or subunits in the org-structure of the ES project. Only applicable in Employee survey.

Data types

  • check_metaname string - metaname

  • metanames array - list of metanames the participant is assigned to

  • returns bool

is_assigned_in(check_value, codes)

Checks whether the participant's metaname code is assigned to the given unit metanames (array of codes). Only applicable in Employee survey.

Data types

  • check_value string - metaname

  • codes array - array of codes

  • returns bool

is_assigned_in_branch(check_metaname, metanames)

Checks whether the participant is assigned in part of specified units branch. Only applicable in Employee survey.

Data types

  • check_metaname string - metaname

  • metanames array - array of metanames the participant is assigned to

  • returns bool

check_character_filter_any(needle, haystack)

Checks whether the needle string is in the list provided in the haystack list of strings. Strings in haystack may be delimited by |, ",", ; or -.

Data types

  • needle string

  • haystack string

  • returns bool

date([format [, time]])

Replaces os.date() and returns a string or a table containing date and time, formatted according to the given string format. Please refer to os.date documentation.

time([table])

Replaces os.time() and returns the current time when called without arguments, or a time representing the date and time specified by the given table. Please refer to os.time documentation.

Examples for LUA in EFS

Block specific IPs from accessing a survey using a LUA filter

If you have a manageable list of IPs, which you want to block from accessing your survey, you can use a LUA filter to screen out these participants. This example code blocks the IPs 78.34.112.1, 78.34.112.2 and 78.34.112.3 and the list can be extended by providing additional comma-separated IPs. The last item should not have a comma after it.


Example survey structure

Example code

Show a item of the day to all participants

Select a random item from a list once per day, hour or minute and store it in variable v_1 for all participants for the defined time. The function math.randomseed sets the randomizing integer for the random function, by providing the same integer for other participants, the same random value will be returned. To get the size of an array, put # in front of the name, e.g. #randomRestaurants.

Example code

Detect first name, last name and company name from email address

This example takes the email address stored in v_1 and assigns the company, first and last name to variables v_10, v_11 and v_12. This works on email aliases using firstname.lastname@company.tld or firstname_lastname@company.tld as their format. If the first or last name are not detected, then the variables will not be assigned a value.  

Example code

Look up participant_country code in a list of countries

If you have enabled the setting "Detect participants' location using the IP address" in the survey's project properties, then EFS will store the code of the participants country in the variable participant_country. Currently, there is no possibility to recode this variable to another variable with the country's label. With the example code, the code will be looked up in an array and the country's label is stored in v_1. This can be used to pre-populate a text field when asking for a user's location and the example code can be adapted to match participant_country codes to codes in a select box for pre-selection.

Example code (long)

Get current GMT time

This code will return the time on server side in GMT and can be used for date related calculations.

Example code

Comparing the Frequency of completed Interviews

The following alternative filter condition checks which of several possibilities the participants preferred so far:

(count['v_1 = 1'] > count['v_2 = 1'] ) AND (count['v_1 = 1'] > count['v_3 = 1'] ) OR (count['v_2 = 1'] > count['v_1 = 1'] ) AND (count['v_2 = 1'] > count['v_3 = 1'] )

  • If at the point in time when a participant passes the filter, v_1 or v_2 were selected more frequently, the filter condition is fulfilled.

  • If v_3 has been selected more frequently, the condition does not apply for the current participant. 

Using LUA instead of the alternative filter function has several advantages:

  • Speed: Each count strains the server. In the alternative filter condition above, count requests are executed repeatedly for the same variable. In the LUA condition above, only one count request is necessary for each variable.

  • Shortness and overview: The more variables are to be checked, the longer and more confusing the alternative filter condition will become. In the LUA filter below, additional variables are simply added to the “configuration” section.

  • Easier to maintain and configure:
    – When making changes, all you need to do is modify the two arrays in the “configuration” section.
    – Non-programmers can re-use the condition, by doing simple copy&paste and a few minor modifications.

Example code

Comparing Answer Patterns

The filter checks if the sum of answers in a 1st branch equals the sum of answers in a second branch. In the following example, for the sake of briefness, only one of these sums is created. The survey contains variables in the range v_135 to v_173.

The syntax ( v_x > 0 ? 1 :0) used in the alternative filter condition ensures that hiding conditions and unanswered items are considered appropriately:

  • If the participant has answered v_x > 0. With v_x > 0 ? 1 the value is uniformly set to = 1.

  • If the participant has not seen or not answered an item, i.e. v_x smaller or equal 0, the corresponding factor will be set = 0. This is necessary because missing values, as e.g. -77, prevent a proper calculation of the sum.

(( v_135 > 0 ? 1 :0) + ( v_136> 0 ? 1 :0) + ( v_137 > 0 ? 1 :0) + ( v_138 > 0 ? 1 :0) +( v_139 > 0 ? 1 :0) +( v_140 > 0 ? 1 :0) + ( v_141 > 0 ? 1 :0) + ( v_142 > 0 ? 1 :0) + ( v_143> 0 ? 1 :0) + ( v_144 > 0 ? 1 :0) + ( v_145 > 0 ? 1 :0) + ( v_146 > 0 ? 1 :0) + ( v_147 > 0 ? 1 :0) + ( v_148 > 0 ? 1 :0) + ( v_149 > 0 ? 1 :0) + ( v_150 > 0 ? 1 :0) + ( v_151 > 0 ? 1 :0) +( v_152 > 0 ? 1 :0) +( v_153 > 0 ? 1 :0) +( v_154 > 0 ? 1 :0) +( v_155 > 0 ? 1 :0) +( v_156 > 0 ? 1 :0) +( v_157 > 0 ? 1 :0) +( v_158 > 0 ? 1 :0) +( v_159 > 0 ? 1 :0) + ( v_160 > 0 ? 1 :0) + ( v_161 > 0 ? 1 :0) + ( v_162 > 0 ? 1 :0) + ( v_163 > 0 ? 1 :0) + ( v_164 > 0 ? 1 :0) +( v_165 > 0 ? 1 :0) +( v_166 > 0 ? 1 :0) +( v_167> 0 ? 1 :0) +( v_168 > 0 ? 1 :0) +( v_169> 0 ? 1 :0) + ( v_170 > 0 ? 1 :0) +( v_171 > 0 ? 1 :0) +( v_172 > 0 ? 1 :0) +( v_173 > 0 ? 1 :0)) [...etc]

With LUA, this can be achieved much easier, by creating a for loop and passing a counter ranged from 135 to 173, alternatively an array of specific variable ids could be supplied to the for loop.

Example code

My code is not working, what should I do?

First of all, check if there are any trigger errors reported on the survey menu, EFS does not display LUA errors to survey participants. You can also go to Test and validation -> Project check, click on errors in "Errors while processing the survey" to get a list of all trigger errors in your survey. Also check the execution time of the LUA code, whether it is processed when the page is loaded or only when the page is submitted, and whether all required variables have already been filled in by the participant when the LUA code is executed.

If this does not help use external LUA sandboxes where debugging is easier, for example online at repl.it or in a local LUA 5.1 sandbox on your computer. You will have to create any EFS variables or EFS-specific functions in the external LUA sandbox, see the end of this page for an example.

Please note, that Questback's EFS support team is unable to assist you in bug fixing your LUA code.

Useful LUA resources

  • LUA 5.1 Reference Manual

  • Programming in LUA 5.0, free for personal use

  • Description of mathematical functions in the math library

  • Lrexlib reference manual (Regular Expressions)

  • Repl.it provides a convenient LUA 5.1 online environment for writing and debugging LUA code in a controlled environment. Some EFS functions can be emulated by rewriting them, see example below.

    Example functions for use in repl.it



© 2024 Tivian XI GmbH