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

Using code to generate the choices

A powerful feature of buttons (which also works with choices, dropdown, and combobox) 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:
  code: |
    [{'understands':"I understand"}, {'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 mix choices that are specified manually with choices that are specified with code:

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-partial

As explained below, you can also use code to decorate the buttons with images.

True/False buttons

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

Another way to set a default is by adding default: True to the choice that you want to be the default.

question: |
  What type of shoes do you wear?
field: target_variable
choices:
  - Sneakers: sneakers
  - Sandals: sandals
    default: True
  - Clogs: clogs
  - Other: other
choices-with-default-item

You can also provide help text for a radio button using help:

question: |
  What type of shoes do you wear?
field: target_variable
choices:
  - Sneakers: sneakers
    help: |
      Comfortable shoes.
  - Sandals: sandals
    help: |
      For summer.
  - Clogs: clogs
    help: |
      For hippies.
  - Other: other
    default: True
    help: |
      Because the other types suck.
choices-with-help

These modifications can also be specified when building a list of choices using code:

question: |
  What type of shoes do you wear?
field: target_variable
choices:
  code: |
    [{'sneakers': "Sneakers",
      'help': "Comfortable shoes."},
     {'sandals': "Sandals",
      'help': "For summer."},
     {'clogs': "Clogs",
      'help': "For hippies."},
     {'other': "Other",
      'default': True,
      'help': "Because the other types suck."}]
choices-from-code

Multiple choice dropdown

To provide a multiple choice question with a dropdown selector, use field with a dropdown list:

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

Multiple choice combobox

To provide a multiple choice question with a “combobox” selector, use field with a combobox list:

question: |
  What is your favorite color?
field: favorite_color
combobox:
  - Red
  - Green
  - Purple
choices-combobox

Adding images to buttons and list items

To add a decorative icon to a buttons 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

It is not possible to decorate dropdown or combobox choices with images.

In these examples, calendar and map are the names of decorations that are defined in an images or image sets block.

If you create the list of choices with code, you can specify an image by including an additional key/value pair within an item, where the key is image.

question: |
  What is the most important question
  to ask?
field: interrogatory
buttons:
  code: myoptions
---
code: |
  myoptions = \
    [{"when": "When?",
      "image": "calendar"},
     {"where": "Where?",
      "image": "map"}]
buttons-icons-code

There is an additional feature available when you assemble buttons with code: you can use DAFile or DAFileList objects to indicate the image. This example uses an uploaded image file as the source of the image for one of the buttons:

question: |
  What is the most important question
  to ask?
field: interrogatory
buttons:
  code: myoptions
---
code: |
  myoptions = \
    [{"when": "When?",
      "image": uploaded_file},
     {"where": "Where?",
      "image": "map"}]
---
question: |
  Please upload an image.
fields:
  - Image: uploaded_file
    datatype: file
buttons-icons-code-upload

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, choices, or dropdown 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: Do you like soda?
yesno: user_likes_soda
---
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

The hint is also used to provide the default text the user sees when they fill out a multiple-choice dropdown or a combobox input element within a fields question.

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

You can specify a default by including a three-element list where the third element is True if the choice should be selected by default.

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

You can include “help text” for a choice by including a fourth element in one of the lists, where the element contains the help text you want to be available. The user can see the help text by touching the question mark button.

code: |
  myoptions = [
                ['apple', 'Apples', None, 'Apples are good in pies.'],
                ['orange', 'Oranges', None, 'Oranges are a type of citrus fruit.'],
                ['pear', 'Pears', None, 'Pears are an acquired taste.']
              ]
fields-mc-6

If your code is a list of dictionaries, you can include a 'default' key in the dictionary indicating a true or false value that represents whether the choice should be selected by default.

code: |
  myoptions = [
                {'apple': "Apples",
                 'default': True},
                {'orange': "Oranges"},
                {'pear': "Pears"}
              ]
fields-mc-7

Similarly, you can include help text in a list of dictionaries by including a 'help' key in the dictionary indicating the help text that should be available to the user.

code: |
  myoptions = [
                {'apple': "Apples",
                 'default': True,
                 'help': "Apples are good in pies."},
                {'orange': "Oranges",
                 'help': "Oranges are a type of citrus fruit."},
                {'pear': "Pears",
                 'help': "Pears are an acquired taste."}
              ]
fields-mc-8

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.

none of the above

If you use datatype: checkboxes, then by default a “None of the above” choice is added.

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-nota

You can turn off the “None of the above” choice by setting the none of the above option to False.

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

You can also change the phrase from “None of the above” to something else, even a Mako expression. Just set none of the above to the text you want to be displayed.

question: |
  Please fill in the following information.
fields:
  - Requested options: car_options
    datatype: checkboxes
    choices:
      - Sunroof
      - Automatic transmission
      - Heated seats
    none of the above: |
      Nothing ${ "at all" }
fields-mc-nota

When the list of choices is empty

If the list of choices for a multiple choice question is empty, docassemble will try to deal with the situation gracefully. If there is only a single field listed under fields, or the question is a standalone multiple choice question, then the variable that will be set by the user’s selection will be set to None, and the question (or the field, if there are other fields listed under fields) will be skipped.

If the datatype is checkboxes, the variable will be set to an empty DADict (a type of dictionary specific to docassemble). If the datatype is object_checkboxes, the variable will be set to an empty DAList (a type of list specific to docassemble).

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.

question: |
  What is your favorite color?
fields:
  - Favorite color: favorite_color
    choices:
      - Red
      - Blue
      - Green
      - Purple
    disable others: True
  - Other: favorite_color
---
question: |
  Your favorite color is
  ${ favorite_color }.
mandatory: True
disable-others

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

Data types

Within a fields question, there are many possible datatype values, which affect what the user sees and how the input is stored in a variable. The following sections describe the available datatypes.

Plain text

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

Passwords

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

Dates

datatype: date provides a date entry input box. The style of the input box depends on the browser.

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

Note that while input validation is applied, the resulting variable will be plain text, not a special Python date object. For more information about working with date variables, see the documentation for the date functions. These functions are flexible and will work correctly if you give them dates as text, as long as a date can be discerned from the text.

If you set a default value for a date field, write the date in the format YYYY-MM-DD. Many browsers have built-in “date pickers” that expect dates to be in this format. See Mozilla’s documentation of the date input field. If the browser uses a date picker, then your interview will see text values in the form YYYY-MM-DD, but on other browsers, like Firefox, the format may be some other format.

E-mail addresses

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

Numbers

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

Currency

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

The variable will be set to a number, just as if datatype: number was used. For information about how to display currency values, see the currency() function.

Sliders

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!
subquestion: |
  ${ user_pictures }
mandatory: True
upload-multiple

If your users upload digital photos into your interviews, the uploads may take a long time. You can configure an upload field so that images are reduced in size before they are uploaded by modifying your field definition with a maximum image size. The image will be reduced in size so that is no taller than or wider than the number of pixels designated by maximum image size.

In this example, images will be reduced in size to no more than 100 pixels in height or width:

question: |
  Please upload a picture of yourself.
fields:
  - Picture: user_picture
    datatype: file
    maximum image size: None
---
question: |
  You're so small!
subquestion: |
  ${ user_picture }
mandatory: True
upload-max-image-size

Note that the image file type of the uploaded file may be changed to [PNG] during the conversion process. Different browsers behave differently.

If you have a lot of document upload fields, you can set a default maximum image size on an interview-wide basis with the maximum image size interview feature and on a site-wide basis with the maximum image size configuration directive. If you have a default set up, but you want to override it for a particular field, you can set the maximum image size field modifier to None.

There are a few other data types that result in file uploads:

datatype: camera is just like file, except with an HTML5 input type 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.

Whether these special data types do anything different from the file data type is dependent on the web browser. Mobile browsers are the most likely to respond to these features.

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

Sometimes, when you are using a series of these checkboxes, you might want to have a “none of the above” selection. To do this, add a field for the selection, and associate it with a variable. (Your interview does not need to use the variable.) Then modify the field with uncheck others: True.

question: |
  Please provide the following information.
fields:
  - "What is your favorite food?": favorite_food
  - note: Check which foods you like.
  - Apples: likes_apples
    datatype: yesnowide
  - Turnips: likes_turnips
    datatype: yesnowide
  - Neither: dislikes_both_foods
    datatype: yesnowide
    uncheck others: True
fields-yesno-uncheck-others

This will cause the field to act as a “none of the above” field for all the other yes/no checkbox fields on the page. If you want the field to only relate to specific other fields, use a list of the variable names of those fields instead of True.

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: likes_turnips
    datatype: yesno
  - Neither: dislikes_both_foods
    datatype: yesno
    uncheck others:
      - likes_turnips
      - likes_apples
  - note: Check which rocks you like.
  - Granite: likes_granite
    datatype: yesno
  - Obsidian: likes_obsidian
    datatype: yesno
  - I do not like these rocks: dislikes_both_rocks
    datatype: yesno
    uncheck others:
      - likes_granite
      - likes_obsidian
fields-yesno-uncheck-others-list

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

Checkboxes

datatype: checkboxes will show the choices list as checkboxes. The variable will be a DADict (a type of dictionary specific to docassemble) 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

As you can see in this example, the keys of the resulting dictionary are the names of fruit, the values that are checked are True, and the values that were not checked are False.

In the example above, the keys of the dictionary are the same as the labels displayed to the user. If you want labels to be different from the keys, you can specify the choices in the following manner:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    choices:
      - Apples: apple
      - Peaches: peach
      - Pears: pear
  - "What is your favorite fruit overall?": favorite_fruit
---
question: |
  Thank you for your thoughts.
subquestion: |
  % if likes_fruit['apple']:
  You like apples.
  % endif
  % if likes_fruit['peach']:
  You like peaches.
  % endif
  % if likes_fruit['pear']:
  You like pears.
  % endif
  Your favorite, though, is ${ favorite_fruit }.

  In Python, `likes_fruit` is
  `${ variable_as_code }`.
mandatory: True
fields-checkboxes-different-labels

You can generate checkbox choices with code:

question: |
  Please tell me what you think.
fields:
  - "Select the fruits you like": likes_fruit
    datatype: checkboxes
    code: |
      [
        {'apple': 'Apples'},
        {'peach': 'Peaches'},
        {'pear': 'Pears'}
      ]
  - "What is your favorite fruit overall?": favorite_fruit
---
question: |
  Thank you for your thoughts.
subquestion: |
  % if likes_fruit['apple']:
  You like apples.
  % endif
  % if likes_fruit['peach']:
  You like peaches.
  % endif
  % if likes_fruit['pear']:
  You like pears.
  % endif
  Your favorite, though, is ${ favorite_fruit }.
mandatory: True
fields-checkboxes-code

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 your code returns 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

Multiple-choice dropdown

If you provide a list of choices or some choice-generating code for a field within a list of fields, the user will see a dropdown. The variable will be set to the value of the selected choice.

question: | 
  What type of shoes do you wear?
fields:
  - Shoe Type: target_variable
    choices: 
      - Sneakers
      - Sandals
      - Clogs
      - Other
fields-choices-dropdown

Multiple-choice combobox

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

question: |
  What is your favorite color?
fields:
  - Color: favorite_color
    datatype: combobox
    choices: 
      - Red
      - Green
      - Purple
---
mandatory: True
question: All done
subquestion: |
  Your favorite color is
  ${ favorite_color }.
fields-choices-combobox

Radio buttons

datatype: radio shows a choices list as a list of radio buttons instead of as a dropdown select element (which is the default). The variable will be set to the value of the selected 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

By using datatype: object in combination with disable others, you can create questions that either set the attributes of an object or set the object equal to another object.

objects:
  cook: Individual
  gardener: Individual
  maid: Individual
---
question: |
  Who is the cook?
fields:
  - Somebody already mentioned: cook
    datatype: object
    disable others: True
    choices: |
      [person for person in [cook, gardener, maid] if person.name.defined()]
  - First Name: cook.name.first
  - Last Name: cook.name.last
  - Suffix: cook.name.suffix
    required: False
    code: name_suffix()
---
question: |
  Who is the gardener?
fields:
  - Somebody already mentioned: gardener
    datatype: object
    disable others: True
    choices: |
      [person for person in [cook, gardener, maid] if person.name.defined()]
  - First Name: gardener.name.first
  - Last Name: gardener.name.last
  - Suffix: gardener.name.suffix
    required: False
    code: name_suffix()
someone-already-mentioned

In this example, if the gardener and the cook are the same person, the interview effectively does the following in Python:

gardener = cook

Please note that datatype: object cannot be used with the generic object modifier if the variable being set is x.

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
---
mandatory: True
code: |
  protagonist.name.first = "Harry"
  protagonist.name.last = "Potter"
  antagonist.name.first = "Tom"
  antagonist.name.last = "Riddle"
---
question: Who are the villains, if any?
fields:
  no label: villain
  datatype: object_checkboxes
  choices:
    - protagonist
    - antagonist
object-checkboxes-dalist

You can also use datatype: object_checkboxes on variables that already exist in your interview. You would need to do this if you wanted the variable to be a subtype of DAList. If you use a variable name that already exists, note that the question will only be used when the .gathered attribute is needed. To avoid questions asking for .there_are_any and .there_is_another, set .auto_gather to False. For example:

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

Another advantage of using an already-existing variable is that the choices in the question will default to the current elements in the list. In this example, we use the .append() method to initialize the list of villains.

mandatory: True
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
  villain.append(antagonist)
---
mandatory: True
question: Who are the villains, if any?
fields:
  no label: villain
  datatype: object_checkboxes
  choices:
    - protagonist
    - antagonist
object-checkboxes-default

Machine learning

From the user’s perspective, datatype: ml works just like datatype: text (which is the default if no datatype is indicated), and datatype: mlarea works just like datatype: area.

From the interview author’s perspective, however, the variable that is set is not a piece of text, but an object representing a classification of the user’s input, based on a machine learning model that is “trained” to classify user input.

question: |
  Describe how you feel.
fields:
  - no label: mood
    datatype: ml
---
mandatory: True
question: |
  You sound ${ mood }.
predict-happy-sad

For more information about how to use machine learning variables, see the machine learning section.

Input validation

Some datatypes, such as numbers, dates, and e-mail addresses, have validation features that prevent the user from moving to the next page if the input value does not meet the requirements of the data type. The jQuery Validation Plugin is used.

For some field types, you can require additional 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

You can also use Python code to validate an input field. To do so, add a validate directive to the field description that refers to the name of a function that returns True (or something that Python considers “true”) if the value is valid, and False (or something that Python considers “not true”) if the value is invalid.

modules:
  docassemble.demo.validationfuncs
---
mandatory: True
question: |
  There were ${ number_of_tires }
  in the garage.
---
question: |
  How many car tires are in the
  garage?
fields:
  - Tires: number_of_tires
    datatype: integer
    validate: is_multiple_of_four
validation-test

In this example, the function is_multiple_of_four is defined as follows:

def is_multiple_of_four(x):
    return x/4 == int(x/4)

This Python code is in the validationfuncs.py file. The modules block includes this code. The function returns True if 4 divides the input value into a whole number

The error message that the user will see is a generic error message, “Please enter a valid value.” In most cases you will want to explain to the user why the input did not validate. To provide a more descriptive error message, your function can raise an exception using the error message you would like the user to see. Both a false return value and an exception signal that the input is not valid.

modules:
  docassemble.demo.validationfuncstwo
---
mandatory: True
question: |
  There were ${ number_of_tires }
  in the garage.
---
question: |
  How many car tires are in the
  garage?
fields:
  - Tires: number_of_tires
    datatype: integer
    validate: is_multiple_of_four
validation-test-two

In this example, the function is_multiple_of_four is defined as follows:

def is_multiple_of_four(x):
    if x/4 != int(x/4):
        raise Exception("The number must be a multiple of four")
    return True

This Python code is in the validationfuncstwo.py file. If 4 does not divide the input value into a whole number, then an exception is raised. The text passed to Exception() will be the text the user sees if the value does not validate. If 4 does divide the input value by a whole number, True is returned.

One limitation of these validation functions is that they can only test for characteristics inherent in the variable being validated; they cannot compare the variable to other variables.

You can get around this restriction using the validation code modifier.

question: |
  There are 10 fruit in all.
subquestion: |
  How many apples and oranges
  are there?
fields:
  - Apples: number_of_apples
    datatype: integer
  - Oranges: number_of_oranges
    datatype: integer
validation code: |
  if number_of_oranges + number_of_apples != 10:
    raise Exception("The number of fruit must add up to 10!")
validation-code

Note that the code under validation code is not within a function, so it should not try to return any values. If the code runs through to the end, this indicates that the input for the question is valid. If an exception is raised, the input for the question is invalid.

If the input is invalid, the user will see a message at the top of the screen containing the error message passed to the Exception that was raised.

A comprehensive 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.

When you are using embedded fields, you can add the field modifier inline width to change the initial width of the field. For example, if you include inline width: 15em, the CSS will be altered so that the field is 15em wide. This modifier has no effect when embedded fields are not being used.

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

If you click “Favorite fruit,” you are taken to a question where you can edit the value of fruit. This has the same effect as calling force_ask() on 'fruit' or running an action on 'fruit'; whatever block in your interview offers to define fruit will be used. After the user edits the value of the variable, the user will return to the review screen again.

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.