-
Notifications
You must be signed in to change notification settings - Fork 5
Survey definition documentation
To create a new survey, navigate to https://home.ctagroup.org/surveys and click on** New Survey:**
When creating a survey, you need to specify it’s title (text) and a survey definition, which contains survey elements (questions, sections, etc.) and survey logic. A survey definition must be in a valid YAML format.

When working with surveys, it’s convenient to open 2 browser windows: the first one with the survey preview, and the other one with the editor. Each time you hit the Submit button, the preview will be updated automatically:

Let’s create a basic survey with a single question:
variables: {}
items:
- id: question1
type: question
title: What is your name?
category: text
The items object holds an array of survey items, each of them must have an id and type. Id is needed to refer to survey items and must be unique in the survey, i.e. if you want to check for question value. If you click Save now, a survey will be uploaded to HSLYNK and you will notice that question1 received a new property: hmisId - this is a HSLYNK question id for a question, which is unique across all surveys. When the survey is submitted, each question response is sent to a specific hmisId question. This allows us to share questions between surveys. For example, there could be a specific HMIS question for an age which will collect respondents age across different surveys (assuming that all those questions share the same hmisId).
Let’s add more questions:
variables: {}
items:
- id: question1
type: question
title: What is your name?
category: text
hmisId: e8b2b1a9-8bc8-4658-8e8c-982b6b5af61b
- id: question2
type: question
title: Choice question
category: choice
options:
'1': Yes
'0': No
Now we have created a choice question, note that it has additional property options. It this case we have created a dictionary with values 0 and 1 and labels Yes and No.
Key value pairs can also be expressed using the following notation:
- id: question2
type: question
title: Choice question
category: choice
options:
- '1|Yes'
- '0|No'
There is an alternative way of providing options if values and labels are the same:
options:
- 'Yes'
- 'No'
- 'Maybe'
Note that when submitting a survey response, question values (not labels) are collected.
Let’s add another item, this time it’s a text item:
items:
...
- id: openingScript
type: text
title: Opening Script
text: >-
<p>My name is <strong style="color: rgb(83, 129, 53);">(your name)...</p>
Text items are useful if you want to provide guidelines or additional information for surveyed person.
Properties: title, text
title: the highlighted title may contain placeholders (using {{ }} quotes, see expression evaluation)
text: Additional text, can he HTML, may contain placeholders
variables[id].hidden - will hide this item
Properties: title, text, category, options, refusable, mask
title: Question title
text: Optional question description
category required, can be one of: text, choice, date, number, currency
choices: (required for choice category), can be a list of strings, or object with key-value pairs
refusable: set to true , if client can refuse this question. Question value will be Refused. If you need to set different refuse value, you can set as a value of refusable property, i.e. refusable: 80 will set 80 as a response value for this question.

other: set to true, if client can specify custom value for choice question. If you need to display different placeholder for the Other option, you can set it to a value of other property, i.e. other: Your answer will display Your answer as a placeholder.

values[id] - will hold value for a question
props[id].skip - will disable this question
Properties: title, rows, columns
title: Grid title
columns: List of columns, each column is an object with fields: id, type, title, category
rows: Number of rows in the grid
Example:
- id: children
title: '4. Please provide a list of children’s names and ages:'
type: grid
rows: 8
columns:
- id: children.firstName
type: question
title: First Name
category: text
- id: children.age
type: question
title: Age
category: number
- id: children.dob
type: question
title: Date of Birth
category: date
props[id].skip - will disable this question
props[id].rows - will control the number of rows
Properties: text, score
score: A value or an expression to evaluate (i. e. variable name)
text: A text to display
Example:
- id: score1
type: score
score: variables.score
text: Your score is
Properties: skip, title
title: Section title
items: A list of survey items under this section may recursively contain another section
skip: Set it to true to display Skip this section checkbox, or set it to a string to provide a custom label for a checkbox.
Example:
- id: parent2
type: section
title: Parent 2
skip: Second parent currently not part of the household
items:
- id: parent2.firstName
type: question
category: text
title: First Name
- id: parent2.nickName
type: question
category: text
title: Nickname
props[id].skip controls the on/off state of a ‘Skip this section’ checkbox.
This item will not be displayed, may be used as a container for survey logic.
Survey state can be stored using variables. Consider the following survey:
variables:
score1: 1
score2: 2
items:
- id: item1
type: text
title: Your scores
text: 'Your score1 is {{variables.score1}} and score2 is {{variables.score2}}'
In item1 text {{variables.score1}} is evaluated to 1 and {{variables.score2}} to 2. You can use variables to check for conditions (i. e. if question has a particluar value) and you can use them is actions (set item visibility according to a value of a variable)

In many places, i.e. in text items, rule conditions and actions you can use expression evaluation to get the value of a question, variable, or other object (i.e. a client). The following example illustrates how to get display currently surveyed client name:
id: clientName
type: text
title: Client name
text: '<div class="readonly">{{client.name}}</div>'
In general, you can access all data visible in suervey debug tool:
- Variables: i.e.
variables.someVariable - Question values: i.e.
values.questionId - Client information: i.e.
client.name - User information: i.e.
user.fullName - Project information
- Enrollment information
Note that you can apply reducers to expressions, i.e. client.dob:age will return client’s age (see reducers for a reference).
Each survey item can contain rules property, which holds a list of rules. Each rule has conditions (which check if certain actions should be triggered), and actions (which change the survey state, i.e. show/hide questions, sets variables, etc.). Rules are fired from top to bottom of the survey definition, on every user input. Let’s start with an example:
variables:
message: 0
items:
- id: question1
type: question
category: number
title: 2 + 2 = ?
- id: text1
type: text
text: 'You typed {{values.question1}} and your answer is {{variables.message}}'
rules:
- always:
- - set
- message
- incorrect
- any:
- - ==
- values.question1
- 4
then:
- - set
- message
- correct
The logic is as following:
- The first rule (
always) fires, and it setsmessagevariable toincorrectvalue - The second rule (
any) evaluates its conditions. There is only one condition to check (ifvalues.question1== 4) and if the check is successful, then rule actions are executed (there is only 1 action - setmessagevariable tocorrectvalue)
always: this rule is always fired (no conditions are required). For example:
- always:
- [set, someVariable, 4]
any: this rule fires all its actions if any condition is satisfied. For example:
- any:
- - <=
- values.client.age
- 60
then:
- - set
- score1
- 1
all: this rule fires if conditions are satisfied. For example:
- all:
- - isset
- values.question1
- - '!='
- values.question1
- Refused
- - '!='
- values.question1
- Shelters
- - '!='
- values.question1
- Transitional Housing
- - '!='
- values.question1
- Safe Haven
then:
- - set
- scoreA1
- 1
All available actions are listed below. Actions must be placed as a list under always rule or as a then part of any/all rule.
show: Shows an item with a specific id (i.e. a text item)
hide: Hides an item with a specific id
set, vset: Sets a variable to a value (or a result of an expression). For example:
- [set, someVariable, 4]
- [set, youngestChildAge, 'values.children.age:min']
pset: sets a survey property. For example:
- [pset, section1.skip, 1]
- [pset, gridQuestion1.rows, 10]
add: the first argument is a variable, which will be incremented by a second argument (a number or a result of an expression):
- [add, someVariable, 1]
sum: similar to add, but can have multiple arguments:
- [sum, numChildren, values.childrenNear, values.childrenFar]
rows: sets a number of rows for an item containing this action:
- [rows, 10]
email: sends an email using a provided template
- [email, emailTemplate1, recipient, cc, bcc]
When any or all rule is used it has a list of conditions. For example:
- id: scoreA2
type: score
score: variables.scoreA2
text: >-
IF THE FAMILY HAS EXPERIENCED 1 OR MORE CONSECUTIVE YEARS OF
HOMELESSNESS, AND/OR 4+ EPISODES OF HOMELESSNESS, THEN SCORE 1.
rules:
- any:
- - '>='
- values.question6
- 1
- - '>='
- values.question7
- 4
then:
- - set
- scoreA2
- 1
isset: evaluates to true if an expression has a value. Example conditions:
- [isset, values.question1]
- [isset, variables.someVariable]
==: checks for equality. Example conditions:
- ['==', values.question5, Shelters]
!=: checks for inequality. Example conditions:
- ['!=', values.question5, Shelters]
<: checks for less. Example conditions:
- [isset, values.question1]
- [isset, variables.someVariable]
<=: checks for less or equal. Example conditions:
- ['<=', variables.youngestChildAge, 12]
>: checks for greater. Example conditions:
- ['>', variables.youngestChildAge, 12]
>=: cChecks for greater or equal. Example conditions:
- ['>=', values.someValue, 12]
When evaluating an expression, reducers can be used to transform the value of an expression. Reducers can be chained using colon (:) separator.
Available reducers: min, max, date, age, digits, last, now, addDays
min/max: calculates min/max value for a grid column
age: calculates age (based on current system date, in years) from a value which is a date or timestamp
digits: extracts digits from a string
last: extracts last n characters from a string
date: converts a timestamp to a date, useful for displaying information in text items (client.dob is a timestamp), i.e:
id: parent1.dob
type: text
title: Date of Birth
text: '<div class="readonly">{{client.dob:date}}</div>'
now: ...
addDays: adds a number of days to a varaible holding a date value
Some other examples:
- [set, oldestChildAge, 'values.childrenGrid.ageColumn:max']
- [set, 'parent1.age', 'client.dob:age']
When surveying a client, a survey logic has access to contextual data: client, user, project, enrollmentInfo. You can use this data to display certain information using text items or use it in survey logic (i.e. to display information for a specific gender). Use survey debug tool to access all available contextual information.
When creating a survey, some of the responses can be prepopulated with initial data. To do so, you should add initialValues to survey definition with keys corresponding to question ids and values corresponding to prepopulated answers. For example:
variables: {}
initialData:
question1: John
question2: user.fullName
items:
- id: question1
type: question
title: What is your name?
category: text
- id: question2
type: question
title: Interviewer name
category: text
would result in setting the first answer as John and a second answer as the current user's name. Note that initial values are ignored when editing an existing survey.
It is convenient to use choice keys for scoring purposes. For example, in question1 a score of 1 is set for Yes, and 0 for No (see below). In some cases, multiple choices can have the same score but the keys must be unique. To overcome this limitation, a choice key can be prefixed with a letter (as in question2), and :digits reducer can be used to extract the score from the selected option (see totalScore).
items:
- id: question1
type: question
title: First question
category: choice
options:
- 1|Yes
- 0|No
- id: question2
type: question
title: First question
category: choice
options:
- a1|Yes
- b0|No
- c0|'Don't know/Refused'
- id: totalScore
type: score
text: Total score
score: variables.total
rules:
- always:
- [sum, total, values.question1, values.question2:digits ]