Setting variables (and doing other things) with questions

To instruct docassemble to store user input in a variable in response to a question, you need to include in your question a variable name within a directive that indicates how you would like docassemble to ask for the value of the variable.

A note about variable names

Variable names are Python identifiers, which means they can be any sequence of uppercase or lowercase letters, digits, and underscores, except the first character cannot be a digit. No spaces are allowed and no punctuation is allowed except for the underscore, _.

The following are valid variable names:

  • fried_fish1
  • NyanCat
  • nyancat (variables are case-sensitive, so this is not the same as the above)
  • __f645456DG_greij_43 (but why would you use something so ugly?)
  • USER_PHONE_NUMBER (ok, but why are you yelling?)

The following are not valid variable names, and if you try to use such variable names you will may get an error or unexpected results:

  • 8th_plaintiff (you can’t begin a variable name with a number; Python will say “invalid syntax”)
  • Nyan-Cat (this is arithmetic: Nyan minus Cat)
  • fried.fish1 (this is valid code, but Python will think you are referring to the attribute fish1 of the object fried)
  • user's_phone_number (apostrophes are not allowed; Python recognizes them as single quotes)
  • favorite animal (spaces are not allowed)
  • beneficiary#1 (punctuation marks other than _ are not allowed)
  • applicant_résumé (only plain alphabet characters can be used)

See reserved variable names for a list of variable names that you cannot use because they conflict with built-in names that Python and docassemble use.

Multiple choice questions (one variable only)

Yes or no questions

The yesno and noyes statements

The yesno statement causes a question to set a boolean (true/false) variable when answered.

question: |
  Are you at least 18 years of age?
yesno: over_eighteen
yesno

In the example above, the web app will present “Yes” and “No” buttons and will set over_eighteen to True if “Yes” is pressed, and False if “No” is pressed.

The noyes statement is just like yesno, except that “Yes” means False and “No” means True.

question: |
  Are you at least 18 years of age?
noyes: user_is_minor
noyes

Note that yes/no fields can also be gathered on a screen along with other fields; to make screens like that, use fields below.

yesnomaybe or noyesmaybe

These statements are just like yesno and noyes, except that they offer a third choice, “I don’t know.” If the user selects “I don’t know,” the variable is set to None, which is a special Python constant that represents the absence of a value.

question: |
  Is Topeka the capital of Kansas?
yesnomaybe: topeka_is_capital_of_kansas
yesnomaybe

Multiple choice buttons

A question block with a buttons statement will set the variable identified in field to a particular value depending on which of the buttons the user presses.

The buttons statement must always refer to a YAML list, so that docassemble knows the order of the buttons.

If an item under buttons is a YAML key-value pair (written in the form of - key: value), then the key will be the button label that the user sees, and the value will be what the variable identified in field will be set to if the user presses that button.

question: |
  How would you like to pay for your
  car?
field: target_variable
buttons:
  - Buy it: purchaser
  - Lease it: borrower
buttons-labels

An item under buttons can also be plain text; in that case docassemble uses this text for both the label and the variable value.

question: |
  What type of belly button do you
  have?
field: target_variable
buttons:
  - Innie
  - Outie
  - No belly button
buttons

In other words, this:

field: user_gender
question: What is your gender?
buttons:
  - Male
  - Female
buttons-variation-1

is equivalent to this:

field: user_gender
question: What is your gender?
buttons:
  - Male: Male
  - Female: Female
buttons-variation-2

A powerful feature of buttons is the ability to use Python code to generate button choices. If an item under buttons is a key-value pair in which the key is the word code, then docassemble executes the value as Python code, which is expected to return a list. This code is executed at the time the question is asked, and the code can include variables from the interview. docassemble will process the resulting list and create additional buttons for each item.

field: target_variable
question: |
  Your use of this system does not
  mean that you have a lawyer.  Do
  you understand this?
buttons:
  - "I understand": understands
  - code: |
      [{'does not understand':"I do not understand"}, {'unsure':"I'm not sure"}]
buttons-code-list

Note that the Python code needs to return a list of key-value pairs (Python dictionaries) where the key is what the variable should be set to and the value is the button label. This is different from the YAML syntax.

This is equivalent to:

field: target_variable
question: |
  Your use of this system does not
  mean that you have a lawyer.  Do
  you understand this?
buttons:
  - "I understand": understands
  - "I do not understand": does not understand
  - "I'm not sure": unsure
buttons-code-list-equivalent

You can use buttons as an alternative to yesno where you want different text in the labels.

question: |
  Are you satisfied?
field: user_is_satisfied
buttons:
  - "You bet": True
  - "No way": False
yesno-custom

Multiple choice list

To provide a multiple choice question with “radio buttons” and a “Continue” button, use field with a choices list:

question: |
  What type of shoes do you wear?
field: target_variable
choices:
  - Sneakers
  - Sandals
  - Clogs
  - Other
choices

You can specify a default value using default:

question: |
  What type of shoes do you wear?
field: target_variable
default: Sandals
choices:
  - Sneakers
  - Sandals
  - Clogs
  - Other
choices-with-default

Adding images to buttons and list items

To add a decorative icon to a choice, use a key/value pair and add image as an additional key.

question: |
  What is the most important question
  to ask?
field: interrogatory
buttons:
  - "When?": when
    image: calendar
  - "Where?": where
    image: map
buttons-icons

This works with choices as well:

question: |
  What is the most important question
  to ask?
field: interrogatory
choices:
  - "When?": when
    image: calendar
  - "Where?": where
    image: map
choices-icons

Embedding question and code blocks within multiple choice questions

Multiple choice questions can embed question blocks and code blocks. These questions are just like ordinary questions, except they can only be asked by way of the questions in which they are embedded.

You embed a question by providing a YAML key-value list (a dictionary) (as opposed to text) as the value of a label in a buttons or choices list.

question: What is your favorite color?
buttons:
  - Red:
      question: Dark red or light red?
      field: favorite_color
      buttons:
        - Dark Red
        - Light Red
  - Green:
      question: Dark green or light green?
      field: favorite_color
      buttons:
        - Dark Green
        - Light Green
buttons-code-color

While embedding question blocks can be useful sometimes, it is generally not a good idea to structure interviews with a lot of embedded questions. You will have more flexibility if your questions stand on their own.

It is also possible for multiple-choice questions to embed code blocks that execute Python code. (If you do not know what code blocks are yet, read the section on code blocks first.) This can be useful when you want to set the values of multiple variables with one button.

question: What kind of car do you want?
buttons:
  - Ford Focus:
      code: |
        car_model = "Focus"
        car_make = "Ford"
  - Toyota Camry:
      code: |
        car_model = "Camry"
        car_make = "Toyota"
buttons-code

The question above tells docassemble that if the interview logic calls for either car_model or car_make, the question should be tried. When the user clicks on one of the buttons, the code will be executed and the variables will be set.

A simple “continue” button that sets a variable

question: |
  Welcome to the interview!
subquestion: |
  Your participation means a lot to us.
field: user_saw_intro
continue-participation

A question with a field and no buttons will offer the user a “Continue” button. When the user presses “Continue,” the variable indicated by field will be set to True.

Uploads

Storing files as variables

Users can upload files, and the files are stored as a variable in docassemble.

question: |
  Please upload a picture of yourself.
fields:
  - Picture: user_picture
    datatype: file
---
question: |
  You're so adorable!
subquestion: |
  ${ user_picture }
mandatory: True
upload

Note that this question uses the fields statement, which is explained in more detail below.

When set, the variable user_picture will be a special object of type DAFileList. For instructions about how to make use of uploaded files, see inserting images.

Gathering the user’s signature into a file variable

The signature directive presents a special screen in which the user can sign his or her name with the trackpad or other pointing device.

question: |
  Sign your name
subquestion: |
  By signing your name, you agree to
  our terms and conditions.
signature: target_variable
under: |
  ${ user }
signature

On the screen, the question text appears first, then the subquestion text, then the signature area appears, and then the under text appears.

In this example, the user_signature variable will be set to an object of type DAFile. This variable can be included in the same way that a document upload can be included. For example:

---
question: |
  Is this your signature?
subquestion: |
  ${ user_signature }
yesno: user_signature_verified
---

or, if you want to control the width of the image:

---
question: |
  Is this your signature?
subquestion: |
  ${ user_signature.show(width='1in') }
yesno: user_signature_verified
---

Signatures can be also be inserted into assembled documents in the same way. They can also be inserted into .docx fill-in forms and PDF fill-in forms.

On a small screen, users need as much of the screen as possible to write their signature. For this reason, docassemble will reduce the size of the navigation bar and put the question text into the navigation bar. For this reason, you should make sure your question text is very brief – no longer than “Sign your name.” You should also make the subquestion text as brief as possible. Although you may be developing your app on a desktop or laptop monitor, your users are probably using smartphones, so test your app on a smartphone.

Setting multiple variables with one screen

Creating a list of fields

The fields statement is used to present the user with a list of fields.

question: Tell me about yourself
fields:
  - Favorite color: user_favorite_color
  - Description of your ideal vacation: user_ideal_vacation
    datatype: area
    required: False
text-field-example

The fields must consist of a list in which each list item consists of one or more key/value pairs. One of these keys (typically) is the label the user sees, where the value associated with the key is the name of the variable that will store the user-provided information for that field. The other key/value pairs in the item (if any) are modifiers that allow you to customize how the field is displayed to the user.

These modifiers are distinguished from label/variable pairs based on the key; if the key is uses one of the names listed below, it will be treated as a modifier; if it is anything else, it will be treated as a label.

Customizing each field

The following are the keys that have special meaning:

datatype

datatype affects how the data will be collected, validated and stored. For a full explanation of how this is used, see the section on datatype below.

required

required affects whether the field will be optional or required. If a field is required, it will be marked with a red asterisk. The value of required can be True or False. By default, all fields are required, so you never need to write required: True unless you want to.

question: |
  What are your favorite things to eat?
subquestion: |
  You may not like any vegetables,
  but at least tell me your favorite
  fruit.
fields:
  - Vegetable: target_variable
    required: False
  - Fruit: other_target_variable
optional-field

Instead of writing True or False, you can write Python code. This code will be evaluated for whether it turns out to be true or false. For example, instead of True or False, you could use the name of a variable that is defined by a yesno question (as long as that variable was defined before the screen loads; the red asterisk cannot be toggled in real time within the browser).

question: |
  What are your favorite
  things to drink?
fields:
  - Favorite Beverage Overall: favorite_beverage
  - Favorite Soda: favorite_soda
    required: user_likes_soda
required-code

hint

You can guide users as to how they should fill out a text field by showing greyed-out text in a text box that disappears when the user starts typing in the information. In HTML, this text is known as the placeholder. You can set this text for a text field by setting hint. You can use Mako templates.

question: |
  What are your favorite things to eat?
subquestion: |
  Please be specific.
fields:
  - Vegetable: target_variable
    hint: e.g., eggplant, turnips
  - Fruit: other_target_variable
    hint: e.g., apples, oranges
text-hint

help

You can provide contextual help to the user regarding the meaning of a field using the help modifier. The label will be green to indicate that it can be clicked on, and the value of help will appear on the screen when the user clicks the green text. You can use Mako templates.

question: |
  What are your favorite things to eat?
subquestion: |
  If you don't know what a vegetable
  or fruit is, click the green text.
fields:
  - Vegetable: target_variable
    help: |
      A plant.
  - Fruit: other_target_variable
    help: |
      The pulpy, edible seed vessels
      of certain plants.
text-help

default

You can provide a default value to a field using default. You can also use Mako templates.

question: |
  What are your favorite things to eat?
subquestion: |
  Please be specific.
fields:
  - Vegetable: target_variable
    default: eggplant
  - Fruit: other_target_variable
    default: |
      ${ greatest_fruit }
---
code: |
  greatest_fruit = "apples"
text-default

choices

The choices modifier is used with multiple-choice fields. It must refer to a list of possible options. Can be a list of key/value pairs (key is what the variable will be set to; value is the label seen by the user) or a list of plain text items (in which case the label and the variable value are the same).

question: |
  What is your favorite fruit?
fields:
  - Fruit: favorite_fruit
    choices:
      - Apples: apple
      - Oranges: orange
      - Pears: pear
fields-choices

When the datatype is object, object_radio, or object_checkboxes, choices indicates a list of objects from which the user will choose. For more information about using objects in multiple choice questions, see the section on selecting objects, below.

code

If you have a multiple-choice question and you want to reuse the same selections several times, you do not need to type in the whole list every time. You can define a variable to contain the list and a code block that defines the variable.

Adding code to a field makes it a multiple-choice question. The code itself refers to Python code that generates a list of possible options for a multiple choice field.

question: |
  What is your favorite fruit?
fields:
  - Fruit: favorite_fruit
    datatype: radio
    code: |
      myoptions
---
question: |
  What is your brother's favorite
  fruit?
fields:
  - Fruit: favorite_fruit_of_brother
    datatype: radio
    code: |
      myoptions
---
code: |
  myoptions = [
                {'apple': "Apples"},
                {'orange': "Oranges"},
                {'pear': "Pears"}
              ]
fields-mc

The Python code runs at the time the question is asked. Therefore, you can use the code feature to create multiple-choice questions that have dynamically-created lists of choices.

The Python code needs to be a single expression. The result of the expression can take several forms.

It can be a list of single-item dictionaries, as in the example above.

It can be a dictionary (in which case you cannot control the order of items):

code: |
  myoptions = {
                'apple': "Apples",
                'orange': "Oranges",
                'pear': "Pears"
              }
fields-mc-2

It can be a list of text items (in which case the values and labels will be the same):

code: |
  myoptions = ["Apples", "Oranges", "Pears"]
fields-mc-3

It can be a list of two-element lists:

code: |
  myoptions = [
                ['apple', 'Apples'],
                ['orange', 'Oranges'],
                ['pear', 'Pears']
              ]
fields-mc-4

When code returns an empty list

If your code returns an empty list, docassemble will try to deal with the situation gracefully. If there is only a single field listed under fields, then the variable that will be set by the user’s selection will be set to None, and the question will be skipped. If the datatype is checkboxes, the variable will be set to an empty dictionary.

exclude

If you build the list of choices with code, you can exclude items from the list using exclude, where the value of exclude is Python code.

question: |
  What is your favorite fruit?
fields:
  - Fruit: favorite_fruit
    datatype: radio
    code: |
      myoptions
---
question: |
  What is your brother's favorite
  fruit, assuming he does not like
  ${ favorite_fruit }?
fields:
  - Fruit: favorite_fruit_of_brother
    datatype: radio
    code: |
      myoptions
    exclude: |
      favorite_fruit
---
code: |
  myoptions = [
                {'apple': "Apples"},
                {'orange': "Oranges"},
                {'pear': "Pears"}
              ]
fields-mc-exclude

In this example, the value of exclude is a single variable. If given a list of things, it will exclude any items that are in the list.

shuffle

shuffle can be used on multiple-choice fields (defined with code or choices). When True, it randomizes the order of the list of choices; the default is not to “shuffle” the list.

question: |
  For which of the following obscure
  candidates do you wish to vote?
fields:
  - Type: candidate
    datatype: radio
    shuffle: True
    choices:
      - Aaron Aardvark
      - Albert Arnold
      - Felicia Fellowes
      - Miranda Moore
      - Zachariah Zephyr
shuffle

show if

You can use the show if modifier if you want the field to be hidden under certain conditions. There are three methods of using show if, which have different syntax.

Using the first method, the field will appear or disappear in the web browser depending on the value of another field in the fields list. Under this method, show if refers to a YAML dictionary with two keys: variable and is, where variable refers to the variable name of the other field, and is refers to the value of the other field that will cause this field to be shown.

This can be useful when you have a multiple-choice field that has an “other” option, where you want to capture a text field but only if the user selects the “other” option.

question: |
  What kind of car do you drive?
fields:
  - Make: car_make
    choices:
      - Honda
      - Toyota
      - Mazda
      - Other
  - Other: car_make
    show if:
      variable: car_make
      is: Other
other

The second method is like the first, but is for the special case where the other field in fields is a yes/no variable. Under this method, show if refers to the other field’s variable name. If that variable is true, the field will be shown, and if it is not true, the field will be hidden.

question: |
  Please fill in the following information.
fields:
  - "Do you like fruit?": likes_fruit
    datatype: yesnoradio
  - "What's your favorite fruit?": favorite_fruit
    show if: likes_fruit
showif-boolean

Under the third method, the field is either shown or not shown on the screen when it loads, and it stays that way. You can use Python code to control whether the field is shown or not. Unlike the first method, you are not limited to using variables that are part of the fields list; you can use any Python code. Under this method, show if must refer to a YAML dictionary with one key, code, where code contains Python code. The code will be evaluated and if it evaluates to a positive value, the field will be shown.

question: |
  Please fill in the following information.
fields:
  - Favorite fruit: fruit
  - Favorite vegetable: vegetable
  - Favorite fungus: mushroom
    show if:
      code:
        2 + 2 == 3
showif

hide if

This works just like show if, except that it hides the field instead of showing it.

question: |
  Please fill in the following information.
fields:
  - "Do you have fruit?": has_fruit
    datatype: yesnoradio
  - "What fruit do you need?": fruit
    hide if: has_fruit
hideif-boolean

disable others

If disable others is set to True, then when the user changes the value of the field to something, all the other fields in the question will be disabled.

note

The value of note is Markdown text that will appear on the screen; useful for providing guidance to the user on how to enter information.

question: |
  Please fill in the following information.
fields:
  - Favorite fruit: fruit
  - Favorite vegetable: vegetable
  - note: |
      In case you did not know, a 
      mushroom is a fungus, not a
      vegetable.
  - Favorite fungus: mushroom
note

html

The html directive is like note, except the format is expected to be raw HTML. It can be used in combination with the css and script modifiers.

question: |
  I was thinking about your birthday.
fields:
  - html: |
      The date and time today is
      <span class="mytime" id="today_time"></span>.
  - "When is your next birthday?": birth_date
    datatype: date
css: |
  <style>
    .mytime {
       color: green;
    }
  </style>
script: |
  <script>
    $("#today_time").html(Date());
  </script>
html

no label

If you use no label as the label for your variable, the label will be omitted. On wide screens, the field will fill more of the width of the screen if the label is set to no label.

question: |
  What is your Zodiac sign?
fields:
  - no label: target_variable
no-label-field

To keep the width of the field normal, but have a blank label, use "" as the label.

question: |
  What is your Zodiac sign?
fields:
  - "": target_variable
blank-label-field

label and field

Instead of expressing your labels and variable names in the form of - Label: variable_name, you can specify a label using the label key and the variable name using the field key.

question: |
  What are your favorite things to eat?
fields:
  - label: Vegetable
    field: favorite_vegetable
  - label: Fruit
    field: favorite_fruit
label

Possible data types for fields

There are many possible datatype values, which affect what the user sees and how the input is stored in a variable.

Text fields

A datatype: text provides a single-line text input box. This is the default, so you never need to specify it unless you want to.

question: |
  What are your favorite things to eat?
subquestion: |
  Please be specific.
fields:
  - Vegetable: target_variable
  - Fruit: other_target_variable
text-field

datatype: area provides a multi-line text area.

question: |
  Tell me the story of your life.
fields:
  - Life Story: target_variable
    datatype: area
text-box-field

datatype: date provides a date entry input box and requires a valid date.

question: |
  What is your date of birth?
fields:
  - Birthdate: target_variable
    datatype: date
date-field

datatype: email provides an e-mail address input box.

question: |
  What is your e-mail address?
fields:
  - E-mail: target_variable
    datatype: email
    required: False
email-field

datatype: password provides an input box suitable for passwords.

question: |
  Enter your username and password.
fields:
  - Username: user_name
  - Password: target_variable
    datatype: password
password-field

Numeric fields

datatype: integer indicates that the input should be a valid whole number.

datatype: number indicates that the input should be a valid numeric value.

question: |
  Describe your possessions.
fields:
  - Number of cars: number_cars
    datatype: integer
  - Ounces of gold: gold_ounces
    datatype: number
number-field

datatype: currency indicates that the input should be a valid numeric value. In addition, the input box shows a currency symbol based on locale defined in the configuration.

question: |
  How much is your house worth?
fields:
  - Value: target_variable
    datatype: currency
money-field

datatype: range shows a slider that the user can use to select a number within a given range. The range must be supplied by providing min and max values. An option step value can also be provided, the default of which is 1.

question: |
  On a scale from 1 to 10, how
  much do you like these animals?
fields:
  - Possums: possum_preference
    datatype: range
    min: 1
    max: 10
    step: 0.5
  - Rabbits: rabbit_preference
    datatype: range
    min: 1
    max: 10
range

File uploads

Using the file or files datatypes within a fields list, you can allow users to upload one or more files.

datatype: file indicates that the user can upload a single file. The variable is set to a DAFileList object containing the necessary information about the uploaded file.

question: |
  Please upload a picture of yourself.
fields:
  - Picture: user_picture
    datatype: file
---
question: |
  You're so adorable!
subquestion: |
  ${ user_picture }
mandatory: True
upload

datatype: files indicates that the user can upload one or more files. The variable is set to a DAFileList object containing the necessary information about the uploaded files.

question: |
  Please upload pictures of yourself.
fields:
  - Pictures: user_pictures
    datatype: files
---
question: |
  Look at all those adorable photos!
audio: ${ user_pictures }
mandatory: True
upload-multiple

datatype: camera is just like file, except with HTML5 that suggests using the device’s camera to take a picture. On many devices, this is no different from datatype: file.

datatype: camcorder is just like camera, except for recording a video.

datatype: microphone is just like camera, except for recording an audio clip.

Yes/no fields

datatype: yesno will show a checkbox with a label, aligned with labeled fields. datatype: noyes is like datatype: yesno, except with True and False inverted.

question: |
  Please provide the following information.
fields:
  - "What is your favorite food?": favorite_food
  - note: Check which foods you like.
  - Apples: likes_apples
    datatype: yesno
  - Turnips: dislikes_turnips
    datatype: noyes
fields-yesno

datatype: yesnowide will show a checkbox with a label that fills the full width of area. datatype: noyeswide is like datatype: yesnowide, except with True and False inverted.

question: |
  Please provide the following information.
fields:
  - note: Check which foods you like.
  - Peaches: likes_peaches
    datatype: yesnowide
  - Pears: dislikes_pears
    datatype: noyeswide
fields-yesnowide

datatype: yesnoradio will show radio buttons offering choices “Yes” and “No.”

datatype: noyesradio is like datatype: yesnoradio, except with True and False inverted.

question: |
  Please provide the following information.
fields:
  - "Do you like apricots?": likes_apricots
    datatype: yesnoradio
  - "Do you like pineapple?": dislikes_pineapple
    datatype: noyesradio
fields-yesnoradio

datatype: yesnomaybe will show radio buttons offering choices “Yes,” “No,” and “I don’t know.”

question: |
  Please answer the following question.
fields:
  - "Is Topeka the capital of Kansas?": topeka_is_capital_of_kansas
    datatype: yesnomaybe
fields-yesnomaybe

datatype: noyesmaybe is like datatype: yesnomaybe, except with True and False inverted.

question: |
  Please answer the following question.
fields:
  - "Was Washington the first U.S. president?": washington_not_the_first_president
    datatype: noyesmaybe
fields-noyesmaybe

Multiple-choice fields

Checkboxes

datatype: checkboxes will show the choices list as checkboxes. The variable will be a dictionary with items set to True or False depending on whether the option was checked. No validation is done to see if the user selected at least one, regardless of the value of required.

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples
      - Peaches
      - Pears
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes

Default values for checkboxes

To set default values in a checkbox list, you have a few options.

If you want to select just one option, just indicate the name of the option:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples
      - Peaches
      - Pears
    default: Pears
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-0

If you want to select multiple options, indicate a YAML list:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples
      - Peaches
      - Pears
    default:
      - Pears
      - Apples
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-1

You can also indicate your defaults in the form of a YAML dictionary:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples
      - Peaches
      - Pears
    default:
      Pears: True
      Apples: True
      Peaches: False
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-2

You can also use Python code to generate the defaults:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples
      - Peaches
      - Pears
    default:
      code: |
        ['Pears', 'Apples']
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-3

Your Python code can also return a dictionary:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples
      - Peaches
      - Pears
    default:
      code: |
        dict(Pears=False, Peaches=False, Apples=True)
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-4

If you generate the checkbox options with code, you can include defaults directly within your code when you use a list of dictionaries:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    code: |
      [
        {'apple': 'Apples', 'default': True},
        {'peach': 'Peaches'},
        {'pear': 'Pears'}
      ]
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-5

This also works if you use a list of lists:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    code: |
      [
        ['apple', 'Apples', True],
        ['peach', 'Peaches'],
        ['pear', 'Pears']
      ]
  - "What is your favorite fruit overall?": favorite_fruit
fields-checkboxes-default-6

Radio buttons

datatype: radio shows a choices list as a list of radio buttons instead of as a dropdown select tag (which is the default). The variable will be set to the value of the choice.

question: |
  Describe your car.
fields:
  - Number of wheels: wheels_on_car
    datatype: integer
  - Type: car_type
    datatype: radio
    choices:
      - Convertible
      - Hatchback
      - Sedan
  - Model: car_country
    datatype: radio
    choices:
      - BMW: Germany
      - Buick: United States
      - Honda: Japan
      - Toyota: Japan
radio-list

Multiple-choice with objects

datatype: object is used when you would like to use a variable to refer to an existing object. You need to include choices, which can be a list of objects.

objects:
  protagonist: Individual
  antagonist: Individual
---
code: |
  protagonist.name.first = "Harry"
  protagonist.name.last = "Potter"
  antagonist.name.first = "Tom"
  antagonist.name.last = "Riddle"
---
question: Who is the villain?
fields:
  The villain is: villain
  datatype: object
  default: antagonist
  choices:
    - protagonist
    - antagonist
object

If choices refers to a variable that is a list of things, the list will be unpacked and used as the list of items from which the user can select. Python code can be used here.

objects:
  protagonist: Individual
  antagonist: Individual
  actors: PartyList
---
mandatory: True
code: |
  protagonist.name.first = "Harry"
  protagonist.name.last = "Potter"
  antagonist.name.first = "Tom"
  antagonist.name.last = "Riddle"
  actors.append(protagonist)
  actors.append(antagonist)
  actors.auto_gather = False
  actors.gathered = True
---
question: Who is the villain?
fields:
  - The villain is: villain
    datatype: object
    choices: actors
object-selections

datatype: object_radio is like datatype: object, except the user interface uses radio buttons rather than a pull-down list.

objects:
  protagonist: Individual
  antagonist: Individual
---
code: |
  protagonist.name.first = "Harry"
  protagonist.name.last = "Potter"
  antagonist.name.first = "Tom"
  antagonist.name.last = "Riddle"
---
question: Who is the villain?
fields:
  The villain is: villain
  datatype: object_radio
  default: antagonist
  choices:
    - protagonist
    - antagonist
object-radio

For a fuller discussion on using multiple-choice object selectors, see the section on selecting objects, below.

datatype: object_checkboxes is used when you would like to use a question to set the elements of an object of type DAList (or a subtype thereof). The choices in choices (optionally modified by exclude) will be presented to the user as checkboxes. The .gathered attribute of the variable will be set to True after the elements are set. See groups for more information.

objects:
  protagonist: Individual
  antagonist: Individual
  villain: PartyList
---
mandatory: True
code: |
  protagonist.name.first = "Harry"
  protagonist.name.last = "Potter"
  antagonist.name.first = "Tom"
  antagonist.name.last = "Riddle"
  villain.auto_gather = False
---
question: Who are the villains, if any?
fields:
  no label: villain
  datatype: object_checkboxes
  choices:
    - protagonist
    - antagonist
object-checkboxes

Input validation

For some field types, you can require input validation by adding the following to the definition of a field:

  • min: for currency and number data types, require a minimum value. This is passed directly to the jQuery Validation Plugin.
  • max: for currency and number data types, require a maximum value. This is passed directly to the jQuery Validation Plugin.
question: |
  What percentage of your friends
  are turnips?
fields:
  - Percent: percent
    datatype: number
    min: 0
    max: 100
min
  • minlength: require a minimum number of characters in a textbox, number of checkboxes checked, etc. This is passed directly to the jQuery Validation Plugin.
  • maxlength: require a maximum number of characters in a textbox, number of checkboxes checked, etc. This is passed directly to the jQuery Validation Plugin.
question: |
  What is the nuclear launch code?
fields:
  - Code: launch_code
    minlength: 5
    maxlength: 15
minlength

Example

Here is a lengthy example that illustrates many of these features.

question: Tell me more about yourself
fields:
  - Description: user_description
    datatype: area
    hint: |
      E.g., you can describe your
      hair color, eye color, 
      favorite movies, etc.
  - Annual income: user_annual_income
    datatype: currency
    min: 100
  - E-mail address: user_email_address
    datatype: email
  - Been vaccinated: user_vaccinated
    datatype: yesno
  - Seen Mount Rushmore: mount_rushmore_visited
    datatype: yesnowide
  - Opinion of turnips: turnip_rating
    datatype: range
    min: 1
    max: 10
  - Belly button type: belly_button
    datatype: radio
    choices:
      - Innie
      - Outie
  - html: |
      The date and time is
      <span class="mytime"
      id="today_time"></span>.
  - Number of friends: num_friends
    datatype: radio
    choices:
      - One: 1
      - Two: 2
      - Three: 3
  - Degrees obtained: degrees
    datatype: checkboxes
    choices:
      - High school
      - College
      - Graduate school
  - State you grew up in: home_state
    code: |
      us.states.mapping('abbr', 'name')
  - note: |
      #### Politics

      Tell me about your political
      views.
  - no label: political_views
    default: I have no political views
    maxlength: 30
  - Party: political_party
    datatype: radio
    shuffle: True 
    choices:
      - Republican
      - Democrat
      - Independent
css: |
  <style>
    .mytime {
       color: green;
    }
  </style>
script: |
  <script>
    $("#today_time").html(Date());
  </script>
fields

Assigning existing objects to variables

Using Mako template expressions (Python code enclosed in ${ }), you can present users with multiple-choice questions for which choices are based on information gathered from the user. For example:

include: basic-questions.yml
---
question: |
  What is your favorite date?
fields:
  - Greatest Date Ever: favorite_date
    datatype: date
    choices:
      - ${ client.birthdate }
      - ${ advocate.birthdate }
---
question: |
  The best day in the history of
  the world was ${ favorite_date }.
mandatory: True
object-try-1

But what if you wanted to use a variable to refer to an object, such as a person? You could try something like this:

question: |
  Who is the tallest?
fields:
  - Tallest person: tallest_person
    choices:
      - ${ client }
      - ${ advocate }
object-try-2

In this case, tallest_person would be set to the name of the client or the name of the advocate. But what if you wanted to then look at the birthdate of the tallest person, or some other attribute of the person? If all you had was the person’s name, you would not be able to do that. Instead, you would want tallest_person to be defined as the object client or the object advocate, so that you can refer to tallest_person.birthdate just as you would refer to client.birthdate.

You can accomplish this by setting datatype to object within a fields list, where the choices are the names of the objects from which to choose. (Optionally, you can set a default value, which is also the name of a variable.)

For example:

include: basic-questions.yml
---
question: Who is the villain?
fields:
  no label: villain
  datatype: object
  default: client
  choices:
    - client
    - advocate
---
question: |
  The villain, ${ villain }, was
  born on
  ${ format_date(villain.birthdate) }.
mandatory: True
object-try-3

Note that this interview incorporates the basic-questions.yml file which defines objects that are commonly used in legal applications, including client and advocate. It also contains questions for asking for the names of these people.

The interview above presents the names of the client and the advocate and asks which of these people is the villain.

If the user clicks the name of the advocate, then docassemble will define the variable villain and set it equal to advocate.

Note that because advocate is an object, villain will be an alias for advocate, not a copy of advocate. If you subsequently set advocate.birthdate, you will immediately be able retrieve that value by looking at villain.birthdate, and vice-versa.

Also because villain is an alias, if you refer to villain.favorite_food and it is not yet defined, docassemble will go searching for a question that offers to define advocate.favorite_food. This is because docassemble objects have an intrinsic identity, a unique name given to them at the time they are created. (You can inspect this by referring to villain.instanceName in a question and will see that it returns advocate.) For more information about this, see the discussion in the documenation for DAObject. (All docassemble objects are subtypes of DAObject.)

If any of the objects listed under choices represent lists of objects, such as case.defendant or client.child (objects of type PartyList, those lists will be expanded and every item will be included. You can also include under choices Python code, such as case.parties() or case.all_known_people().

The datatype of object presents the list of choices as a pull-down. If you prefer to present the user with radio buttons, set the datatype to object_radio.

Embedding fields within a paragraph

Within a fields question, you can include fill-in fields within the text of the subquestion using markup of the form [FIELD variable_name].

question: |
  Fill in the blanks.
subquestion: |
  I went downtown to get some
  [FIELD grocery_item].
  I slipped on the ice and
  fell in the
  [FIELD unfortunate_place].
fields:
  - Grocery item: grocery_item
  - no label: unfortunate_place
    choices:
      - storm drain
      - gutter
      - mineshaft
embed

Any variable name referenced in [FIELD ...] must be one of the variable names listed in the fields: list. If a field is referenced this way in the subquestion, it will not be displayed the way that fields are ordinarily displayed, but will be moved into the subquestion, where it will be formatted differently. Any fields in the fields: list that are not referenced in the subquestion will appear on the screen in the normal fashion.

The label of an embedded field is used as the tooltip of the field.

Generalizing questions

docassemble lets you write a single question that can be re-used throughout an interview.

For example, suppose you want to gather the following variables:

  • spouse.birthdate
  • mother.birthdate
  • father.birthdate

or:

  • plaintiff[0].served
  • plaintiff[1].served
  • plaintiff[2].served

It would be tedious to have to write separate questions for each of these variables.

Luckily, there are two features in docassemble that allow you to write questions in a generalized way: the generic object modifier, and index variables.

The generic object modifier

The generic object modifier is explained more fully in the section on modifiers, but here is an example:

generic object: Individual
question: |
  Does ${ x } like cats?
yesno: x.likes_cats
generic-object

The special variable x stands in for any object of type Individual.

If you are not yet familiar with the concept of “objects,” see the objects section.

Index variables

If you have an object that is a type or subtype of DAList or DADict, you can refer generically to any item within the object using an index variable.

question: |
  What is the ${ ordinal(i) }
  person's name?
fields:
  - First: people[i].name.first
  - Last: people[i].name.last
index-variable

The special variable i will stand in for the index of whichever list member your interview asks about.

You can nest iterators up to six levels, using the variables i, j, k, l, m, and n, but you have to use them in this order.

mandatory: True
code: |
  veggies.object_type = DAList
  veggies.new('potato', 'turnip')
  veggies.gathered = True
  for item in veggies:
    veggies[item].there_are_any = True
---
question: |
  Is there another ${ i }?
yesno: veggies[i].there_is_another
---
question: |
  How much does the
  ${ ordinal(j) }
  ${ i }
  weigh?
fields:
  - Grams: veggies[i][j]
    datatype: number
nested-veggies

For more information about populating groups of things, see the groups section.

For more information about how docassemble identifies what question to ask in order to define a given variable, see the interview logic section.

Special screens

Performing special actions requested by the user

In docassemble, you can allow users to click links or menu items that take the user to a special screen that the user would not ordinarily encounter in the course of the interview. You can create such a screen using the event statement.

An event statement acts just like sets: it advertises to docassemble that the question will potentially define a variable.

In the following example, the variable show_date is never defined; it is simply sought. The task_not_yet_performed() function is used to make sure that the dialog box only appears once.

mandatory: True
code: |
  if task_not_yet_performed('show current date'):
    mark_task_as_performed('show current date')
    show_date
---
event: show_date
question: |
  The current date is ${ format_date(current_datetime()) }.
buttons:
  - Ok: continue
dialog-box

The event statement is important if you use the roles feature to conduct multi-user interviews.

event: role_event
question: All done for now.
subquestion: |
  Someone else needs to answer
  questions now.  You will be notified
  when you can resume the interview.
buttons:
  - Exit: leave
event-role-event

In the example above, the event line tells docassemble that this question should be displayed to the user if docassemble encounters the role_event, which is a special “event” that can happen in multi-user interviews. The event is triggered when the interview reaches a point when a person other than the current user needs to answer a question. For example, while a client is filling out an interview, the interview logic might call for a variable that can only be set by an advocate who reviews the client’s answers. In this scenario, a role_event will be triggered. When this happens, docassemble will look for a question or code block that defines the variable role_event, and it will find the example question above.

This directive can also be used to create screens that the user can reach from the menu or from hyperlinks embedded in question text. For information and examples, see url_action(), process_action(), action_menu_item(), and menu_items.

Creating a special screen where the user can review his or her answers

A review block allows interview authors to provide a screen where users can review and edit their answers. Typically, the user will get to this screen by selecting an option from the web app menu (e.g., “Review Answers”), or by clicking on a hyperlink within subquestion text (e.g., “to review the answers you have provided so far, click here”).

Here is an example of a review block that is launched from the menu:

event: review_answers
question: |
  Revisit questions
subquestion: |
  These are the questions you have
  answered so far.  Click to revisit.
review:
  - Favorite fruit: fruit
  - Favorite vegetable: vegetable
  - Favorite fungus: fungi
---
mandatory: True
code: |
  menu_items = [ action_menu_item('Review Answers', 'review_answers') ]
review-1

Note that the review block does not show a link for “Favorite fungus” because the variable fungi has not been defined yet. However, once fungi is defined, the review block would show it.

This behavior is different from the typical behavior of docassemble blocks. Normally, referring to a variable that has not yet been defined will trigger the asking of a question that will define that variable. In the review block, however, the presence of an undefined variable simply causes the item to be omitted from the display.

For more information about adding menu items, see the sections on special variables and functions.

Customizing the display of review options

You can provide the user with a review of answers and buttons that the user can press to revisit an answer:

event: review_answers
question: |
  Revisit your answers
review:
  - Revisit fruit: fruit
    button: |
      You said your favorite fruit was
      ${ fruit }.
  - Revisit vegetable: vegetable
    button: |
      You said your favorite vegetable
      was ${ vegetable }.
  - Revisit fungus: fungi
    button: |
      You said your favorite fungus 
      was ${ fungi }.
review-2

In addition, the review block, like the fields block, allows you to use note and html entries.

If these are modified with the optional show if modifier, they will only be displayed if the variable referenced by the show if modifier has been defined. In addition, if any of these entries refer to a variable that has not been defined yet, they will be omitted.

event: review_answers
question: |
  Revisit your answers
review:
  - note: |
      Revisit your food preferences.
    show if: fruit
  - Favorite fruit: fruit
  - Favorite vegetable: vegetable
  - Favorite fungus: fungi
review-3

The review block allows you to add help text to an entry, in which case the text is shown underneath the hyperlink. If this text expects a variable to be defined that has not actually been defined, the item will not be shown. Note: this is not available with the button display format.

event: review_answers
question: |
  Revisit your answers
review:
  - Favorite fruit: fruit
    help: |
      You indicated you liked
      ${ fruit }.
  - Favorite vegetable: vegetable
    help: |
      You indicated you liked
      ${ vegetable }.
  - Favorite fungus: fungi
    help: |
      You indicated you liked
      ${ fungi }.
review-4

Customizing the Resume button

By default, the review block puts a “Resume” button at the bottom of the screen. If you want the label on the button to be something other than the word “Resume,” add a resume button label modifier.

event: review_answers
question: |
  Revisit your answers
review:
  - Revisit fruit: fruit
    button: |
      You indicated you liked
      ${ fruit }.
  - Revisit vegetable: vegetable
    button: |
      You indicated you liked
      ${ vegetable }.
  - Revisit fungus: fungi
    button: |
      You indicated you liked
      ${ fungi }.
resume button label: Keep going
resume-button-label

Why can’t review blocks be automatically generated?

The list of variables to display to the user in a review block needs to be specified by the interview author. There are several reasons why this needs to be done manually as opposed to automatically:

  1. Variables in your interview may be interdependent. You do not necessarily want to allow the interviewee to edit any past answer at will because this may result in internal inconsistencies or violations of the logic of your interview. For example, if your interview has a variable called eligible_for_medicare, which is set after the user answers a series of questions, you would not want the user to be able to go back and set his or her age to 30, at least not without a reconsideration of the definition of eligible_for_medicare. Therefore, it is important that the interview author control what the user can edit.
  2. A list of answers already provided might not be user-friendly unless the interview author presents it in a logically organized fashion. The order in which the questions were asked is not necessarily the most logical way to present the information for editing.