Skip to content

Survey definition documentation

Przemysław Górecki edited this page May 17, 2019 · 6 revisions

Introduction

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.

Available item types

Type: text

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

Type: question

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

Type: grid

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

Type: score

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

Type: section

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.

Type: hidden

This item will not be displayed, may be used as a container for survey logic.

Using variables

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)

Expression evaluation

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).

Survey logic

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:

  1. The first rule (always) fires, and it sets message variable to incorrect value
  2. The second rule (any) evaluates its conditions. There is only one condition to check (if values.question1 == 4) and if the check is successful, then rule actions are executed (there is only 1 action - set message variable to correct value)

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

Actions

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]

Conditions

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]

Reducers

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']

Contextual data

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.

Default answers

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.

Handling non-unique scores in choice question

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 ]

Clone this wiki locally