# Table of Contents
- [Overview of docassemble](#overview-of-docassemble)
- [Hello world example](#hello-world-example)
- [Question blocks](#question-blocks)
- [How to write an interview](#how-to-write-an-interview)
- [Code questions](#code-questions)
- [Question modifiers](#question-modifiers)
- [Marking up text](#marking-up-text)
- [External data](#external-data)
- [Interview logic](#interview-logic)
- [Initial blocks](#initial-blocks)
- [Special variables](#special-variables)
- [Assembling documents](#assembling-documents)
- [Groups of things: lists, dictionaries, and sets](#groups-of-things-lists-dictionaries-and-sets)
- [Language support](#language-support)
- [Roles](#roles)
- [Text messaging interface](#text-messaging-interface)
- [Accessibility](#accessibility)
- [Live help: chat, phone, monitoring](#live-help-chat-phone-monitoring)
- [User login system](#user-login-system)
- [Custom Front Ends](#custom-front-ends)
- [Machine learning](#machine-learning)
- [Customizing the user interface](#customizing-the-user-interface)
- [Interview developers' playground](#interview-developers-playground)
- [Running code behind the scenes](#running-code-behind-the-scenes)
- [Overview of the development process](#overview-of-the-development-process)
- [Support](#support)
- [Debugging](#debugging)
- [Package management](#package-management)
- [Overview of docassemble administration](#overview-of-docassemble-administration)
- [Contributing](#contributing)
- [Policies](#policies)
- [Security](#security)
- [Scalability of docassemble](#scalability-of-docassemble)
- [License](#license)
- [Migration from Python 2.7 to Python 3](#migration-from-python-2-7-to-python-3)
- [Future development ideas](#future-development-ideas)
- [The "legal" module and basic-questions.yml](#the-legal-module-and-basic-questions-yml)
- [API](#api)
- [Overview of docassemble](#overview-of-docassemble)
- [System-wide configuration](#system-wide-configuration)
- [Installing on Docker](#installing-on-docker)
- [Recipes](#recipes)
- [Change Log](#change-log)
---
# Overview of docassemble
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/docs.md "Edit this page on GitHub")
Overview of docassemble
=======================
**docassemble** is a platform for creating mobile-friendly web applications called [Interviews](https://docassemble.org/docs/interviews.html)
that ask one question at a time in order to reach an end point. This end point may be the presentation of advice, the creation of a signed document, the submission of an application, or something else. Run the [Demonstration](https://docassemble.org/demo.html)
to get an idea for what **docassemble** applications look like.
You can run **docassemble** on your laptop, but most people run it “in the cloud” on [Amazon Web Services](https://aws.amazon.com/ec2/)
, [Digital Ocean](https://www.digitalocean.com/)
, or another hosting service. The [Deploy](https://docassemble.org/deploy.html)
page describes a variety of ways you can get your own **docassemble** instance up and running. You can install **docassemble** on a server using [Docker](https://docassemble.org/docs/docker.html)
or (if you are an expert) follow the detailed [Installation](https://docassemble.org/docs/installation.html)
instructions. For the most part, the [Administration](https://docassemble.org/docs/admin.html)
and [Configuration](https://docassemble.org/docs/config.html)
of **docassemble** can be handled through the web interface.
Introduction to **docassemble**
===============================
Interview developers write interviews in [YAML](https://en.wikipedia.org/wiki/YAML)
format, a plain-text format that is human-readable but also machine-readable.
An interview consists of multiple “blocks.” Blocks are pieces of text separated by `---`, which is a record separator in [YAML](https://en.wikipedia.org/wiki/YAML)
. For example:
question: Do you like turnips?
yesno: likes_turnips
---
question: When did you stop idolizing worms?
fields:
- Date: worm_idolizing_stop_date
datatype: date
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/turnips-worms.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/turnips-worms.yml&reset=1 "Click to try this interview")
You can click the screenshot above to see this interview in action. If you hover over the source code, three dots will appear in the lower-right corner; if you click the three dots, you can see full interview [YAML](https://en.wikipedia.org/wiki/YAML)
behind the sample interview.
Some blocks are [Question Blocks](https://docassemble.org/docs/questions.html)
that represent screens that the user will actually see. (Two examples are above.) The basic structure of question blocks is simple, but there are a lot of possible [Question Modifiers](https://docassemble.org/docs/modifiers.html)
that can make your screens do different things.
The information gathered from users is stored in “variables,” and the purpose of most question blocks will be [Setting Variables](https://docassemble.org/docs/fields.html)
. You can use whatever variable names you want, except you can’t use the names of [Special Variables](https://docassemble.org/docs/special.html)
that already have their own meaning.
**docassemble** supports [many different types](https://docassemble.org/docs/fields.html#data%20types)
of variables – even [file uploads](https://docassemble.org/docs/fields.html#file)
and [touchscreen signatures](https://docassemble.org/docs/fields.html#signature)
. One of the most powerful features is the ability to store information in a structured way using [Objects](https://docassemble.org/docs/objects.html)
. When you want to collect one or more pieces of related information, you can collect the information into [Groups](https://docassemble.org/docs/groups.html)
such as [lists](https://docassemble.org/docs/groups.html#gather%20list)
and [dictionaries](https://docassemble.org/docs/groups.html#gather%20dictionary)
.
There are other types of blocks besides [`question`](https://docassemble.org/docs/questions.html#question)
blocks.
[Initial Blocks](https://docassemble.org/docs/initial.html)
set interview-wide options, like the special [features](https://docassemble.org/docs/initial.html#features)
of an interview, or the default [screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
.
[Code](https://docassemble.org/docs/code.html)
blocks allow you to set variables through computation. Code is written in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
. To write an interview, you do not need to know much about [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
except how to write “if/then/else” statements.
code: |
if plaintiff.county == defendant.county:
jurisdiction_is_proper = True
You can even use “fuzzy logic” with **docassemble**’s [Machine Learning](https://docassemble.org/docs/ml.html)
feature.
**docassemble** decides which questions to ask, and the order in which to ask them, according to the [Interview Logic](https://docassemble.org/docs/logic.html)
. You can specify the order of questions with great specificity if you want, or you can just specify an end goal and let **docassemble** figure everything out on its own.
A popular use of interviews is the assembly of [Documents](https://docassemble.org/docs/documents.html)
(hence the name **docassemble**). You can write document templates in [DOCX](https://docassemble.org/docs/documents.html#docx%20template%20file)
or [PDF](https://docassemble.org/docs/documents.html#pdf%20template%20file)
formats. You can also write documents the same way you write questions, in plain text using [Markup](https://docassemble.org/docs/markup.html)
to indicate formatting.
As your interviews become more sophisticated, you will find it useful to invoke [Functions](https://docassemble.org/docs/functions.html)
to do things like conjugate verbs, compute differences between dates, or offer the user hyperlinks that perform special [actions](https://docassemble.org/docs/functions.html#actions)
.
**docassemble** uses a [User Login](https://docassemble.org/docs/users.html)
system that allows users to create accounts, save their answers, and resume their interviews at a later date.
Since **docassemble** is a free and open-source application, it is designed to be interoperable with other applications. There are a variety of ways to work with [External Data](https://docassemble.org/docs/external.html)
; you can move information easily into and out of a **docassemble** interview session. There is also a full-featured [API](https://docassemble.org/docs/api.html)
for interacting with **docassemble** programmatically. You can also design your own [Custom Front Ends](https://docassemble.org/docs/frontend.html)
.
Developers can prototype and test their interviews in the browser, using the interview developers’ [Playground](https://docassemble.org/docs/playground.html)
.
Once you get a **docassemble** server [up and running](https://docassemble.org/deploy.html)
, go through the [Hello World](https://docassemble.org/docs/helloworld.html)
tutorial to learn more about how interviews work. As you become more experienced using the system, you may want to explore using other [Development Workflows](https://docassemble.org/docs/development.html)
than just the [Playground](https://docassemble.org/docs/playground.html)
.
One of **docassemble**’s most powerful features is its ability to operate multi-user interviews through the [Roles](https://docassemble.org/docs/roles.html)
feature. For example, a user could fill out an interview and then an attorney could enter the interview to evaluate the information and provide legal advice, which the user would see the next time they log in.
Using the [Background Tasks](https://docassemble.org/docs/background.html)
features, you can have your interviews do things on the server at times other than when the user presses a button to advance to a new page. Time-intensive tasks can run in the [background](https://docassemble.org/docs/background.html#background)
. The interview can [evaluate user input](https://docassemble.org/docs/background.html#check%20in)
before the user clicks the Continue button. Interviews can do things [when the user is not logged in](https://docassemble.org/docs/background.html#scheduled)
, like send a reminder [e-mail](https://docassemble.org/docs/functions.html#send_email)
to a user about a deadline as the date approaches.
**docassemble** is a multi-purpose platform, but it is particularly designed for [Legal Applications](https://docassemble.org/docs/legal.html)
, and has special functionality for that specific purpose.
If you need to make an interview available in more than one language, **docassemble**’s [Language Support](https://docassemble.org/docs/language.html)
features can help you manage translations. **docassemble** also has a number of features for [Accessibility](https://docassemble.org/docs/accessibility.html)
by persons with disabilities.
**docassemble** was built on the model of the open-source software development world. Interviews can be bundled into [Packages](https://docassemble.org/docs/packages.html)
, which can be shared on [GitHub](https://github.com/)
or moved between servers as ZIP files.
The mobile-friendly web interface is the primary way that users will run interviews, but there is also the option of making interviews available via [Text Messaging](https://docassemble.org/docs/sms.html)
.
When you deploy your interviews, there are a variety of ways you can provide support to your users. The [Live Help](https://docassemble.org/docs/livehelp.html)
features allow operators to communicate with users using on-line chat. Operators can see users’ screens and even take control if necessary. If communication by phone is necessary, operators can provide users with a special phone number and code that forwards a call without revealing the operator’s actual phone number.
**docassemble** has excellent [Scalability](https://docassemble.org/docs/scalability.html)
when deployed in the cloud, so you don’t have to worry about what will happen if your interviews get a lot of traffic.
If your interviews will process sensitive information, **docassemble** has a number of [Security](https://docassemble.org/docs/security.html)
features to keep that information safe, such as server-side encryption.
Developers will invariably make mistakes and encounter [Errors](https://docassemble.org/docs/errors.html)
. **docassemble** tries to provide helpful error messages in the browser or in logs stored on the server.
If you get stuck, you can seek out [Support](https://docassemble.org/docs/support.html)
from the **docassemble** community, in particular by posting a question on the **docassemble** [Slack](https://join.slack.com/t/docassemble/shared_invite/zt-2cspzjo9j-YyE7SrLmi5muAvnPv~Bz~A)
. You might also find that there is an example interview in the [Recipes](https://docassemble.org/docs/recipes.html)
that will help you solve your problem.
**docassemble** is free software available with a highly permissive open-source [License](https://docassemble.org/docs/license.html)
. The software is updated frequently, and you can see what new features are available by reading the [Change Log](https://docassemble.org/docs/changelog.html)
.
Note that if you have been using **docassemble** for a long time, you need learn about the necessity of doing a [Python Upgrade](https://docassemble.org/docs/twotothree.html)
.
Using the documentation
=======================
The **docassemble** documentation is intended more as a reference guide than as a manual that you have to read before getting started.
The best way to learn about **docassemble** is to start creating your own interview. Start by following along with the “Hello, world” [tutorial](https://docassemble.org/docs/helloworld.html)
that explains how to create a simple interview. Once you get that working, you can experiment with adding more questions to it.
The best way to learn about more advanced **docassemble** features is to study working examples. The sections of this documentation site contain a number of side-by-side examples comparing source code to screenshots. You can click on the screenshots to run the interviews. The code next to the screenshots is often only an excerpt of the full interview. To see the full source code of the interview, hover over the source code and click the button that appears in the lower right corner. In addition, while you are developing interviews in the [Playground](https://docassemble.org/docs/playground.html)
, you can browse working examples of many of **docassemble**’s features.
There is also a full-featured sample interview linked from the [demonstration page](https://docassemble.org/demo.html)
. While you are using the interview you can click “Source” in the navigation bar to toggle display of the source code for the question and an explanation of the path **docassemble** took to decide to ask that question.
Sections of the documentation
=============================
* Tutorial
* [Hello World](https://docassemble.org/docs/helloworld.html)
* Authoring guide
* [Interviews](https://docassemble.org/docs/interviews.html)
* [Initial Blocks](https://docassemble.org/docs/initial.html)
* [Question Blocks](https://docassemble.org/docs/questions.html)
* [Setting Variables](https://docassemble.org/docs/fields.html)
* [Question Modifiers](https://docassemble.org/docs/modifiers.html)
* [Code](https://docassemble.org/docs/code.html)
* [Interview Logic](https://docassemble.org/docs/logic.html)
* [Markup](https://docassemble.org/docs/markup.html)
* [Documents](https://docassemble.org/docs/documents.html)
* [Objects](https://docassemble.org/docs/objects.html)
* [Groups](https://docassemble.org/docs/groups.html)
* [Functions](https://docassemble.org/docs/functions.html)
* [External Data](https://docassemble.org/docs/external.html)
* [Legal Applications](https://docassemble.org/docs/legal.html)
* [Special Variables](https://docassemble.org/docs/special.html)
* Special features
* [Language Support](https://docassemble.org/docs/language.html)
* [Accessibility](https://docassemble.org/docs/accessibility.html)
* [Roles](https://docassemble.org/docs/roles.html)
* [Background Code](https://docassemble.org/docs/background.html)
* [Machine Learning](https://docassemble.org/docs/ml.html)
* [SMS Interface](https://docassemble.org/docs/sms.html)
* [API](https://docassemble.org/docs/api.html)
* [Custom Front Ends](https://docassemble.org/docs/frontend.html)
* Users
* [User Login](https://docassemble.org/docs/users.html)
* [Live Help](https://docassemble.org/docs/livehelp.html)
* Development
* [Overview](https://docassemble.org/docs/development.html)
* [Playground](https://docassemble.org/docs/playground.html)
* [Packages](https://docassemble.org/docs/packages.html)
* [UI Customization](https://docassemble.org/docs/ui.html)
* [Debugging](https://docassemble.org/docs/errors.html)
* [Support](https://docassemble.org/docs/support.html)
* [Future](https://docassemble.org/docs/future.html)
* [Recipes](https://docassemble.org/docs/recipes.html)
* Administration
* [Overview](https://docassemble.org/docs/admin.html)
* [Installation](https://docassemble.org/docs/installation.html)
* [Docker](https://docassemble.org/docs/docker.html)
* [Configuration](https://docassemble.org/docs/config.html)
* [Scalability](https://docassemble.org/docs/scalability.html)
* [Security](https://docassemble.org/docs/security.html)
* [Change Log](https://docassemble.org/docs/changelog.html)
* [Contributing](https://docassemble.org/docs/contributing.html)
* [Policies](https://docassemble.org/docs/policies.html)
* [License](https://docassemble.org/docs/license.html)
* * *
---
# Hello world example
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/helloworld.md "Edit this page on GitHub")
Hello world example
===================
Contents
--------
* [1 Adding a question](https://docassemble.org/docs/helloworld.html#tocAnchor-1-1)
* [2 Adding some Python code](https://docassemble.org/docs/helloworld.html#tocAnchor-1-2)
* [3 Creating a document](https://docassemble.org/docs/helloworld.html#create%20document)
* [4 Decorate with an image](https://docassemble.org/docs/helloworld.html#tocAnchor-1-4)
* [5 Testing your interview](https://docassemble.org/docs/helloworld.html#testing)
* [6 Packaging your interview](https://docassemble.org/docs/helloworld.html#packaging)
* [6.1 Storing on PyPI](https://docassemble.org/docs/helloworld.html#pypi)
* [6.2 Storing on Github](https://docassemble.org/docs/helloworld.html#github)
Here is a simple interview file that says “Hello, world!” to the user.
---
question: Hello, world!
buttons:
- Exit: exit
mandatory: True
---
To run this, first set up your server using [Docker](https://docassemble.org/docs/docker.html)
.
To get **docassemble** up and running in your web browser, visit [http://localhost](http://localhost/)
if you are running **docassemble** locally (i.e., using Docker Desktop on your computer), or else navigate to your **docassemble** server URL).
Once **docassemble** is up and running in your web browser, click “Log in” in the upper right hand corner. The default username and password are:
* **E-mail**: admin@example.com
* **Password**: password
The default password (“password”) will need to be changed. Change it to something that is at least eight characters long with at least one lowercase letter and one number. You should also change the e-mail address by going to [User List](https://docassemble.org/docs/admin.html#user%20list)
and clicking “Edit” next to the admin@example.com user.
Then, from the menu, select [Playground](https://docassemble.org/docs/playground.html)
. The [Playground](https://docassemble.org/docs/playground.html)
is a “sandbox” area where you can develop interviews and test them, all inside the web browser.

Then, click the [Add](javascript:return(0)) button to create a new interview. Call it “hello.yml.” (`.yml` is the file extension for the data format known as [YAML](https://en.wikipedia.org/wiki/YAML)
.)

Then, copy and paste the interview code above into the editor:

Then, click the [Save](javascript:return(0))
button, followed by the [Save and Run](javascript:return(0))
button.
You should see:

(If you do not have a server yet, you can [try it out here](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/hello.yml)
.)
Adding a question
=================
Now let’s change the interview so that it asks the user a [question](https://docassemble.org/docs/questions.html)
. Edit the interview and change the contents to:
---
question: Hello, ${ planet }!
buttons:
- Exit: exit
mandatory: True
---
question: |
What is your planet's name?
fields:
- Your Planet: planet
---
(If you do not have your own server yet, you can [try it out here](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/hello2.yml)
.)
It should now ask you “What is your planet’s name?” and then greet your world by its name.
Try clicking in the navigation bar. This toggles the display of information that will help you understand how a question came to be asked. This can be helpful for “debugging” your interview. Information about the readability of your interview question is also displayed.

Note that end users will not see the tab; it will only be shown to users if the interview is in the [Playground](https://docassemble.org/docs/playground.html)
, or if the server is [configured as a development server](https://docassemble.org/docs/config.html#debug)
.
In this example, the information explains that the interview tried to show a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question, but couldn’t, because it needed the definition of the variable `planet`. Therefore it looked for a question that offered to define `planet`, and asked that question of the user.
Adding some Python code
=======================
Now let’s extend the interview by adding a [`code`](https://docassemble.org/docs/code.html)
block that makes a calculation based on a number provided by the user.
Change the interview code to the following:
---
question: Hello, ${ planet }!
subquestion: |
I surmise that you have no more than ${ inhabitant_count }
inhabitants.
buttons:
- Exit: exit
mandatory: True
---
question: |
What is your planet's name?
fields:
- Your Planet: planet
---
code: |
if favorite_number == 42:
inhabitant_count = 2
else:
inhabitant_count = 2000 + favorite_number * 45
---
question: What is your favorite number?
fields:
- Number: favorite_number
datatype: number
---
([Try it out here](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/hello3.yml)
.)
Note that the order in which the [question](https://docassemble.org/docs/questions.html)
and [code](https://docassemble.org/docs/code.html)
blocks appear in the [YAML](https://en.wikipedia.org/wiki/YAML)
does not determine the order in which questions are asked. This is because **docassemble** only asks questions as needed and when needed. The path of this interview is driven by the single [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question. In order to say “Hello, \_\_\_\_\_\_”, **docassemble** needs to know what `planet` is, so it asks the question “What is your planet’s name?” Then, in order to say “I surmise that you have no more than \_\_\_\_ inhabitants,” **docassemble** needs to know what `inhabitant_count` is, so it runs the [`code`](https://docassemble.org/docs/code.html)
that computes `inhabitant_count`. However, in order to compute that, **docassemble** needs to know `favorite_number`, so it asks “What is your favorite number?” Then it knows everything it needs to know in order to display the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question.
The [`code`](https://docassemble.org/docs/code.html)
block contains [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code. The syntax follows the rules of [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
. For example, the `==` syntax tests whether the `favorite_number` is 42 or not. The `+` performs addition and the `*` performs multiplication. The `=` sets the value of a variable.
Creating a document[¶](https://docassemble.org/docs/helloworld.html#create%20document)
=======================================================================================
Now let’s provide the user with a [document](https://docassemble.org/docs/documents.html)
by adding an [`attachment`](https://docassemble.org/docs/documents.html#attachment)
.
---
question: Hello, ${ planet }!
subquestion: |
I surmise that you have no more than ${ inhabitant_count }
inhabitants.
attachment:
name: A letter for the inhabitants of ${ planet }
filename: hello
metadata:
SingleSpacing: True
content: |
Dear ${ planet } residents,
Hello to all ${ inhabitant_count } of you.
Goodbye,
Your friend
buttons:
- Exit: exit
mandatory: True
---
question: |
What is your planet's name?
fields:
- Your Planet: planet
---
code: |
if favorite_number == 42:
inhabitant_count = 2
else:
inhabitant_count = 2000 + favorite_number * 45
---
question: What is your favorite number?
fields:
- Number: favorite_number
datatype: number
---
([Try it out here](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/hello4.yml)
.)
This creates a document “from scratch” that is available in PDF or RTF format. The content of the document is contained in the `content` specifier within the [`attachment`](https://docassemble.org/docs/documents.html#attachment)
specifier.
Let’s also try modifying this interview to use a [DOCX template](https://docassemble.org/docs/documents.html#docx%20template%20file)
in order to generate a document that will be available to the user in PDF or DOCX format.
To do this, first open a word processing application capable of saving files in DOCX format (e.g., [Microsoft Word](https://en.wikipedia.org/wiki/Microsoft_Word)
). Create a file that looks like the following:

You can format [this file](https://docassemble.org/img/hello_planet.docx)
however you like; in this example, we have indented some text and inserted an image. The important thing is that the variable names `planet` and `inhabitant_count` are spelled correctly and are enclosed in double curly brackets.
Save the file as a DOCX file (e.g., `hello_planet.docx`).
Now you need to make this DOCX file available to your interview by putting the DOCX file in the [Templates](https://docassemble.org/docs/playground.html#templates)
folder of your [Playground](https://docassemble.org/docs/playground.html)
.
To do this, go to the Folders menu and select “Templates.”

Then, go to “Upload a template file” and click “Choose Files.”

Locate your DOCX file on your computer and select it. Then click [Upload](javascript:return(0))
.
Now you should see the DOCX file listed as one of your Templates.

Then, click [Back](javascript:return(0))
or [Back to Playground](javascript:return(0))
to go back to the main page of the [Playground](https://docassemble.org/docs/playground.html)
.
Now you need to edit the interview so that it uses the DOCX file.
In the block with the [`attachment`](https://docassemble.org/docs/documents.html#attachment)
, replace the `content` specifier with a [`docx template file`](https://docassemble.org/docs/documents.html#docx%20template%20file)
specifier that references the file you uploaded. The interview should look like this:
---
question: Hello, ${ planet }!
subquestion: |
I surmise that you have no more than ${ inhabitant_count }
inhabitants.
attachment:
name: A letter for the inhabitants of ${ planet }
filename: hello
docx template file: hello_planet.docx
buttons:
- Exit: exit
mandatory: True
---
question: |
What is your planet's name?
fields:
- Your Planet: planet
---
code: |
if favorite_number == 42:
inhabitant_count = 2
else:
inhabitant_count = 2000 + favorite_number * 45
---
question: What is your favorite number?
fields:
- Number: favorite_number
datatype: number
---
([Try it out here](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/hello6.yml)
.)
For more information about assembling downloadable documents, see the [documents](https://docassemble.org/docs/documents.html)
section.
Decorate with an image
======================
Now let’s try [decorating](https://docassemble.org/docs/modifiers.html#decoration)
one of the questions with an image.
First you need to upload the image you want to use.
From the [Playground](https://docassemble.org/docs/playground.html)
, go to the Folders menu and select “Static files.” (The files are called “[static](https://docassemble.org/docs/playground.html#static)
” because they do not change, unlike templates, which can produce different files every time.)

Then, go to “Upload a static file” and click “Choose Files.”

After you select the file (or files) on your computer that you want to upload, click [Upload](javascript:return(0))
. You should now see the file listed as one of the “Static files.”

In this example, the file is [globe.svg](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/static/globe.svg)
, a picture of a globe.
Now, click [Back](javascript:return(0))
or [Back to Playground](javascript:return(0))
and add the following to your interview:
---
image sets:
freepik:
images:
earth: globe.svg
attribution: |
Icon made by [Freepik](https://www.flaticon.com/authors/freepik)
---
The file [globe.svg](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/static/globe.svg)
is a copyrighted image obtained from [Freepik](https://www.flaticon.com/authors/freepik)
with an attribution-only license. The [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
block facilitates the use of such images. It defines a set of images, called `freepik`, that share a common attribution. All the images you obtain from this source can be added to this image set, and an appropriate attribution line will be added to the screen whenever the image is used.
Under `images`, we indicate that the shorthand name we will give our image is `earth`.
Now, edit the “What is your planet’s name?” question and add a [`decoration`](https://docassemble.org/docs/modifiers.html#decoration)
line referencing `earth`:
---
question: |
What is your planet's name?
fields:
- Your Planet: planet
decoration: earth
---
Now, when you run the interview, you can see that the image “decorates” the question:

([Try it out here](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/hello5.yml)
.)
For more ideas about how you can extend your interview, check out the examples area of the [Playground](https://docassemble.org/docs/playground.html)
.
Testing your interview[¶](https://docassemble.org/docs/helloworld.html#testing)
================================================================================
Any time you develop an interview, you will want to test it on multiple devices, and ask other people to test it out and give you feedback.
The “Save and Run” button is not the only way to start using an interview in your [Playground](https://docassemble.org/docs/playground.html)
. Any interview in the [Playground](https://docassemble.org/docs/playground.html)
can be started by visiting its hyperlink. To get this hyperlink, right-click on the [Share](https://docassemble.org/docs/helloworld.html#testing)
button and copy the URL to your clipboard. You can share this URL with other people, or bookmark it in your browser. When people visit this URL, they will start an interview session. (Note that this only works if the server you are using is accessible to the user’s device; if you are using a personal computer, your computer’s firewall might prevent other people from accessing it.)
Packaging your interview[¶](https://docassemble.org/docs/helloworld.html#packaging)
====================================================================================
So far, we have been running our interview from the [Playground](https://docassemble.org/docs/playground.html)
, which is a testing area where we can test things, break things, and “play” with different possibilities. If our interview has reached a point where it is flawless and we want to put it into “production” to that users can use it, we need to move it out of the [Playground](https://docassemble.org/docs/playground.html)
and install it in a more “permanent” place.
To do this, we bundle our interview into a “package.” Packages can be installed on the same system, or another system, or they can be shared with other developers or posted on [GitHub](https://github.com/)
.
Go to the Folders menu and select “Packages.”

This will take you to the [“Packages” folder](https://docassemble.org/docs/playground.html#packages)
, where you can create and edit packages.

If there is an existing package, click the [Add](javascript:return(0)) button to create a new package. Otherwise, edit the “New” package.
Call your new package “helloworld.”

On this screen, you can define the characteristics of your package and indicate which resources from the [Playground](https://docassemble.org/docs/playground.html)
should be included in the package.
Under “Interview files,” select your `hello.yml` file.

Under “Static files,” select your `globe.svg` file.

Under “Template files,” select your `hello_planet.docx` file.

If you were creating an actual package for distribution, you would type a careful description of your package and the contents of a “README” file here. But since this is only a tutorial, you can skip that.
At the bottom of the screen, you will see some buttons:
[Save](javascript:return(0))
[Cancel](javascript:return(0))
Click the [Save](javascript:return(0))
button to save your package.
Then, once your package is saved in the system, you will see the following buttons at the top of the screen.

You will see the following buttons at the bottom of the screen.
[Save](javascript:return(0))
[Install](javascript:return(0))
[Download](javascript:return(0))
[Delete](javascript:return(0))
Click the [Install](javascript:return(0))
button. This will take a snapshot of your package, bundle it all up into a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
package, and install that [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
package on the computer.
Now, users can run the interview using a different URL. If your server is `interview.example.com`, users will be able to run the interview by visiting a URL like:
> https://interview.example.com/interview?i=docassemble.helloworld:data/questions/hello.yml
Previously, the URL to your interview ended with something like `?i=docassemble.playground1:hello.yml`. That is a link to the “bleeding edge” version of your interview as it exists in the [Playground](https://docassemble.org/docs/playground.html)
. The link that ends with `?i=docassemble.helloworld:data/questions/hello.yml` is a link to the snapshot that you installed. This will be the “stable” version of your interview. The “filename” of your interview is `docassemble.helloworld:data/questions/hello.yml`, which tells the server “go into the Python package `docassemble.helloworld`, then go into the `data` directory in that package, then go into the `questions` subdirectory under `data`, and access the file `hello.yml` in that subdirectory.”
Alternatively, you can use a more presentable URL, which is like a shorthand version of the above URL:
> https://interview.example.com/start/helloworld/hello/
When you run this interview, the URL in the location bar will switch to https://interview.example.com/run/helloworld/hello/. The difference between `start` and `run` is that the `start` URL will always start a new interview, while the `run` version will resume an interview session that has already started.
It is also possible to make these URLs even shorter by using the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
Configuration directive.
Now that your interview is installed, you can continue to make changes to the [Playground](https://docassemble.org/docs/playground.html)
version, while your users are using the snapshot that you installed. When you have made further modifications and you have a new version you want to make available again, you can just click the “Install” button again.
For serious deployment of interviews, [it is recommended](https://docassemble.org/docs/development.html)
that you use separate “development” and “production” servers.
Another thing you can do from the “Packages” folder is click the [Download](javascript:return(0))
button. This will download the package as a ZIP file called `docassemble-helloworld.zip`.
If you unpack the contents of the ZIP file, you will have a folder called `docassemble-helloworld`. You can make changes to the files, then re-ZIP the `docassemble-helloworld` folder, and install the revised package on a **docassemble** server using the [Package Management](https://docassemble.org/docs/playground.html)
tool. Or, if you want to edit the revised contents of your package in the [Playground](https://docassemble.org/docs/playground.html)
again, click the [Upload](javascript:return(0)) button to upload the contents of your ZIP file into the [Playground](https://docassemble.org/docs/playground.html)
.
Storing on PyPI[¶](https://docassemble.org/docs/helloworld.html#pypi)
----------------------------------------------------------------------
A **docassemble** package is really just a regular [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
package. It follows all of the conventions of [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
software package distribution. That means you can share your package on [PyPI](https://pypi.python.org/pypi)
, the central repository for [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
software. Other people can then install your package on their servers using [pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29)
, just as they would install any [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
software.
This section of the tutorial will explain how to upload the `docassemble.helloworld` package to [PyPI](https://pypi.python.org/pypi)
, but keep in mind that if you try this yourself on a package called `docassemble.helloworld`, you will probably get an error because package names on [PyPI](https://pypi.python.org/pypi)
are unique and the `docassemble.helloworld` package has already been uploaded (namely, by me, when I was creating this tutorial). However, these instructions will work if you use a package name of your own invention.
First, you need to create an account on [PyPI](https://pypi.python.org/pypi)
and obtain an API key. The API key has the form `pypi-` followed by a series of alphanumeric characters.
The **docassemble** [configuration](https://docassemble.org/docs/config.html)
on your server will need to be set up to allow publishing to [PyPI](https://pypi.python.org/pypi)
. To configure this, log in as a user with `admin` [privileges](https://docassemble.org/docs/users.html)
, go to “Configuration” on the menu, and add the following to the [configuration](https://docassemble.org/docs/config.html)
:
pypi: True
If this configuration has been made, you can go to “Profile” from the menu and scroll down to the “PyPI Username” and “PyPI Password” fields. Fill in these fields with `__token__` as the username and your API key as the password. (This is how authentication works with [`twine`](https://pypi.org/project/twine/)
.)

Then, go to the [“Packages” folder](https://docassemble.org/docs/playground.html#packages)
of the **docassemble** [Playground](https://docassemble.org/docs/playground.html)
and open your `docassemble-helloworld` package that you created [above](https://docassemble.org/docs/helloworld.html#packaging)
. At the bottom of the screen you will see a message about whether the package is published on [PyPI](https://pypi.python.org/pypi)
and/or [GitHub](https://github.com/)
.

Press the [PyPI](javascript:return(0))
button to publish the package to [PyPI](https://pypi.python.org/pypi)
.
When the publishing is done, you will see an informational message with the output of the uploading commands. Check this message to see if there are any errors.
If the publishing was successful, then at the bottom of the page describing your package, you should see a message that the package now exists on [PyPI](https://pypi.python.org/pypi)
. (However, sometimes the [PyPI](https://pypi.python.org/pypi)
server is slow to reflect the existence of the package, so you may need to give it a minute or two.)

If you click the link, you can see what the package looks like on the [PyPI](https://pypi.python.org/pypi)
web site.

Now, on the **docassemble** menu (of this server or another server), you can go to Package Management and [install](https://docassemble.org/docs/packages.html)
the package by specifying a “Package on PyPI.”

For more information about uploading packages to [PyPI](https://pypi.python.org/pypi)
, see the [PyPI subsection](https://docassemble.org/docs/packages.html#pypi)
of the [packages](https://docassemble.org/docs/packages.html)
section.
Storing on Github[¶](https://docassemble.org/docs/helloworld.html#github)
--------------------------------------------------------------------------
You can also share your package on [GitHub](https://github.com/)
, a popular [version control system](https://en.wikipedia.org/wiki/Version_control)
that facilitates collaboration.
You will need an account on [GitHub](https://github.com/)
. If you do not have one, you can [create one](https://github.com/join)
.
Your **docassemble** configuration will need to be configured to allow [GitHub](https://github.com/)
integration. The instructions for setting that up are [in the installation section](https://docassemble.org/docs/installation.html#github)
.
Once your server allows [GitHub integration](https://docassemble.org/docs/packages.html#github)
, you can go to “Profile” on the menu, open “Other settings,” and click the link for “GitHub integration.” Follow the instructions to connect your [GitHub](https://github.com/)
account with your **docassemble** account.
Once you have connected your [GitHub](https://github.com/)
account with your **docassemble** account, go to the [“Packages” folder](https://docassemble.org/docs/playground.html#packages)
of the **docassemble** [Playground](https://docassemble.org/docs/playground.html)
and open your `docassemble-helloworld` package that you created [above](https://docassemble.org/docs/helloworld.html#packaging)
. At the bottom of the screen you will see a message about whether the package is published on [GitHub](https://github.com/)
.

Press the [GitHub](javascript:return(0))
button.
You will be asked for a “commit message.” This is a brief, one-line message that describes the changes made to your package since the last time you “committed” changes. Each “commit” is like a snapshot, and the history of “commit” messages is a record of the development of your project. You can give your first commit a simple name:

However, your subsequent “commits” should have meaningful names that describe succinctly how that snapshot is different from the previous snapshot. You should also use the README text box to describe to other people the history of all of your changes.
When you press the “Commit” button, your package will be “pushed” to a “repository” in your [GitHub](https://github.com/)
account. If a repository does not already exist on [GitHub](https://github.com/)
with the name of your package, a new repository will be created.
When the uploading is done, you will see an informational message with the output of the uploading commands. Check this message to see if there are any errors.
If the publishing was successful, then at the bottom of the page describing your package, you should see a message that the package now exists on [GitHub](https://github.com/)
.

If you click the link, you can see what the package looks like on [GitHub](https://github.com/)
:

Now, on the **docassemble** menu, you can go to Package Management and [install](https://docassemble.org/docs/packages.html)
the package using its [GitHub](https://github.com/)
URL.

For more information about uploading packages to [GitHub](https://github.com/)
, see the [GitHub subsection](https://docassemble.org/docs/packages.html#github)
of the [packages](https://docassemble.org/docs/packages.html)
section.
* * *
---
# Question blocks
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/questions.md "Edit this page on GitHub")
Question blocks
===============
Contents
--------
* [1 The question block](https://docassemble.org/docs/questions.html#question)
* [2 The subquestion specifier](https://docassemble.org/docs/questions.html#subquestion)
* [3 The under specifier](https://docassemble.org/docs/questions.html#under)
* [4 The pre specifier](https://docassemble.org/docs/questions.html#pre)
* [5 The post specifier](https://docassemble.org/docs/questions.html#post)
* [6 The right specifier](https://docassemble.org/docs/questions.html#right)
* [7 The css class specifier](https://docassemble.org/docs/questions.html#css%20class)
* [8 The table css class specifier](https://docassemble.org/docs/questions.html#table%20css%20class)
* [9 The question metadata specifier](https://docassemble.org/docs/questions.html#question%20metadata)
* [10 Using questions to set variables](https://docassemble.org/docs/questions.html#tocAnchor-1-10)
* [10.1 True or false: yesno and noyes](https://docassemble.org/docs/questions.html#tocAnchor-1-10-1)
* [10.2 Multiple choice: choices or buttons](https://docassemble.org/docs/questions.html#choices)
* [10.3 Acknowledgement button: continue button field](https://docassemble.org/docs/questions.html#tocAnchor-1-10-3)
* [10.4 One or more fill-in fields: fields](https://docassemble.org/docs/questions.html#tocAnchor-1-10-4)
* [10.5 User’s signature: signature](https://docassemble.org/docs/questions.html#tocAnchor-1-10-5)
* [11 Ending screens](https://docassemble.org/docs/questions.html#ending%20screens)
* [11.1 Special buttons](https://docassemble.org/docs/questions.html#special%20buttons)
* [11.2 Mixing special buttons with buttons that set a variable](https://docassemble.org/docs/questions.html#special%20mixed)
* [11.3 Special screens: event](https://docassemble.org/docs/questions.html#event)
* [12 Include additional buttons on the screen](https://docassemble.org/docs/questions.html#action%20buttons)
* [13 Customizing screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
In the **docassemble** web app, every screen that a user sees is generated by a [`question`](https://docassemble.org/docs/questions.html#question)
block, which is a section of [YAML](https://en.wikipedia.org/wiki/YAML)
code that contains a [`question`](https://docassemble.org/docs/questions.html#question)
specifier. (Well, you can also generate a screen using the [`message()` function](https://docassemble.org/docs/functions.html#message)
, but that is an advanced topic.)
All blocks in an interview file must either be [initial blocks](https://docassemble.org/docs/initial.html)
, [code blocks](https://docassemble.org/docs/code.html)
, or contain a [`question`](https://docassemble.org/docs/questions.html#question)
specifier.
The `question` block[¶](https://docassemble.org/docs/questions.html#question)
==============================================================================
question: |
What is the airspeed velocity of an
unladen swallow?
fields:
- Velocity: swallow_velocity
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question.yml&reset=1 "Click to try this interview")
By including the above [YAML](https://en.wikipedia.org/wiki/YAML)
block in your interview file, you are telling **docassemble** that if it ever needs to know the value of `swallow_velocity`, it should ask the user the question “What is the airspeed velocity of an unladen swallow?” and provide the user with an input box labeled “Velocity” in which the user can type the answer.
Like many things in **docassemble**, the `question` specifier can contain [Markdown](https://daringfireball.net/projects/markdown/)
and [Mako](https://www.makotemplates.org/)
. For example:
question: |
What is the *airspeed* velocity of an
unladen ${ african_or_european }
swallow?
fields:
- Velocity: swallow_velocity
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-markup.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-markup.yml&reset=1 "Click to try this interview")
In this example, the word “airspeed” is italicized, using the [Markdown](https://daringfireball.net/projects/markdown/)
syntax for italics. The type of swallow is given by a variable (`african_or_european`), the value of which (`"African"`) is incorporated using [Mako](https://www.makotemplates.org/)
. (One of the features of [Mako](https://www.makotemplates.org/)
is that anything enclosed in `${ ... }` is evaluated as [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code.)
The `subquestion` specifier[¶](https://docassemble.org/docs/questions.html#subquestion)
========================================================================================
The optional `subquestion` adds text underneath the [`question`](https://docassemble.org/docs/questions.html#question)
. It is typically used to explain the question in more detail, if such an explanation is necessary.
question: |
What is the airspeed velocity of an
unladen swallow?
subquestion: Be careful how you answer.
fields:
- Velocity: swallow_velocity
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/subquestion.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/subquestion.yml&reset=1 "Click to try this interview")
The `under` specifier[¶](https://docassemble.org/docs/questions.html#under)
============================================================================
The optional `under` specifier adds text underneath the buttons.
question: |
What is the airspeed velocity of an
unladen swallow?
fields:
- Velocity: swallow_velocity
under: |
Note: if you did not catch this
movie reference, you need to
improve your cultural literacy.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/under.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/under.yml&reset=1 "Click to try this interview")
The `pre` specifier[¶](https://docassemble.org/docs/questions.html#pre)
========================================================================
The optional `pre` specifier adds text before the `question`.
question: |
What is the airspeed velocity of an
unladen swallow?
fields:
- Velocity: swallow_velocity
pre: |
Note: if you do not catch the
movie reference in this question,
you need to improve your cultural
literacy.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/pre.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/pre.yml&reset=1 "Click to try this interview")
The `post` specifier[¶](https://docassemble.org/docs/questions.html#post)
==========================================================================
The optional `post` specifier adds text after the `question`.
question: |
What is the airspeed velocity of an
unladen swallow?
fields:
- Velocity: swallow_velocity
under: |
Note: if you did not catch this
movie reference, you need to
improve your cultural literacy.
post: |
This is the `post`, which comes
after `under`.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/post.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/post.yml&reset=1 "Click to try this interview")
The `right` specifier[¶](https://docassemble.org/docs/questions.html#right)
============================================================================
The optional `right` specifier adds text on the right-hand side of the screen, or below the question on small screens.
question: |
What is the airspeed velocity of an
unladen swallow?
fields:
- Velocity: swallow_velocity
right: |
### Note
If you did not catch this
movie reference, you need to
improve your cultural literacy.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/right-centered.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/right-centered.yml&reset=1 "Click to try this interview")
If your interview has questions that use the `right` specifier, you might want to set the [`centered`](https://docassemble.org/docs/initial.html#centered)
feature to `False` so that the text on the right is wider.
features:
centered: False
---
question: |
What is the airspeed velocity of an
unladen swallow?
fields:
- Velocity: swallow_velocity
right: |
### Note
If you did not catch this
movie reference, you need to
improve your cultural literacy.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/right.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/right.yml&reset=1 "Click to try this interview")
The `css class` specifier[¶](https://docassemble.org/docs/questions.html#css%20class)
======================================================================================
The optional `css class` specifier can be used to set a [CSS](https://en.wikipedia.org/wiki/CSS)
class for the `
` of the screen of a question.
css class: first-question
mandatory: True
question: |
View the source and look for
the word `first-question`.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/css-class.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/css-class.yml&reset=1 "Click to try this interview")
The `table css class` specifier[¶](https://docassemble.org/docs/questions.html#table%20css%20class)
====================================================================================================
The optional `table css class` specifier can be used to set the [CSS](https://en.wikipedia.org/wiki/CSS)
class of any [Markdown](https://daringfireball.net/projects/markdown/)
\-generated tables that appear in the question. There is no feature in [Markdown](https://daringfireball.net/projects/markdown/)
for specifying a class of a table. The class of tables can be customized at the `question` level, or at the interview level with [`default screen parts`](https://docassemble.org/docs/initial.html#default%20screen%20parts)
, or you can set a server-wide default with the `table css class` [Configuration](https://docassemble.org/docs/config.html)
directive.
The `question metadata` specifier[¶](https://docassemble.org/docs/questions.html#question%20metadata)
======================================================================================================
The optional `question metadata` specifier allows you to associate custom metadata with a `question`. You can use any format [YAML](https://en.wikipedia.org/wiki/YAML)
will accept, and you can use [Mako](https://www.makotemplates.org/)
in text. The metadata will appear in the [JSON](https://en.wikipedia.org/wiki/JSON)
representation of the `question`.
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
question metadata:
inspiration: Sophocles
antagonists:
- Frozen vegetables
- Roasted tree nuts
- ${ favorite_automobile }
Using questions to set variables
================================
Here is a brief summary of the types of questions that can be asked. More detail about how these question types work is provided in the [next section](https://docassemble.org/docs/fields.html)
.
True or false: `yesno` and `noyes`
----------------------------------
A [`yesno`](https://docassemble.org/docs/fields.html#yesno)
question will set a variable to `True` or `False` when a Yes or No button is pressed. [`noyes`](https://docassemble.org/docs/fields.html#noyes)
does the opposite.
question: |
Are you at least 18 years of age?
yesno: over_eighteen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/yesno.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/yesno.yml&reset=1 "Click to try this interview")
Multiple choice: `choices` or `buttons`[¶](https://docassemble.org/docs/questions.html#choices)
------------------------------------------------------------------------------------------------
You can ask a multiple-choice question by providing a list of [`choices`](https://docassemble.org/docs/fields.html#field%20with%20choices)
:
question: |
What type of shoes do you wear?
field: target_variable
choices:
- Sneakers
- Sandals
- Clogs
- Other
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/choices.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/choices.yml&reset=1 "Click to try this interview")
Or, if you would prefer to use one-click buttons instead of “radio buttons” in combination with a “Continue” button, use can use [`buttons`](https://docassemble.org/docs/fields.html#field%20with%20buttons)
instead of [`choices`](https://docassemble.org/docs/fields.html#field%20with%20choices)
.
question: |
What type of belly button do you
have?
field: target_variable
buttons:
- Innie
- Outie
- No belly button
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/buttons.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/buttons.yml&reset=1 "Click to try this interview")
The variable indicated by [`field`](https://docassemble.org/docs/fields.html#field%20with%20choices)
will be set to the value supplied in the [`choices`](https://docassemble.org/docs/fields.html#field%20with%20choices)
/[`buttons`](https://docassemble.org/docs/fields.html#field%20with%20buttons)
list.
Acknowledgement button: `continue button field`
-----------------------------------------------
If you simply want the user to acknowledge something by clicking “Continue,” provide a [`continue button field`](https://docassemble.org/docs/fields.html#field%20continue)
.
The variable indicated by [`continue button field`](https://docassemble.org/docs/fields.html#field%20continue)
will be set to `True` when the user clicks “Continue.”
continue button field: target_variable
question: |
Welcome to the interview.
subquestion: |
When you are ready to begin, press
Continue.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/continue.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/continue.yml&reset=1 "Click to try this interview")
One or more fill-in fields: [`fields`](https://docassemble.org/docs/fields.html#fields)
----------------------------------------------------------------------------------------
You can ask the user to fill in multiple fields using [`fields`](https://docassemble.org/docs/fields.html#fields)
.
question: |
What are your favorite things to eat?
subquestion: |
Please be specific.
fields:
- Vegetable: target_variable
- Fruit: other_target_variable
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/text-field.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/text-field.yml&reset=1 "Click to try this interview")
There are many other types of input that you can gather with [`fields`](https://docassemble.org/docs/fields.html#fields)
, including [large text areas](https://docassemble.org/docs/fields.html#area)
, [file uploads](https://docassemble.org/docs/fields.html#file)
, [radio buttons](https://docassemble.org/docs/fields.html#radio)
, and [checkboxes](https://docassemble.org/docs/fields.html#fields%20checkboxes)
. There are a variety of ways you can insert text into the list of fields to help guide the user. If you know [HTML](https://en.wikipedia.org/wiki/HTML)
, you can insert arbitrary [HTML](https://docassemble.org/docs/fields.html#html)
, [CSS](https://docassemble.org/docs/modifiers.html#css)
, and [JavaScript](https://docassemble.org/docs/modifiers.html#script)
. Using the [`show if`](https://docassemble.org/docs/fields.html#show%20if)
feature, you can cause fields to appear or disappear depending on the values of other fields.
User’s signature: `signature`
-----------------------------
You can ask the user to write his or her signature using [`signature`](https://docassemble.org/docs/fields.html#signature)
:
question: |
Sign your name
subquestion: |
By signing your name, you agree to
our terms and conditions.
signature: target_variable
under: |
${ user }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/signature.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/signature.yml&reset=1 "Click to try this interview")
The signature will be stored as an image file in the variable indicated by [`signature`](https://docassemble.org/docs/fields.html#signature)
.
Ending screens[¶](https://docassemble.org/docs/questions.html#ending%20screens)
================================================================================
Sometimes, the purpose of your [`question`](https://docassemble.org/docs/questions.html#question)
is not to gather information but to present an end screen to the user. You can create such “questions” by marking them as [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
:
question: |
Congratulations!
subquestion: |
You are all done with the interview.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/terminal-screen.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/terminal-screen.yml&reset=1 "Click to try this interview")
If you have more than one possible ending screen in your interview, use the [`event`](https://docassemble.org/docs/questions.html#event)
feature described below.
Special buttons[¶](https://docassemble.org/docs/questions.html#special%20buttons)
----------------------------------------------------------------------------------
Using the [`buttons`](https://docassemble.org/docs/questions.html#buttons)
or [`choices`](https://docassemble.org/docs/questions.html#choices)
syntax described above, you can add special buttons to a screen. These buttons are special because they do not set a variable.
These buttons are particularly useful on “terminal” screens.
question: We are all done.
buttons:
- Exit: exit
- Restart: restart
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/exit-buttons.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/exit-buttons.yml&reset=1 "Click to try this interview")
The above example allows the user to “exit” the interview (i.e., to be redirected to a different web site) or “restart” the interview (i.e., go back to the beginning, with all of the answers forgotten).
There are six special button functions:
* `restart`
* `new_session`
* `exit`
* `logout`
* `exit_logout`
* `leave`
* `continue`
* `refresh`
* `signin`
`restart` resets the user’s variable store, except that any parameters that were originally passed through as URL parameters will be used again. The user is redirected to the first question of the interview.
`new_session` does not disturb the current interview session, but starts a new session for the same interview, with the same URL parameters and Referer.
`exit` means that the user’s variable store will be erased and the user will be redirected either to the URL given by the associated `url` text, or if no `url` is defined, to the [`exit url`](https://docassemble.org/docs/initial.html#exit%20url)
defined in the [`metadata`](https://docassemble.org/docs/initial.html#metadata)
, or if that does not defined, then to the [`exitpage`](https://docassemble.org/docs/config.html#exitpage)
defined in the [configuration](https://docassemble.org/docs/config.html)
. If the user tries to come back to the interview again, he will start the interview again, as though it had never been started. Original URL parameters will be lost.
For example:
question: |
Congratulations, you found Nemo!
buttons:
- Try again: restart
- Learn More: exit
url: https://en.wikipedia.org/wiki/Amphiprioninae
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/exit-url.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/exit-url.yml&reset=1 "Click to try this interview")
[Mako](https://www.makotemplates.org/)
can be used in the `url` text.
`logout` logs the user out, if the user is logged in.
`exit_logout` does the same thing as `exit`, except that it also logs the user out, if the user is logged in.
`leave` works like `exit` except that the user’s variable store will be left intact. This means that if the user comes back to the interview again, he will pick up where he left off.
`continue` means that **docassemble** will move on from the question without really doing anything. Here is an example of when it is useful:
mandatory: True
code: |
menu_items = [ action_menu_item('Visit Cow', 'see_cow') ]
---
event: see_cow
question: |
Moo!
subquestion: |
[FILE cow.jpg]
Cow illustration designed by
[Freepik](http://www.freepik.com/free-photos-vectors/animal)
buttons:
- Continue: continue
---
question: Welcome to the interview
subquestion: |
Check out the menu in the
upper-right corner.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/menu-item.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/menu-item.yml&reset=1 "Click to try this interview")
This example uses the [actions](https://docassemble.org/docs/functions.html#actions)
feature and the [`menu_items` special variable](https://docassemble.org/docs/special.html#menu_items)
to present a special screen that shows a picture. This screen does not set any variable, but the user can click the button to “continue” with the normal course of the interview.
The `continue` button can also be used with a `question` that is shown because it is [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
.
mandatory: True
question: |
Please note that this app is only right for you if
you are a bear.
buttons:
- I am not a bear: exit
- Continue: continue
---
mandatory: True
question: |
Instructions for bears
subquestion: |
Grip the doorknob and turn it.
Then look for the pantry. That is where they keep the food.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mandatory-continue.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mandatory-continue.yml&reset=1 "Click to try this interview")
While using `continue` can be useful sometimes, it is generally not a good idea to structure interviews around `question`s that use `continue`. These `question`s cannot use the [`generic object` modifier](https://docassemble.org/docs/fields.html#generic)
or [index variables](https://docassemble.org/docs/fields.html#index%20variables)
.
`refresh` re-runs the [interview logic](https://docassemble.org/docs/logic.html)
. It has much the same effect as refreshing the page in the browser. It is useful in multi-user interviews when the user is waiting for another user to finish entering information. It can also be useful in interviews that use external data sources.
mandatory: True
code: |
tell_time
---
event: tell_time
question: |
The current time is
${ current_datetime().format_time() }.
buttons:
- Tell me again: refresh
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/refresh.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/refresh.yml&reset=1 "Click to try this interview")
`signin` redirects the user to the **docassemble** sign-in page.
mandatory: True
code: |
if user_logged_in():
goodbye_page
else:
sign_in_page
---
question: |
Please sign in
buttons:
- Sign in: signin
sets: sign_in_page
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/signin.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/signin.yml&reset=1 "Click to try this interview")
Instead of using [`buttons`](https://docassemble.org/docs/questions.html#buttons)
, you can use [`choices`](https://docassemble.org/docs/questions.html#choices)
to get a radio list instead of a selection of buttons.
question: We are all done.
choices:
- Exit: exit
- Restart: restart
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/exit-choices.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/exit-choices.yml&reset=1 "Click to try this interview")
The functionality is the same.
The [`command()`](https://docassemble.org/docs/functions.html#command)
function allows you to do similar things with Python code. In addition, the [`url_of()`](https://docassemble.org/docs/functions.html#url_of)
function provides URLs that accomplish a similar purposes.
Mixing special buttons with buttons that set a variable[¶](https://docassemble.org/docs/questions.html#special%20mixed)
------------------------------------------------------------------------------------------------------------------------
Note that since “special” buttons do not set a variable, you should not include a [`field`](https://docassemble.org/docs/fields.html#field%20with%20choices)
in the [`question`](https://docassemble.org/docs/questions.html#question)
. If you include a [`field`](https://docassemble.org/docs/fields.html#field%20with%20choices)
in the [`question`](https://docassemble.org/docs/questions.html#question)
, **docassemble** will assume you are creating an ordinary [multiple choice question](https://docassemble.org/docs/questions.html#buttons)
.
If you wish to mix “special” buttons with buttons that set a variable, you can use the [`command()`](https://docassemble.org/docs/functions.html#command)
function in [`code`](https://docassemble.org/docs/code.html#code)
to trigger the same effects as clicking on a “special” button.
The following example uses a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html#code)
block to determine the course of the interview. Depending on the answer to an ordinary [multiple choice question](https://docassemble.org/docs/questions.html#buttons)
, it either exits from the interview or proceeds to another screen.
mandatory: True
code: |
if user_choice == 'exit':
command('exit')
elif user_choice == 'proceed':
final_screen
---
question: |
Warning!
subquestion: |
Proceeding with this interview may
result in despair-inducing levels
of liability.
field: user_choice
buttons:
- Exit: exit
- Keep going: proceed
---
event: final_screen
question: |
Hey, I warned you.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/exit-buttons-mixed-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/exit-buttons-mixed-code.yml&reset=1 "Click to try this interview")
Another alternative is to use [multiple-choice buttons that run code](https://docassemble.org/docs/fields.html#code%20button)
:
question: |
Warning!
subquestion: |
Proceeding with this interview may
result in despair-inducing levels
of liability.
buttons:
- I understand:
code: |
attitude = 'careful'
- I do not care:
code: |
attitude = 'reckless'
- Get me out of here: exit
---
mandatory: true
question: |
% if attitude == 'reckless':
You are foolish!
% else:
Ok, we are proceeding with caution.
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/exit-buttons-mixed.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/exit-buttons-mixed.yml&reset=1 "Click to try this interview")
Special screens: `event`[¶](https://docassemble.org/docs/questions.html#event)
-------------------------------------------------------------------------------
Some screens are shown to the user when a special event occurs.
For example, if you have a [multi-user interview](https://docassemble.org/docs/roles.html)
and the interview reaches a point where the user cannot proceed until the other interviewee answers a question, the interview will present the user with a [`question`](https://docassemble.org/docs/questions.html#question)
that explains why he or she needs to wait.
Or, you might want to provide the user with the ability to review his or her answers on a single screen, using the [`review`](https://docassemble.org/docs/fields.html#review)
functionality.
The [`event`](https://docassemble.org/docs/fields.html#event)
specifier advertises to the interview logic that the question should be asked if a special event occurs.
question: |
This is a special screen.
event: show_special_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/event-example.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/event-example.yml&reset=1 "Click to try this interview")
This specifier can be used to indicate an [ending screen](https://docassemble.org/docs/questions.html#ending%20screens)
, where your interview has more than one possible ending screen and you use [interview logic](https://docassemble.org/docs/logic.html)
to direct the user to the appropriate screen.
question: |
Which door do you choose?
field: door_choice
choices:
- Door Number 1
- Door Number 2
---
mandatory: True
code: |
if door_choice == 'Door Number 1':
good_luck
if door_choice == 'Door Number 2':
bad_luck
---
question: |
You walk into a field of clover.
event: good_luck
---
question: |
You walk into a stinky swamp.
event: bad_luck
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/doors.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/doors.yml&reset=1 "Click to try this interview")
In this example, `good_luck` and `bad_luck` are actually names of [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
variables. However, they are [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
variables that will never be defined. If a reference to these variables is made, as it is in the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html#code)
block, **docassemble** will locate the `good_luck` and `bad_luck` questions, just as it looks for questions that offer to define any variable.
See [interview logic](https://docassemble.org/docs/logic.html)
for more information about how to give direction to your interview by adding [`code`](https://docassemble.org/docs/code.html#code)
blocks that are marked with the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
modifier.
The `event` specifier is also used to create screens that the user can reach from the menu or from hyperlinks embedded in question text. For more information, see [`event`](https://docassemble.org/docs/fields.html#event)
, [`url_action()`](https://docassemble.org/docs/functions.html#url_action)
, [`process_action()`](https://docassemble.org/docs/functions.html#process_action)
, [`action_menu_item()`](https://docassemble.org/docs/functions.html#action_menu_item)
, and [`menu_items`](https://docassemble.org/docs/special.html#menu_items)
.
Include additional buttons on the screen[¶](https://docassemble.org/docs/questions.html#action%20buttons)
==========================================================================================================
In many [`question`](https://docassemble.org/docs/questions.html#question)
s, the only button the user can click is “Continue.” You can set a variable using a [`buttons`](https://docassemble.org/docs/fields.html#field%20with%20buttons)
specifier with a `field`. You can also use a [`buttons`](https://docassemble.org/docs/questions.html#buttons)
screen that does not set a variable. You can also use [`question back button`](https://docassemble.org/docs/initial.html#question%20back%20button)
to move the “Back” button to the buttons area, or [`question help button`](https://docassemble.org/docs/initial.html#question%20help%20button)
to move the interview help button to the buttons area.
If you want to include additional button choices on a screen, you can use `action buttons`. When the user clicks a button it will run an [action](https://docassemble.org/docs/functions.html#actions)
in the interview.
question: |
I will give you
${ quantity_noun(num_apples, 'apple') }.
field: show_quantity
action buttons:
- label: More
action: change_quantity
arguments:
amount: 1
color: success
css class: quantity-up
icon: chevron-up
- label: Less
action: change_quantity
arguments:
amount: -1
color: danger
css class: quantity-down
icon: chevron-down
---
event: change_quantity
code: |
num_apples += action_argument('amount')
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/action-buttons.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/action-buttons.yml&reset=1 "Click to try this interview")
For each button, the required items are `action` and `label`. You can optionally include `color` to indicate the Bootstrap color of the button (`primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `link`, or `dark`). The default color is `primary`. You can also optionally include `icon` to indicate a Font Awesome icon name to include in the button. If your `action` requires arguments, you can indicate the arguments using `arguments`. To specify that a particular button should have a particular CSS class, you can specify `css class`. You can use [Mako](https://www.makotemplates.org/)
templating in any of these elements.
If you want to define the buttons programmatically, you can set `action buttons` to a dictionary where the only item is `code`.
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
---
question: |
Thank you for that information.
field: thank_user
action buttons:
code: |
[{'action': 'eat',\
'arguments': {'fruit': favorite_fruit},\
'label': 'Eat ' + favorite_fruit,\
'color': 'secondary',\
'css class': 'eat-button'}]
---
event: eat
code: |
log('Nom nom nom. That ' + action_argument('fruit') + ' was good.', "info")
set_save_status('ignore')
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/action-buttons-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/action-buttons-code.yml&reset=1 "Click to try this interview")
When you use `code` to define the `action buttons`, you need to provide a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
expression that evaluates to a list of dictionaries, where each dictionary has the items `action` and `label` at a minimum.
By default, the buttons indicated by `action buttons` will be placed after the “Continue” button or any other buttons specified in the `question`, and before the [`question help button`](https://docassemble.org/docs/initial.html#question%20help%20button)
(if present). If you want a button to appear before the standard buttons of the `question`, set `placement` to `before`.
question: |
I will give you
${ quantity_noun(num_apples, 'apple') }.
field: show_quantity
action buttons:
- label: More
action: change_quantity
arguments:
amount: 1
color: success
icon: chevron-up
placement: before
- label: Less
action: change_quantity
arguments:
amount: -1
color: danger
icon: chevron-down
placement: before
---
event: change_quantity
code: |
num_apples += action_argument('amount')
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/action-buttons-before.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/action-buttons-before.yml&reset=1 "Click to try this interview")
If you set the `action` of an button to a web URL (beginning with `http://`, `https://`, `/`, or `?`) or a [JavaScript](https://en.wikipedia.org/wiki/Javascript)
URL (beginning with `javascript:`), then the button will act like a hyperlink with the value of the `action` as the `href`.
question: |
You may wish to wait until
${ today().plus(weeks=3) }
to file your complaint.
subquestion: |
It is up to you whether you wish
to continue with the interview.
If you leave, you will be directed
back to the web site.
field: wishes_to_continue
action buttons:
- label: Come back later
action: https://docassemble.org
color: warning
icon: sign-out-alt
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/action-buttons-http.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/action-buttons-http.yml&reset=1 "Click to try this interview")
If you want the link to open in another window, use `new window: True`.
By default, if the user starts an action and then starts a second action without finishing the first, then when the user finishes the second action, the first action will be resumed. This is sometimes desirable and sometimes not. If you want the action launched by your button to be the only active action, set `forget prior: True` in the action button definition; then all prior actions will be forgotten.
If you want a button to be included only conditionally, set `show if` to a Python expression. If the expression evaluates to a false value, the button will not be shown. When you define buttons with `code`, you can include `show if` in a `dict` for a button, and if it is a false value, the button will be omitted.
Customizing screen parts[¶](https://docassemble.org/docs/questions.html#screen%20parts)
========================================================================================
When the user looks at a screen in an interview, there are many different “parts” of the screen. Above, you were introduced to the [`question`](https://docassemble.org/docs/questions.html#question)
, [`subquestion`](https://docassemble.org/docs/questions.html#subquestion)
, [`under`](https://docassemble.org/docs/questions.html#under)
, [`right`](https://docassemble.org/docs/questions.html#right)
, and [`css class`](https://docassemble.org/docs/questions.html#css%20class)
parts.
There are other parts of the screen as well, which by default are empty or have a useful default. The following interview demonstrates where each part is located. Try out the interview and look at the results with different screen sizes.
metadata:
title: Title
short title: Short title
subtitle: |
This is the subtitle part.
pre: |
This is the pre part.
submit: |
This is the submit part.
post: |
This is the post part.
under: |
This is the under part.
right: |
This is the right part.
exit link: leave
exit label: |
Exit label
help label: |
Help label
help button color: warning
continue button label: |
Continue button label
continue button color: success
resume button label: |
Resume button label
resume button color: info
back button label: |
Back button label
back button color: danger
footer: |
This is the footer part.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/metadata-screen-parts.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/metadata-screen-parts.yml&reset=1 "Click to try this interview")
* The `title` is the “long title” that appears in the upper-left on a large screen.
* The `short title` appears in the upper-left on a small screen. If not provided, the `title` is used in its place.
* The `title url` is the URL to which the user will be directed if the user clicks on the title. The default is that clicking the title does nothing.
* The `title url opens in other window` can be set to `False` if you do not want the `title url` to open in new window or tab.
* The `subtitle` is not visible on the screen, but can be seen in the list of [Available Interviews](https://docassemble.org/docs/admin.html#available%20interviews)
.
* The `logo`, if set, will replace the `title` and `short title` with custom HTML content, such as an image. This HTML is placed inside of a `` element, so it is important to only use “inline” HTML elements. You can [provide CSS](https://docassemble.org/docs/initial.html#css)
to the interview and reference [CSS](https://en.wikipedia.org/wiki/CSS)
classes in your `logo`. Ideally the logo should be no taller than 20 pixels. If the `logo` is taller, the navigation bar will expand to fit, and you will need to [adjust the CSS](https://docassemble.org/docs/config.html#bootstrap%20theme)
to specify different values for `.da-pad-for-navbar` and `.da-top-for-navbar`.
* The `short logo`, if set, will replace the `logo` on a small screen.
* The `pre` area is above the [`question`](https://docassemble.org/docs/questions.html#question)
.
* The `submit` area is above the buttons, if the screen has buttons.
* The `under` area is directly below the buttons.
* The `right` area is to the right, but if the screen is too small, it wraps and appears under the `under` area.
* The `post` area is below everything except for image attributions, which are below the `post` area.
* The `footer` area is at the bottom of the screen, in a 60px tall box with the Bootstrap `light` color. If a `global footer` or `main page footer` is defined in the [Configuration](https://docassemble.org/docs/config.html)
, you can turn off the default footer by setting the `footer` area to `off`.
* The `exit link` is not a visible component, but rather a value that is either `exit` or `leave`. It controls the operation of the exit link in the upper right corner, which is present if [`show login`](https://docassemble.org/docs/initial.html#show%20login)
is set to `False`.
* The `exit label` is the visible label of the exit link in the upper right corner, which is present if [`show login`](https://docassemble.org/docs/initial.html#show%20login)
is set to `False`.
* The `exit url` is a URL to which the user will be directed when they click the exit link.
* The `help label` is the default label for the “help” tab. It can be overridden by a `label` specifier inside a [`help`](https://docassemble.org/docs/modifiers.html#help)
modifier. If you set a [`question help button`](https://docassemble.org/docs/initial.html#question%20help%20button)
, the `help label` will be used for the help button, while the label for the help tab will be the less conspicuous word “Help.”
* The `help button color` is the [Bootstrap color](https://getbootstrap.com/docs/5.2/customize/color/)
of the help button that appears when you set a [`question help button`](https://docassemble.org/docs/initial.html#question%20help%20button)
is enabled. This also affects the color of the question mark icon that appears when a choice in a multiple choice list has help text associated with it.
* The `continue button label` is the label for the “Continue” button. If the `question` is a [`review`](https://docassemble.org/docs/fields.html#review)
question, this is called the `resume button label`.
* The `continue button color` is the [Bootstrap color](https://getbootstrap.com/docs/5.2/customize/color/)
of the “Continue” button. If the `question` is a [`review`](https://docassemble.org/docs/fields.html#review)
question, this is called the `resume button color`.
* The `back button label` is the label for the back button that appears within the question itself when [`question back button`](https://docassemble.org/docs/initial.html#question%20back%20button)
is enabled.
* The `back button color` is the [Bootstrap color](https://getbootstrap.com/docs/5.2/customize/color/)
of the back button that appears within the question itself when [`question back button`](https://docassemble.org/docs/initial.html#question%20back%20button)
is enabled.
* The `corner back button label` is the label for the back button that appears in the upper-left corner.
* The `css class` will be added to the classes of the `` of the question.
* The `table css class` will be the class of any `
` elements created based on [Markdown](https://daringfireball.net/projects/markdown/)
tables. You can set this to `table table-bordered` for a Bootstrap “bordered” table. You can also use `table css class` to specify a class for the `` within the table. If you set `table css class` to `table table-bordered, thead-dark`, then the `
` will have the class `table table-bordered` and the `` will have the class `thead-dark`.
* The `navigation bar html` lets you add HTML to the navigation bar. The HTML will be inserted into a `
` element, so for best results, the [HTML](https://en.wikipedia.org/wiki/HTML)
should consist of one or more items in a form like `
`. For more information about how these [CSS](https://en.wikipedia.org/wiki/CSS)
classes work, see the documentation for the [Bootstrap Navbar](https://getbootstrap.com/docs/5.2/components/navbar/#supported-content)
. On small screens, the HTML is shown when the user clicks the toggler icon. You may wish to use `d-none d-md-block` classes to hide the HTML when the screen is small. Make sure your HTML does not contain any errors, or else the [HTML](https://en.wikipedia.org/wiki/HTML)
of the entire screen could be affected.
There are a variety of ways that you can specify what content should appear in these areas. These ways range from question-specific specifiers to interview-wide defaults to server-wide defaults. You should pick whatever method works best for your purposes.
You can customize many of the screen parts as part of a `question` block. As explained above, there are specifiers for the [`question`](https://docassemble.org/docs/questions.html#question)
, [`subquestion`](https://docassemble.org/docs/questions.html#subquestion)
, [`under`](https://docassemble.org/docs/questions.html#under)
, and [`right`](https://docassemble.org/docs/questions.html#right)
parts. There are also modifiers for [`continue button label`](https://docassemble.org/docs/modifiers.html#continue%20button%20label)
, [`continue button color`](https://docassemble.org/docs/modifiers.html#continue%20button%20color)
, [`resume button label`](https://docassemble.org/docs/fields.html#resume%20button%20label)
, [`resume button color`](https://docassemble.org/docs/modifiers.html#resume%20button%20color)
, [`back button label`](https://docassemble.org/docs/modifiers.html#back%20button%20label)
, and [`corner back button label`](https://docassemble.org/docs/modifiers.html#back%20button%20label)
. When you set question-specific [`help`](https://docassemble.org/docs/modifiers.html#help)
, you can indicate a `label` that will be used as a label for the help tab or the help button.
There are three methods for specifying interview-wide default values for parts of the screen:
* Using the [`set_parts()`](https://docassemble.org/docs/functions.html#set_parts)
function in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code to set default values for the screen parts. You can use [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
logic and the [`get_language()`](https://docassemble.org/docs/functions.html#get_language)
function to set different values for different languages. [Mako](https://www.makotemplates.org/)
templating is not supported and formatting must use raw [HTML](https://en.wikipedia.org/wiki/HTML)
.
* Using a [`default screen parts`](https://docassemble.org/docs/initial.html#default%20screen%20parts)
block to specify dynamic content using [Mako](https://www.makotemplates.org/)
templating and [Markdown](https://daringfireball.net/projects/markdown/)
formatting, which is re-evaluated every time the screen loads. Using the [`language`](https://docassemble.org/docs/modifiers.html#language)
modifier (or a [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block in a separate [YAML](https://en.wikipedia.org/wiki/YAML)
file), you can write different [`default screen parts`](https://docassemble.org/docs/initial.html#default%20screen%20parts)
blocks for different languages.
* Using a [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block to specify static content that is the same for all sessions of the interview. [Mako](https://www.makotemplates.org/)
templating is not supported and formatting must use raw [HTML](https://en.wikipedia.org/wiki/HTML)
. You can specify different content to be used for different languages. An advantage of [`metadata`](https://docassemble.org/docs/initial.html#metadata)
over [`default screen parts`](https://docassemble.org/docs/initial.html#default%20screen%20parts)
is that it is visible to the [Available Interviews](https://docassemble.org/docs/admin.html#available%20interviews)
list, whereas other methods of setting defaults are not.
Finally, there is a server-wide method for specifying default values, which is to set the [Configuration](https://docassemble.org/docs/config.html)
directives [`main page pre`](https://docassemble.org/docs/config.html#main%20page%20pre)
, [`main page post`](https://docassemble.org/docs/config.html#main%20page%20post)
, etc. [Mako](https://www.makotemplates.org/)
templating is not supported and formatting must use raw [HTML](https://en.wikipedia.org/wiki/HTML)
. These directives allow you to set different content for different languages.
These different methods override each other in this order. A [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block will override [Configuration](https://docassemble.org/docs/config.html)
directives, a [`default screen parts`](https://docassemble.org/docs/initial.html#default%20screen%20parts)
block will override a [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block, [`set_parts()`](https://docassemble.org/docs/functions.html#set_parts)
will override a [`default screen parts`](https://docassemble.org/docs/initial.html#default%20screen%20parts)
block, and [`question`](https://docassemble.org/docs/questions.html#question)
\-specific specifiers will override parts specified by [`set_parts()`](https://docassemble.org/docs/functions.html#set_parts)
.
For more information about each method, see its documentation.
* * *
---
# How to write an interview
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/interviews.md "Edit this page on GitHub")
How to write an interview
=========================
Contents
--------
* [1 What is a docassemble interview?](https://docassemble.org/docs/interviews.html#whatis)
* [2 The contents of an interview file](https://docassemble.org/docs/interviews.html#simple%20interview)
* [3 Brief introduction to YAML](https://docassemble.org/docs/interviews.html#yaml)
* [4 How to develop your own interviews](https://docassemble.org/docs/interviews.html#htdevelop)
* [5 How you run a docassemble interview](https://docassemble.org/docs/interviews.html#invocation)
* [5.1 Embedding the interview into a web page with an iframe](https://docassemble.org/docs/interviews.html#iframe)
* [5.2 Embedding the interview into a web page directly](https://docassemble.org/docs/interviews.html#div)
* [5.3 Starting an interview from the beginning](https://docassemble.org/docs/interviews.html#reset)
* [5.4 Resuming an interview with a POST request](https://docassemble.org/docs/interviews.html#resume)
* [6 How answers are stored](https://docassemble.org/docs/interviews.html#howstored)
* [7 Leaving an interview and coming back](https://docassemble.org/docs/interviews.html#comingback)
* [8 Using Jinja2 templating to build the YAML of an interview](https://docassemble.org/docs/interviews.html#jinja2)
What is a **docassemble** interview?[¶](https://docassemble.org/docs/interviews.html#whatis)
=============================================================================================
An “interview” in **docassemble** is a [YAML](https://en.wikipedia.org/wiki/YAML)
file that **docassemble** reads, and on the basis of what it finds, asks questions of a user.
**docassemble** stores the user’s answers in “variables.” The values of these variables may be incorporated into the the text of [questions](https://docassemble.org/docs/questions.html)
, or into the text of [documents](https://docassemble.org/docs/documents.html)
.
The interview can ask users different questions depending on what the answers to earlier questions were.
The contents of an interview file[¶](https://docassemble.org/docs/interviews.html#simple%20interview)
======================================================================================================
The interview file is a series of possible questions that could potentially be asked, arranged in no particular order. Which questions will be asked, and the order in which they are asked, will be determined by **docassemble**. All you need to do is give **docassemble** an end goal.
The end goal might be as simple as “show the exit screen.” This will instruct **docassemble** to try to show the exit screen. But **docassemble** will doubtless find that in order to show the exit screen, it will need some piece of information. It will look for a question in the [YAML](https://en.wikipedia.org/wiki/YAML)
file that will provide that information, and it will try to ask that question. But it may find that in order to ask that question, it needs to know another piece of information, and it will look for a question that provides that information, and so forth and so on. The first question will turn out to be something basic, like “What is your name?” and **docassemble** might not reach the exit screen until 20 questions have been asked and answered.
In addition to questions, the [YAML](https://en.wikipedia.org/wiki/YAML)
file can contain bits of logic, written as lines of [Python](https://www.python.org/)
code. For example:
code: |
if user.age >= 65:
recommended_insurance = "Medicare"
elif user.age < 18:
if household.is_low_income:
recommended_insurance = "CHIP"
else:
recommended_insurance = "parent coverage"
else:
if household.is_low_income:
recommended_insurance = "Medicaid"
else:
recommended_insurance = "Private Insurance"
If the interview ever needs to know the recommended insurance, it will run this code. If it does not know the user’s age, it will ask. If the user is under 65, **docassemble** will ask questions to determine whether the household is low-income.
A [YAML](https://en.wikipedia.org/wiki/YAML)
interview file is simply a text file consisting of “blocks” separated by `---`. For example, this interview has three blocks:
question: What is your favorite animal?
fields:
- Animal: favorite_animal
---
question: What is your favorite vegetable?
fields:
- Vegetable: favorite_vegetable
---
mandatory: True
question: What a coincidence!
subquestion: |
My favorite animal is the ${ favorite_animal }, too!
buttons:
- Exit: exit
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/animal.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/animal.yml&reset=1 "Click to try this interview")
The first block is a “question” that defines the variable `favorite_animal`.
The second block is a “question” that defines the variable `favorite_vegetable`.
The third block is a “question” that is marked as `mandatory`. This is not really a question, since it offers the user no option except clicking the “Exit” button. It refers to the variable `favorite_animal`.
When **docassemble** presents this interview to the user, it follows these steps:
1. It scans the file and processes everything that is “[`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
.” It treats everything else as optional.
2. It finds a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question in the third block and tries to ask the question.
3. It can’t assemble the question because `favorite_animal` is not defined, so it looks for a question that defines `favorite_animal`.
4. It looks through the blocks for a question that defines `favorite_animal`, and finds it in the first block.
5. It asks the user for his or her favorite animal, and goes back to step 1. This time around, it is able to ask the `mandatory` question, and the interview stops there because the only thing the user can do is press the “Exit” button.
The order of the blocks in the file is irrelevant; **docassemble** would do the same thing regardless of the order of the blocks.
Note that the second block, containing the question about the user’s favorite vegetable, was never used because it was never needed.
This is a very simple interview; there are more types of blocks that you can write. These blocks are explained in the following sections:
* [Initial Blocks](https://docassemble.org/docs/initial.html)
- Explains special blocks you can write that have an effect on whole interview.
* [Question Blocks](https://docassemble.org/docs/questions.html)
- Explains the basics of the [`question`](https://docassemble.org/docs/questions.html#question)
block, which presents a screen to the user (which usually asks a question but does not need to).
* [Setting Variables](https://docassemble.org/docs/fields.html)
- Explains how to collect information from users using `question` blocks.
* [Question Modifiers](https://docassemble.org/docs/modifiers.html)
- Explains ways you can enhance questions with special features, for example by adding help text or icons.
* [Code](https://docassemble.org/docs/code.html)
- Explains `code` blocks, which are like `question`s except that instead of presenting something to the user, they run [Python](https://www.python.org/)
code that defines variables or does other things that computer code can do.
* [Interview Logic](https://docassemble.org/docs/logic.html)
- Explains [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
and [`initial`](https://docassemble.org/docs/logic.html#initial)
blocks and how **docassemble** processes your interview [YAML](https://en.wikipedia.org/wiki/YAML)
to produce an interview.
* [Markup](https://docassemble.org/docs/markup.html)
- Explains how to change the formatting of text in **docassemble**.
* [Documents](https://docassemble.org/docs/documents.html)
- Explains how to assemble documents in PDF and RTF format based on the user’s answers to the interview questions.
* [Objects](https://docassemble.org/docs/objects.html)
- Explains the use of Python objects to simplify the way information is organized.
* [Groups](https://docassemble.org/docs/groups.html)
- Explains how to gather information into special variables that contain zero or more items representing a group of some sort.
* [Functions](https://docassemble.org/docs/functions.html)
- Explains how to use special [Python](https://www.python.org/)
functions to simplify and generalize the way questions are asked.
* [Legal Applications](https://docassemble.org/docs/legal.html)
- Explains some special objects types that are useful for interviews created by legal practitioners.
* [Special Variables](https://docassemble.org/docs/special.html)
- Describes variables that have special properties, as well as variable names you aren’t allowed to use because they would conflict with the functionality of **docassemble** and [Python](https://www.python.org/)
.
Brief introduction to YAML[¶](https://docassemble.org/docs/interviews.html#yaml)
=================================================================================
**docassemble** interviews are written in [YAML](https://en.wikipedia.org/wiki/YAML)
format, rather than assembled using a [graphical user interface](https://en.wikipedia.org/wiki/Graphical_user_interface)
, because once developers have climbed the **docassemble** learning curve, the text format is ideal for managing the complexity of advanced interviews, since it allows developers to copy-and-paste, search-and-replace, and organize text into multiple files. [YAML](https://en.wikipedia.org/wiki/YAML)
was chosen as the format because it is the cleanest-looking of data formats that are both machine-readable and human-readable.
The hardest part about learning **docassemble** is not writing [Python](https://www.python.org/)
code, since sophisticated interviews can be built using nothing more complicated than a few [if/else statements](https://docassemble.org/docs/code.html#if)
. The more difficult aspect may be learning [YAML](https://en.wikipedia.org/wiki/YAML)
. While the [YAML](https://en.wikipedia.org/wiki/YAML)
format looks simple, it can be frustrating.
To understand [YAML](https://en.wikipedia.org/wiki/YAML)
, you first need to understand the difference between a “list” and a “dictionary.”
A “list” is an ordered collection of things. If my to-do list for a Saturday afternoon was first to take out the garbage, and then to sweep the porch, this could be represented in [YAML](https://en.wikipedia.org/wiki/YAML)
as:
- Sweep the porch
- Take out the garbage
A “dictionary,” by contrast, associates things with other things. For example, if I have some legal terms that I want to associate with an explanation, I could put this in a [YAML](https://en.wikipedia.org/wiki/YAML)
dictionary:
lawyer: A person who represents you.
judge: A person who decides who wins or loses a court case.
While a list has an order to it (e.g., I need to first sweep the porch and then take out the garbage), the dictionary is just a jumble of words and definitions. More generally, it associates “keys” with “values.”
[YAML](https://en.wikipedia.org/wiki/YAML)
interprets lines of text and figures out whether you are talking about a list or a dictionary depending on what punctuation you use. If it sees a hyphen, it thinks you are talking about a list. If it sees a colon, it thinks you are talking about a dictionary.
Lists and dictionaries can be combined. You can have a dictionary of lists and a list of dictionaries. If I wanted to express the to-do lists of multiple people, I could write:
Frank:
- Sweep the porch
- Take out the garbage
- Clean the toilets
Sally:
- Rake the leaves
- Mow the lawn
Here, you have a dictionary with two keys: “Frank” and “Sally.” The value of the “Frank” key is a list with three items, and the value of the “Sally” key is a list with two items.
If you are familiar with [Python](https://www.python.org/)
’s data notation, this translates into:
{"Frank": ["Sweep the porch", "Take out the garbage", "Clean the toilets"], "Sally": ["Rake the leaves", "Mow the lawn"]}
The [JSON](https://en.wikipedia.org/wiki/JSON)
representation is the same.
You can also have a list of dictionaries:
- title: Tale of Two Cities
author: Charles Dickens
- title: Moby Dick
author: Herman Melville
- title: Green Eggs and Ham
author: Dr. Seuss
In [Python](https://www.python.org/)
’s data notation, this translates into:
[{'title': 'Tale of Two Cities', 'author': 'Charles Dickens'}, {'title': 'Moby Dick', 'author': 'Herman Melville'}, {'title': 'Green Eggs and Ham', 'author': 'Dr. Seuss'}]
[YAML](https://en.wikipedia.org/wiki/YAML)
also allows you to divide up data into separate “documents” using the `---` separator. Here is an example of using three documents to describe three different books:
title: Tale of Two Cities
author: Charles Dickens
---
title: Moby Dick
author: Herman Melville
---
title: Green Eggs and Ham
author: Dr. Seuss
[YAML](https://en.wikipedia.org/wiki/YAML)
’s simplicity results from its use of simple punctuation marks. However, be careful about data that might confuse the computer. For example, how should the computer read this shopping list?
- apples
- bread
- olive oil, the good stuff
- shortening: for cookies
- flour
In [Python](https://www.python.org/)
, this will be interpreted as:
['apples', 'bread', 'olive oil, the good stuff', {'shortening': 'for cookies'}, 'flour']
This is a list of apples, bread, olive oil, a dictionary, and flour. That’s not what you wanted!
You wanted `shortening: for cookies` to be a piece of text. But the computer assumed you wanted to indicate a dictionary. [YAML](https://en.wikipedia.org/wiki/YAML)
’s clean appearance makes it readable, but this kind of problem is the downside to [YAML](https://en.wikipedia.org/wiki/YAML)
.
You can get around this problem by putting quote marks around text:
- apples
- bread
- olive oil
- "shortening: for cookies"
- flour
This will result in all of the list elements being interpreted as plain text. In [Python](https://www.python.org/)
:
['apples', 'bread', 'olive oil', 'shortening: for cookies', 'flour']
[YAML](https://en.wikipedia.org/wiki/YAML)
also allows text to be block quoted:
title: |
Raspberry Jam: a "Fancy" Way to Eat Fruit
author: |
Jeanne Trevaskis
The pipe character `|` followed by a line break indicates the start of the quote. The indentation is important because it indicates where the block quote ends. As long as you are indenting each line of text, you can write anything you want in the text (e.g., colons, quotation marks) without worrying that the computer will misinterpret what you are writing.
The following values in [YAML](https://en.wikipedia.org/wiki/YAML)
are special:
* `null`, `Null`, `NULL` – these become `None` in [Python](https://www.python.org/)
* `true`, `True`, `TRUE` – these become `True` in [Python](https://www.python.org/)
* `false`, `False`, `FALSE` – these become `False` in [Python](https://www.python.org/)
* numbers such as `54`, `3.14` – these become numbers in [Python](https://www.python.org/)
These values will not be interpreted as literal pieces of text, but as values with special meaning in [Python](https://www.python.org/)
. This can cause confusion in your interviews, so if you ever use “True” and “False” as a label or value, make sure to enclose it in quotation marks.
This [YAML](https://en.wikipedia.org/wiki/YAML)
text:
loopy: 'TRUE'
smart: false
pretty: TRUE
energetic: "false"
becomes the following in [Python](https://www.python.org/)
:
{'loopy': 'TRUE', 'smart': False, 'pretty': True, 'energetic': 'false'}
One feature of [YAML](https://en.wikipedia.org/wiki/YAML)
that is rarely used, but that you may see, is the use of “explicit mapping.” Instead of writing:
apple: red
orange: orange
banana: yellow
You can write:
? apple
: red
? orange
: orange
? banana
: yellow
Both mean the same thing. You might want to use this technique if your labels in a [`fields`](https://docassemble.org/docs/fields.html#fields)
specifier are long. For example, instead of writing:
question: |
Please answer these questions.
fields:
"Where were you born?": place_of_birth
"What were the last words of the first President to fly in a Zeppelin?": words
you could write:
question: |
Please answer these questions.
fields:
? Where were you born?
: place_of_birth
? |
What were the last words of the
first President to fly in a Zeppelin?
: words
Note that many punctuation marks, including `"`, `'`, `%`, `?`, `~`, `|`, `#`, `>`, `:`, `!`, `{`, `}`, `[`, and `]`, have special meaning in [YAML](https://en.wikipedia.org/wiki/YAML)
, so if you use them in your text, make sure to use quotation marks or block quotes.
For more information about [YAML](https://en.wikipedia.org/wiki/YAML)
, see the [YAML specification](https://yaml.org/spec/1.2.2/)
.
How to develop your own interviews[¶](https://docassemble.org/docs/interviews.html#htdevelop)
==============================================================================================
To write and test your own interviews, you will need:
1. A **docassemble** server (see [Docker](https://docassemble.org/docs/docker.html)
);
2. An account on the [username and password system](https://docassemble.org/docs/users.html)
of that server, where the privileges of the account are “developer” or “admin.”
There are several to develop your own interviews:
1. When logged in, go to the “Playground” from the menu in the upper right hand corner. The [playground](https://docassemble.org/docs/playground.html)
allows you to quickly edit and run interview [YAML](https://en.wikipedia.org/wiki/YAML)
.
2. Create a [package](https://docassemble.org/docs/packages.html)
on your local computer and then install it on the **docassemble** server either through [GitHub](https://github.com/)
or by uploading a ZIP file.
3. Create a [package](https://docassemble.org/docs/packages.html)
, push it to [GitHub](https://github.com/)
, and then edit your interviews using [GitHub](https://github.com/)
’s web interface. (You can also upload static files using [GitHub](https://github.com/)
.) To run your interview, update your [package](https://docassemble.org/docs/packages.html)
on **docassemble** (which will retrieve your code from [GitHub](https://github.com/)
).
For more information about the development workflow, see the [development overview](https://docassemble.org/docs/development.html)
section.
How you run a **docassemble** interview[¶](https://docassemble.org/docs/interviews.html#invocation)
====================================================================================================
Users start an interview by going to its URL, which is the `/interview` path on your site with the `i` URL parameter set to the name of the interview.
For example, the [demo interview](https://docassemble.org/demo.html)
, which is hosted on the site `demo.docassemble.org`, can be accessed with this URL.
[https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/questions.yml](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/questions.yml)
Here, the interview file name is `docassemble.demo:data/questions/questions.yml`. This tells **docassemble** to look for a Python package named `docassemble.demo` and then within that package, look for the file `questions.yml` located in the subdirectory `data/questions`.
You can make your own [packages](https://docassemble.org/docs/packages.html)
and then install them on your site. If the name of your site is `interview.example.com`, the name of your package is `docassemble.mypackage`, and the name of your interview file is `myinterview.yml`, your users can access the interview at:
> https://interview.example.com/interview?i=docassemble.mypackage:data/questions/myinterview.yml
If your interview is in the [Playground](https://docassemble.org/docs/playground.html)
, the name of the interview will be something like `docassemble.playground1:myinterview.yml` where `1` is your user ID. Or, if your interview is in a “project” inside your Playground, and the project is called `testing`, the name of the interview will be something like `docassemble.playground1testing:myinterview.yml`. You can see the name of the interview in the URL while you are testing the interview, or in the URL available using the “Share” button in the Playground. The interview file name is the `i` parameter in the URL.
Note that while you are using an interview, the URL in the location bar will change. It will end with `#page1`, then `#page2`, then `#page3`, etc., as the interview progresses. These tags have no direct effect and the page number has no particular meaning; these tags exist because of their side effect, which is to enable the user to click the browser’s back button in order to go back one screen.
The remainder of this subsection will discuss ways that you can customize the way that interviews are invoked. This is a fairly advanced topic, so if you are new, feel free to skip to the section on [how answers are stored](https://docassemble.org/docs/interviews.html#howstored)
.
URLs to `/interview` with an `i` parameter are not meant to be understood by users; they are primarily meant to make clear to the developer where the interview file is located in the Python package. If you want a more readable URL, you can use:
> https://interview.example.com/start/mypackage/myinterview/
which launches the same interview as
> https://interview.example.com/interview?i=docassemble.mypackage:data/questions/myinterview.yml
Once the user visits a URL like `/start/mypackage/myinterview/`, the URL in the location bar will change to `/run/mypackage/myinterview/`. This is because `/start` will cause a new session to be created, whereas `/run` will continue an existing session that is operating in the user’s browser.
If you want to use **docassemble** to give users a list of interviews from which to choose, there is a special page of the site, located at `/list`, which displays a [list of interviews](https://docassemble.org/docs/config.html#dispatch)
available on your site.
> https://interview.example.com/list
This list is not automatically-generated. You need to configure the list using the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
configuration directive. The list of interviews can also be [embedded](https://docassemble.org/docs/config.html#dispatch)
into a page of another web site. This page is highly [configurable](https://docassemble.org/docs/config.html#customization)
. You can also replace the default `/list` page with an interview using the [`dispatch interview`](https://docassemble.org/docs/config.html#dispatch%20interview)
configuration directive. Within that interview, you can use the [`interview_menu()`](https://docassemble.org/docs/functions.html#interview_menu)
function within that interview to present the list of interviews in whatever way you want.
The [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
configuration directive also allows your users to start new sessions in specific interviews at human-readable URLs like:
> https://interview.example.com/start/eviction/
> https://interview.example.com/start/namechange/
Once the user visits a URL like `/start/eviction/`, the URL in the location bar will change to `/run/eviction/`.
If the user visits the main (or “root”) URL for the site, e.g., `https://interview.example.com`, the user will be redirected to the URL indicated by the [`root redirect url`](https://docassemble.org/docs/config.html#root%20redirect%20url)
configuration directive. A typical way to use this feature is to direct users to a web site outside of **docassemble** where they can find out information about the services you offer.
If you don’t have a [`root redirect url`](https://docassemble.org/docs/config.html#root%20redirect%20url)
set, the user will be redirected to `/interview` and will start the interview indicated by the [`default interview`](https://docassemble.org/docs/config.html#default%20interview)
configuration directive.
This can be useful when you have one primary interview on your site and you want users to be able to start it by visiting an easy-to-type URL such as:
> https://interview.example.com
If you have set [`root redirect url`](https://docassemble.org/docs/config.html#root%20redirect%20url)
, your [`default interview`](https://docassemble.org/docs/config.html#default%20interview)
interview will still be accessible at:
> https://interview.example.com/interview
If you do not have a [`default interview`](https://docassemble.org/docs/config.html#default%20interview)
, but you have configured a `/list` page using the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
configuration directive, then the user who visits the “root” URL of your site will be redirected to `/list`.
However, if the user had previously been using another interview during the same browser session, going to `https://interview.example.com/` (without a [`root redirect url`](https://docassemble.org/docs/config.html#root%20redirect%20url)
) will resume the original session.
If you want your users who are in the middle of an interview to be able to begin a different interview, you can enable [`show dispatch link`](https://docassemble.org/docs/config.html#show%20dispatch%20link)
in the [configuration](https://docassemble.org/docs/config.html)
, and then in the menu, the user will see a link called “Available Interviews,” which directs to your `/list` page. You can also use the [`menu_items` special variable](https://docassemble.org/docs/special.html#menu_items)
within an interview to provide options on the pull-down menu for starting other interviews. Within the body of an interview question, you can insert a link to another interview using the [`interview_url()`](https://docassemble.org/docs/functions.html#interview_url)
function with an `i` parameter indicating the interview.
Embedding the interview into a web page with an iframe[¶](https://docassemble.org/docs/interviews.html#iframe)
---------------------------------------------------------------------------------------------------------------
You can embed an interview into a web page by inserting an [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
into the [HTML](https://en.wikipedia.org/wiki/HTML)
of the page.
You should adjust the width and height of the [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
based on what makes sense for the web page. **docassemble** can handle a variety of sizes, but make sure you test the user experience both on desktop and on mobile. Since embedded interviews are often less than ideal for mobile users, you can use the [`go full screen`](https://docassemble.org/docs/initial.html#go%20full%20screen)
feature to cause the interview to “go full screen” on the user’s device once the user starts interacting with it.
There is a [Drupal module](https://github.com/jhpyle/docassemble_embed)
and a [WordPress plugin](https://github.com/jhpyle/docassemble-embedder)
for embedding interviews using an [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
.
When embedding using an [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
, set the [`allow embedding`](https://docassemble.org/docs/config.html#allow%20embedding)
directive in the [Configuration](https://docassemble.org/docs/config.html)
to `True`. When you do this, **docassemble** will refrain from setting the `X-Frame-Options` header to `SAMEORIGIN` (which would otherwise tell the browser not to show the [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
). In addition, this will cause a flag on the cookies to be set so that cross-site cookie sharing is allowed.
Web browsers are generally permissive about allowing [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
s even when the host domain and [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
domain are different. However, some browsers, such as Safari, might block the content because of [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
concerns.
It may also help to set [`cross site domains`](https://docassemble.org/docs/config.html#cross%20site%20domain)
in your [Configuration](https://docassemble.org/docs/config.html)
to include the protocol and domain of the host site. For example:
cross site domains:
- https://example.com
When this is set, your server will respond to requests with special headers that indicate to the browser that your server consents to allowing `https://example.com` to embed its content.
Even after you set [`cross site domains`](https://docassemble.org/docs/config.html#cross%20site%20domain)
, some browsers may still block the [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
content if the top-level domain of the host server is different from the top-level domain of the **docassemble** server. Thus, if your host web site is `example.com`, you could edit your [DNS](https://en.wikipedia.org/wiki/Domain_Name_System)
so that your **docassemble** server runs on a subdomain of `example.com`, such as `interviews.example.com`.
Another way that a browser can be persuaded to allow an [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
is if the browser’s history includes a visit to the **docassemble** site. This indicates to the browser that the user has consented to receive content from the **docassemble** server. For example, when a host site on `example.com` tries to show the user an [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
with content from `legalinterviewbot.com`, the browser will consult the user’s browser history, see that the user once visited a page on `legalinterviewbot.com`, so it will conclude that it is safe to show the content to the user.
One way to establish this browser history is to have a link on your host site that points to an HTML file on your **docassemble** site, which then redirects the user back to your host site. This HTML file can be hosted from the “static” folder of one of your packages. For example, if your package name is `docassemble.missouri`, and the file name is `start.html`, you can point the users to the path `/packagestatic/docassemble.missouri/start.html` on your **docassemble** server. The content of the HTML could be something like:
Redirecting...
Starting your interview on example.com...
Another way to redirect the user is to run some [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
like:
window.location = "https://example.com/custody.interview.html";
Different browsers protect user privacy in different ways. Some users may have installed plug-ins that interfere with [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
embedding. The way that browsers behave is usually not well-documented, and it can change at any time. Thus, the most stable solution to any future cross-site scripting problems you might encounter is to make the host site act as a proxy for the **docassemble** server, following the model discussed in the section on [installing on a machine already using a web server](https://docassemble.org/docs/docker.html#forwarding)
. Unfortunately, this is a fairly complex set-up, and it may not be possible to configure this set-up on all sites.
Embedding the interview into a web page directly[¶](https://docassemble.org/docs/interviews.html#div)
------------------------------------------------------------------------------------------------------
A **docassemble** interview can be embedded into a `
` element in a page of another web site, provided that the web page loads the resources that **docassemble** needs. This takes more care and setup than [using an iframe](https://docassemble.org/docs/interviews.html#iframe)
because you need to reconcile the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
and [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
that **docassemble** needs with the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
and [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
already contained within the host web site.
The [Drupal module](https://github.com/jhpyle/docassemble_embed)
and [WordPress plugin](https://github.com/jhpyle/docassemble-embedder)
both support embedding interviews into `
` elements.
**docassemble** depends on the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
classes of [Bootstrap 5](https://getbootstrap.com/)
being defined. Your site’s [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
should be loaded after the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
needed by **docassemble**, so that it overrides the **docassemble** [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
rules. (The [Drupal module](https://github.com/jhpyle/docassemble_embed)
and [WordPress plugin](https://github.com/jhpyle/docassemble-embedder)
are configured to do this.) The [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
of your site will thus affect the styling of the **docassemble** interview. This is generally a good thing because you will probably want the interview to have the same look-and-feel as your site. However, some of the changes that your site’s [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
will make to the look-and-feel of your interview will not be desirable. For example, you might see excessive padding between form fields. You will likely need to make edits to your site’s [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
to adjust these parameters.
Another complication is that **docassemble** requires [jQuery](https://jquery.com/)
. There might be problems due to incompatible versions of [jQuery](https://jquery.com/)
. **docassemble** also requires a number of other [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
libraries. These libraries will probably not have an effect on your site, but it is possible that they will.
To get started with embedding **docassemble** into a web page (if you aren’t using the [Drupal module](https://github.com/jhpyle/docassemble_embed)
or the [WordPress plugin](https://github.com/jhpyle/docassemble-embedder)
), log in to your **docassemble** server as an administrator or developer, then navigate to `/test_embed`. You will see your default interview there, inside of a box. Do “View Source” in your browser to see how it works. Here is an example of what the source will look like on a server with the URL `https://interview.example.com`:
Embed test
Here is some content before the interview.
Here is some content after the interview.
The idea behind `/test_embed` is that you can selectively copy and paste its source code into another web site in order to embed a **docassemble** interview into that web site.
Notice that the last `
If you want to load all of the [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
dependencies except for [jQuery](https://jquery.com/)
, use the following:
Another complication of embedding is avoidance of problems with [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
. Problems with [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
were discussed in the previous section on [using an iframe](https://docassemble.org/docs/interviews.html#iframe)
, and these problems are even more likely to arise when embedding in a `
`.
To get around these problems, set [`cross site domains`](https://docassemble.org/docs/config.html#cross%20site%20domain)
in your [Configuration](https://docassemble.org/docs/config.html)
to the URL of your site:
allow embedding: True
cross site domains:
- https://example.com
Also set the [`allow embedding`](https://docassemble.org/docs/config.html#allow%20embedding)
directive in the [Configuration](https://docassemble.org/docs/config.html)
to `True`, so that cross-site cookie sharing is allowed.
allow embedding: True
Even after you set [`cross site domains`](https://docassemble.org/docs/config.html#cross%20site%20domain)
and [`allow embedding`](https://docassemble.org/docs/config.html#allow%20embedding)
, some browsers, such as Firefox, may still block the content if the top-level domain of the host server is different from the top-level domain of the **docassemble** server. Thus, if your host web site is example.com, you should run your **docassemble** server on a subdomain of example.com, such as interviews.example.com.
If you still encounter problems on some browsers, consider setting up your host server to act as a proxy, following the model discussed in the subsection on [installing on a machine already using a web server](https://docassemble.org/docs/docker.html#forwarding)
.
Starting an interview from the beginning[¶](https://docassemble.org/docs/interviews.html#reset)
------------------------------------------------------------------------------------------------
The **docassemble** web application uses browser cookies to keep track of the user’s current interview session. If the user starts an interview, then navigates to a different page, and then navigates to `/interview` on the **docassemble** site with an `i` parameter that is the same as the `i` parameter of the interview they were using before, the user will resume where they had left off in their existing session.
If you want to be able to provide your users with a URL that always starts a fresh interview session, and will not resume an existing session, include `&new_session=1` in the URL. Whenever this link is clicked (or the [iframe](https://www.w3schools.com/TAgs/tag_iframe.asp)
is drawn), the interview will start at the beginning, even if the user had just been in a session of the same interview. The prior session, if any, is preserved.
If you add `&reset=1` to the end of an `/interview` URL, this will have the same effect as `&new_session=1`, but if the user had just been in a session with the same interview, that session will be deleted. In this cirumstance, adding `&reset=1` is like a “restart” operation.
For other session restarting options, see the `'restart'` and `'new_session'` options for the [`url_of()`](https://docassemble.org/docs/functions.html#url_of)
and [`command()`](https://docassemble.org/docs/functions.html#command)
functions, and the `restart` and `new_session` [special buttons](https://docassemble.org/docs/questions.html#special%20buttons)
.
If the user has been in an interview session and then navigates to a different interview, the user will see an informational message at the top of the screen such as:
* Starting a new interview. To go back to your previous interview, go to My Interviews on the menu.
* Starting a new interview. To go back to your previous interview, log in to see a list of your interviews.
* Entering a different interview. To go back to your previous interview, go to My Interviews on the menu.
* Entering a different interview. To go back to your previous interview, log in to see a list of your interviews.
This message is not shown when the user starts an interview session by clicking a link on the My Interviews page. The informational message is suppressed by the inclusion of `&from_list=1` in the URL parameters of the link.
Resuming an interview with a POST request[¶](https://docassemble.org/docs/interviews.html#resume)
--------------------------------------------------------------------------------------------------
If you do not want your users’ browsers to pass a session ID as a URL parameter (`session`) to a [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET)
request to `/interview`, you can cause the user’s browser to send a [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)
request to `/resume` with the following parameters:
* `i` containing the name of the interview (e.g., `docassemble.mypackage:data/questions/myinterview.yml`)
* `session` containing the session ID of the session.
The server will respond with a 302 redirect to `/interview`. All parameters that you pass to [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)
, except for `session`, will become URL parameters of the [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET)
request that the browser will send after the redirect. Thus you can set [`url_args`](https://docassemble.org/docs/special.html#url_args)
using the parameters of the [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)
request to `/resume`.
How answers are stored[¶](https://docassemble.org/docs/interviews.html#howstored)
==================================================================================
When a user starts a new interview session, a new “variable store” or set of “interview answers” is created. The interview answers are stored in a [Python dictionary](https://docs.python.org/3/tutorial/datastructures.html#dictionaries)
containing the names of the variables that get defined during the course of the interview, such as `favorite_animal` in the example interview above. This dictionary is saved in **docassemble**’s database.
Because this [Python dictionary](https://docs.python.org/3/tutorial/datastructures.html#dictionaries)
can contain data structures and complicated [Python objects](https://docs.python.org/3/tutorial/classes.html)
, before it can be saved in a database, it needs to “frozen” into a form that can be stored in a database and then “thawed” later. The method that **docassemble** uses to “freeze” and “thaw” the interview answers is [Python](https://www.python.org/)
’s [pickle](https://docs.python.org/3.12/library/pickle.html)
method.
**docassemble** keeps a snapshot of the interview answers for every step of the session. If the user presses the back button, **docassemble** will restore the variable store to the next earliest version. This has the effect of an “undo,” and there is no “redo” button.
Since the back button performs a permanent “undo,” you should not encourage your end users to click the “Back” button as a means of changing past answers. Instead, you should provide [review screens](https://docassemble.org/docs/fields.html#review)
where users can quickly find the answer they want to change, and change it without losing their work.
Since the interview answers are stored in a [Python](https://www.python.org/)
data structure that can contain [Python objects](https://docs.python.org/3/tutorial/classes.html)
, you have a great deal of flexibility in how you can structure the information you collect. This also means that the interview answers cannot easily be reduced to a spreadsheet the way that the results of a [Google Form](https://www.google.com/forms/about/)
can be. If you want to be able to store a session’s interview answers in a spreadsheet form, you can write [Python](https://www.python.org/)
code to do so. There are also some helpful [Objects](https://docassemble.org/docs/objects.html)
and [Functions](https://docassemble.org/docs/functions.html)
that you can use, such as the [`DAStore`](https://docassemble.org/docs/objects.html#DAStore)
object, the [`SQLObject`](https://docassemble.org/docs/objects.html#SQLObject)
, and the [`write_record()`](https://docassemble.org/docs/functions.html#write_record)
function.
Leaving an interview and coming back[¶](https://docassemble.org/docs/interviews.html#comingback)
=================================================================================================
If the user is not logged in through **docassemble**’s [username and password system](https://docassemble.org/docs/users.html)
, then the user’s progress through an interview will be lost if the web browser is closed.
If the user is logged in, however, then when the user logs in again, the user will be able to resume the interview where he left off.
If a new user starts an interview without being logged in, and then clicks the “Sign in or sign up to save answers” link to log in, and then clicks the link to register, the user will immediately be directed back to the interview they had been using, and they will immediately pick up where they left off, the only difference being that they are now logged in.
If a logged-in user leaves an interview without completing it, closes their browser, then opens their browser at a later time, and visits the interview link again, they will start a new session for the interview indicated by the `i` parameter. If they then log in using the menu in the corner, they will be directed to the `/interviews` page, where they will see two interview sessions listed, including their original session and the session they just started.
If your users will only ever need to use a single session of an interview, you might want to change the code of your interview so that they have a different experience. For example, you might want to start your interview with a multiple-choice question that asks the user if they are a new user or a returning user. If they are a returning user.
question: |
Are you here for the first time, or returning?
field: user_new_or_returning
buttons:
- First time: new
- Returning: returning
---
mandatory: True
code: |
if user_new_or_returning == 'returning':
command('exit', url=url_of('login'))
Running [`command()`](https://docassemble.org/docs/functions.html#command)
with `'exit'` deletes the current interview session. The `url` keyword parameter redirects the user to a particular page. The function [`url_of()`](https://docassemble.org/docs/functions.html#url_of)
with the parameter `'login'` returns the URL for the **docassemble** login page.
For other exiting options, see the `'exit'`, `'leave'`, `'logout'`, and `'exit_logout'` options for the [`url_of()`](https://docassemble.org/docs/functions.html#url_of)
and [`command()`](https://docassemble.org/docs/functions.html#command)
functions.
Using Jinja2 templating to build the YAML of an interview[¶](https://docassemble.org/docs/interviews.html#jinja2)
==================================================================================================================
When specifying the [YAML](https://en.wikipedia.org/wiki/YAML)
of an interview, you have the option of using [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
templating to construct your [YAML](https://en.wikipedia.org/wiki/YAML)
. The vast majority of **docassemble** developers will not need to do this, so this should be considered an advanced feature.
By default, interview YAML files are not processed with [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
. To turn on [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
processing, add `# use jinja` to the very first line of a [YAML](https://en.wikipedia.org/wiki/YAML)
file. (This needs to be written exactly this way on the first line.)
# use jinja
{% include "jinjayaml-included.yml" %}
mandatory: True
question: |
Your favorite fruit is ${ favorite_fruit }.
subquestion: |
* `__version__`: {{ __version__ }}
* `__architecture__`: {{ __architecture__ }}
* `__filename__`: {{ __filename__ }}
* `__current_package__`: {{ __current_package__ }}
* `__interview_filename__`: {{ __interview_filename__ }}
* `__interview_package__`: {{ __interview_package__ }}
* `__parent_filename__`: {{ __parent_filename__ }}
* `__parent_package__`: {{ __parent_package__ }}
* `__debug__`: {{ __debug__ }}
* `__hostname__`: {{ __hostname__ }}
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/jinjayaml.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/jinjayaml.yml&reset=1 "Click to try this interview")
This example demonstrates the use of the `include` command. The `jinjayaml-included.yml` file is located in the same directory as the YAML file. Its contents are:
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
---
In this context, the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
`include` command acts much like the **docassemble** [`include` block](https://docassemble.org/docs/initial.html#include)
. However, the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
command can do things that the [`include` block](https://docassemble.org/docs/initial.html#include)
cannot do; for example, you can set the contents of a [YAML](https://en.wikipedia.org/wiki/YAML)
file to be a partial block, such as a list of `fields`, and then include that file in the middle of one or more `question` blocks.
Note that all that [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
does is take [YAML](https://en.wikipedia.org/wiki/YAML)
and convert it to [YAML](https://en.wikipedia.org/wiki/YAML)
. The way that [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
is used in this context is very different from the way that [Mako](https://www.makotemplates.org/)
is used inside [Markdown](https://daringfireball.net/projects/markdown/)
text or the way that [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
is used to assemble [DOCX documents](https://docassemble.org/docs/documents.html#docx%20template%20file)
. For example, you cannot run **docassemble** [functions](https://docassemble.org/docs/functions.html)
or refer to variables in the interview answers. The [YAML](https://en.wikipedia.org/wiki/YAML)
is processed by [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
when the interview is first loaded into the memory of the web server process. [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
functions as a preprocessor only; it cannot be used to make dynamic changes to the way an interview works.
In the above example, the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
`include` directive was used to include a file in the “questions” folder of the same package. If you want to refer to a file in another package, you can write a complete filename such as `docassemble.missouri:data/questions/toinclude.yml`.
The following variables are available in the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
context:
* `__config__`: the [Configuration](https://docassemble.org/docs/config.html)
of the server, as a Python `dict`.
* `__version__`: the current version of **docassemble** installed on the server.
* `__architecture__`: the value returned by `platform.machine()` (e.g., `'x86_64'`).
* `__filename__`: the name of the [YAML](https://en.wikipedia.org/wiki/YAML)
file for which [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
was invoked.
* `__current_package__`: the package containing the [YAML](https://en.wikipedia.org/wiki/YAML)
file for which [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
was invoked.
* `__interview_filename__`: the name of the [YAML](https://en.wikipedia.org/wiki/YAML)
file that was invoked to run the current interview.
* `__interview_package__`: the package containing the [YAML](https://en.wikipedia.org/wiki/YAML)
file that was invoked to run the current interview.
* `__parent_filename__`: if the current [YAML](https://en.wikipedia.org/wiki/YAML)
file (`__filename__`) was included through an [`include` block](https://docassemble.org/docs/initial.html#include)
, `__parent_filename__` will contain the name of the [YAML](https://en.wikipedia.org/wiki/YAML)
file that contained the [`include` block](https://docassemble.org/docs/initial.html#include)
. Otherwise, `__parent_filename__` will be the same as `__interview_filename__`.
* `__parent_package__`: the package containing `__parent_filename`.
* `__debug__`: the value of the [`debug`](https://docassemble.org/docs/config.html#debug)
Configuration directive (the default is `True`.
* `__hostname__`: the value of the [`external hostname`](https://docassemble.org/docs/config.html#external%20hostname)
Configuration directive (the default is `localhost`).
In addition, you can make variables available in the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
context on your server by setting the [`jinja data`](https://docassemble.org/docs/config.html#jinja%20data)
directive in your [Configuration](https://docassemble.org/docs/config.html)
. For example:
jinja data:
verbosity: 2
region: Delaware
This will make the variables `verbosity` and `region` available for use in [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
directives.
Note that the variables passed to [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
are fixed at the time that [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
is invoked. For example, if you use the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
`include` directive, the `__filename__` will not be the filename of the included file; it will continue to be whatever it was when the [YAML](https://en.wikipedia.org/wiki/YAML)
filename containing `# use jinja` was encountered.
* * *
---
# Code questions
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/code.md "Edit this page on GitHub")
Code questions
==============
Contents
--------
* [1 An introduction to coding in Python](https://docassemble.org/docs/code.html#python)
* [1.1 Simple examples: arithmetic](https://docassemble.org/docs/code.html#simpleexamples)
* [1.2 Conditional actions: if/then/else statements](https://docassemble.org/docs/code.html#if)
* [1.3 Going through the items in a group](https://docassemble.org/docs/code.html#looping)
* [2 The code block](https://docassemble.org/docs/code.html#code)
* [3 code block modifiers](https://docassemble.org/docs/code.html#modifiers)
* [4 Limitations](https://docassemble.org/docs/code.html#limitations)
**docassemble** allows interview developers to use [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, a general purpose programming language, to control the direction of interviews and do things with user input. It is not necessary to use [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code when developing an interview, but it is an extremely powerful tool.
[Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
appears in **docassemble** interviews in a number of ways:
* Every [variable name](https://docassemble.org/docs/fields.html#variable%20names)
in an interview is a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
variable, whether developers realize it or not. The value of the variable might be text (e.g., `"123 Main Street"`), a number (e.g., `42`), a special value that has meaning in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
(e.g. `True`, `False`, and `None`), a [group](https://docassemble.org/docs/groups.html)
(e.g., a [list](https://docassemble.org/docs/groups.html#list)
, [dictionary](https://docassemble.org/docs/groups.html#dictionary)
, or [set](https://docassemble.org/docs/groups.html#set)
), an [object](https://docassemble.org/docs/objects.html)
, or an attribute of an [object](https://docassemble.org/docs/objects.html)
.
* Developers can use [`code`](https://docassemble.org/docs/code.html#code)
blocks to set variables using [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code, which may act upon user input.
* [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code can be embedded within [`question`](https://docassemble.org/docs/questions.html#question)
s, for example to generate a list of choices in a multiple-choice list.
* The [Mako](https://www.makotemplates.org/)
templating system, which developers can use to format [questions](https://docassemble.org/docs/questions.html)
and [documents](https://docassemble.org/docs/documents.html)
, is based on [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, and allows developers to embed [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
statements within templates. There are slight syntax differences between [Mako](https://www.makotemplates.org/)
and [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
. For example, [Mako](https://www.makotemplates.org/)
requires that if/then/else logic statements be closed with an `endif` statement.
An introduction to coding in Python[¶](https://docassemble.org/docs/code.html#python)
======================================================================================
As general purpose programming languages go, [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
is relatively user-friendly and readable. Python programmers don’t need to worry that their code will fail because of a missing semicolon.
Simple examples: arithmetic[¶](https://docassemble.org/docs/code.html#simpleexamples)
--------------------------------------------------------------------------------------
Here is some very simple [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code:
code: |
answer = 2 + 2
---
question: |
The answer is ${ answer }.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-01.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-01.yml&reset=1 "Click to try this interview")
This code sets the variable `answer` to 2 + 2. The code is contained in a [`code`](https://docassemble.org/docs/code.html#code)
block, which is explained [below](https://docassemble.org/docs/code.html#code)
.
Here is a more complicated example:
code: |
a = 2
b = 3
answer = a + b
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-02.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-02.yml&reset=1 "Click to try this interview")
This code first sets the variable `a` to the number 2. Then it sets the variable `b` to the number 3. Then it sets the variable `answer` to the sum of `a` and `b`, which is 5.
Note that once a variable is set, its value does not change. In the code below, the `answer` is still 5, even though `b` is changed to `1`.
code: |
a = 2
b = 3
answer = a + b
b = 1
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-03.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-03.yml&reset=1 "Click to try this interview")
The `code` blocks can contain multiple lines of code, which are processed one at a time.
It is also possible to run Python code in a more limited way within a [Mako](https://www.makotemplates.org/)
template, using [Mako](https://www.makotemplates.org/)
’s `${}` syntax.
question: |
The answer is ${ 2 + 2 }.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-04.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-04.yml&reset=1 "Click to try this interview")
The contents of `${ ... }` are processed as [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code. The code that can be placed inside `${ ... }` is limited to one line of code, the result of which is then placed into the text of the question. So you could not include multiple lines of code within a `${ ... }` expression.
You can do complicated arithmetic with [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
:
question: |
The answer is ${ ( 42 + 4 ) * 50 / ( 5 - 2 ) }.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-05.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-05.yml&reset=1 "Click to try this interview")
Note that the spaces within this code are purely aesthetic; the code will still function without them:
question: |
The answer is ${(42+4)*50/(5-2)}.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-06.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-06.yml&reset=1 "Click to try this interview")
However, using spaces in your code is highly recommended, because they make the code much more readable!
Conditional actions: if/then/else statements[¶](https://docassemble.org/docs/code.html#if)
-------------------------------------------------------------------------------------------
Sometimes you want different code to run differently depending on certain conditions. In computer programming, the simplest form of a “conditional statement” is the if/then/else statement, where you tell the computer that _if_ a certain condition is true, _then_ do something, or do something _else_ if the condition is false.
For example:
code: |
a = 4
b = 5
if b > a:
b = 62
answer = 20 + b
else:
answer = 40 + b
b = 0
---
question: |
The answer is ${ answer }.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-07.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-07.yml&reset=1 "Click to try this interview")
Here, the condition to be evaluated is `b > a`. The `>` symbol means “greater than.” (The `<` symbol means “less than.”) Since `b` is 5 and `a` is 4, and 5 is greater than 4, the condition is true. Therefore, the lines `b = 62` and `answer = 20` will be run, and the code `answer = 40` will be ignored.
There are several important things to note in this example because they illustrate the syntax of the [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
language:
* The `if` and `else` statements end in a colon `:`, after which the line ends.
* Rule: This colon must be there. If you forget the colon, you will get an “invalid syntax” error.
* The lines after the `if` and `else` lines are indented. The indentation indicates which lines are referred to by the colon, and which are not.
* Rule: There must be at least one indented line following the colon. If you don’t have an indented line following a colon, you will see the error “IndentationError: expected an indented block.”
* At the end, `b` will be set to 0. Although the line follows `else:`, it is not indented relative to the `else:` line.
* The lines `b = 62` and `answer = 20` are both indented by two spaces.
* Rule: While the number of spaces is not important (1, 2, 3, 4, or more spaces would all be valid) the indentation following the colon must be consistent. If you use inconsistent indentation, you will see the error “IndentationError: unindent does not match any outer indentation level.”
These are important rules in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
. In other programming languages, line breaks and spaces do not matter, and punctuation marks like `{`, `}`, and `;` are used to separate different pieces of code. In [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, however, line breaks and spaces are important; they serve the same purposes that `{`, `}`, and `;` serve in other languages.
You can have multiple layers of indentation. For example:
code: |
a = 4
b = 5
c = 2
d = 6
if b > a:
b = 62
if c < d:
answer = 20 + b
else:
answer = 20 + c - d
d = a + b
else:
answer = 40 + b
b = 0
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/code-example-08.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/code-example-08.yml&reset=1 "Click to try this interview")
In addition to greater than (`>`) and less than (`<`), the following conditional operators are available in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
:
* `a == b` is true if `a` equals `b`. There are two equal signs to distinguish this from `a = b`, which sets the value of `a` to the value of `b`. This works with numbers as well as with text.
* `a is b` is essentially synonomous with `a == b`.
* `a >= b` is true if `a` is greater than or equal to `b`.
* `a <= b` is true if `a` is less than or equal to `b`.
* `a in b` is true if `b` is a [list](https://docassemble.org/docs/groups.html#list)
, [dictionary](https://docassemble.org/docs/groups.html#dictionary)
, or [set](https://docassemble.org/docs/groups.html#set)
, and `a` is contained within `b`. For example, `42 in [13, 42, 62]` is true. This also works with text. If you do `a = "Fred"`, then `a in ["Mary", "Fred", "Scott"]` will be true, while `a in ["Harold", "Anthony", "Norman"]` will be false. In the case where `b` is a [dictionary](https://docassemble.org/docs/groups.html#dictionary)
, `a in b` will return true if `a` is a key within `b`.
The following conditions apply when the variables are text.
* `a.rfind(b) >= 0` will return true if `a` and `b` are both text strings and `b` is contained within `a`.
* `a.startswith(b)` returns true if the text in `b` is the start of the text in `a`.
* `a.endswith(b)` returns true if the text in `b` is at the tail end of the text in `a`.
Going through the items in a group[¶](https://docassemble.org/docs/code.html#looping)
--------------------------------------------------------------------------------------
There are special types of variables in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
that help you manage collections of things. For more information about this, see the [groups](https://docassemble.org/docs/groups.html)
section.
The `code` block[¶](https://docassemble.org/docs/code.html#code)
=================================================================
In a **docassemble** interview, a [`question`](https://docassemble.org/docs/questions.html#question)
block tells **docassemble** that if the interview logic wants to know the value of a particular variable, such as `best_fruit_ever`, and that variable has not been defined yet, **docassemble** can pose the question to the user and the user’s answer to the question may provide a definition for that variable.
For example:
---
question: What is the best fruit ever?
fields:
- Fruit: best_fruit_ever
---
This [`question`](https://docassemble.org/docs/questions.html#question)
asks the user to type in the name of the best fruit ever.
The value of variables like `best_fruit_ever` can also be retrieved by running Python code contained within `code` blocks:
---
code: |
best_fruit_ever = "Apple"
---
This `code` “question” is “asked” in much the same way that the previous [`question`](https://docassemble.org/docs/questions.html#question)
question is asked: if and when it needs to be asked. **docassemble** “asks” `code` questions not by asking for the user’s input and then processing the user’s input, but by running the Python code contained in the `code` statement.
As with user [`question`](https://docassemble.org/docs/questions.html#question)
s, **docassemble** might find that “asking” the `code` question did not actually define the needed variable. In that case, it goes looking for another question (which could be of the [`question`](https://docassemble.org/docs/questions.html#question)
or `code` variety) that will provide a definition.
Once `best_fruit_ever` is defined, **docassemble** will not need to run the `code` again if the interview logic calls for `best_fruit_ever` at a later point. In the same way, **docassemble** does not need to ask the user for the user’s name every time it needs to know the user’s name.
The `code` can do anything Python can do, such as retrieve information from the web:
---
import:
- urllib2
---
code:
response = urllib2.urlopen('http://worldsbestfruit.com/')
best_fruit_ever = response.read()
---
or pick a random value from a list:
---
imports:
- random
---
code:
best_fruit_ever = random.choice(['Apple', 'Orange', 'Pear'])
---
(If you don’t remember what an [`imports`](https://docassemble.org/docs/initial.html#imports)
block does, see [initial blocks](https://docassemble.org/docs/initial.html)
.)
All of the variables you set with [`question`](https://docassemble.org/docs/questions.html#question)
blocks are available to your Python code. If your code uses a variable that is not defined yet, **docassemble** will “ask” [`question`](https://docassemble.org/docs/questions.html#question)
blocks and `code` blocks in order to define the variables.
Consider the following example:
---
code: |
if user_age > 60:
product_recommendation = 'Oldsmobile'
else:
product_recommendation = 'Mustang'
---
question: What is your age?
fields:
- Age in Years: user_age
datatype: number
---
If **docassemble** needs to know `product_recommendation`, it will execute the code block, but the code block will fail to execute because `user_age` is undefined. **docassemble** will then go looking for a question that answers `user_age`, and it will ask the user “What is your age?” Upon receiving a response, **docassemble** will continue in its effort to find a definition for `product_recommendation` and will complete the execution of the `code` block.
`code` block modifiers[¶](https://docassemble.org/docs/code.html#modifiers)
============================================================================
You can change the way `code` blocks work by adding modifiers:
* [`reconsider`](https://docassemble.org/docs/logic.html#reconsider)
: If `reconsider` is set to `True`, then **docassemble** will always “reconsider” the values of any of the variables set by the `code` block. That is, every time the interview logic is evaluated (every time the screen loads) **docassemble** will forget about the value of any of the variables set by the `code` block.
* [`initial`](https://docassemble.org/docs/logic.html#initial)
: If `initial` is set to `True`, then **docassemble** will run the code every time the interview logic is evaluated (every time the screen loads).
* [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
: If `mandatory` is set to `True`, then **docassemble** will run the code when the interview logic is evaluated, except that once the code runs through all the way, **docassemble** will remember that the `code` block was successfully run, and it will not re-run it again, as it does with `initial` code.
For more information about these modifiers and how they are used, see the [Interview Logic](https://docassemble.org/docs/logic.html)
section.
Limitations[¶](https://docassemble.org/docs/code.html#limitations)
===================================================================
You can run any [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code within [`code`](https://docassemble.org/docs/code.html#code)
blocks, but there are some constraints based on the way **docassemble** works:
* After each screen loads, the variables are serialized with [pickle](https://docs.python.org/3/library/pickle.html)
. Any name in the global namespace that refers to something non-pickleable will be omitted from this serialization. So, you can define a function `foo()` with some code, but when the next screen loads, the name `foo` will be undefined (as though `reconsider` is set to `True`). Thus, **docassemble** will need to seek out the definition of `foo`, and will re-run the [`code`](https://docassemble.org/docs/code.html#code)
block that defines the function `foo`.
* You can include a `class` definition in [`code`](https://docassemble.org/docs/code.html#code)
, but any instances of objects of that class will not be serializable, and an exception will be raised. So if you want to use [custom classes](https://docassemble.org/docs/objects.html#writing)
, write a [module](https://docassemble.org/docs/packages.html)
, use [`modules`](https://docassemble.org/docs/initial.html#modules)
to import all the names from the module, and use [`objects`](https://docassemble.org/docs/initial.html#objects)
to instantiate objects of your custom class.
* You can use the standard [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
statements `import` and `from ... import` to import names, but if the names you are importing refer to classes of objects that you will create and expect to be serialized, put your `import` and `from ... import` statements in [`initial`](https://docassemble.org/docs/logic.html#initial)
code. Otherwise, the serialization process may raise an exception. Better yet, stick with using [`modules`](https://docassemble.org/docs/initial.html#modules)
and [`imports`](https://docassemble.org/docs/initial.html#imports)
to bring in names from other packages, and then you don’t have to worry about this.
* When **docassemble** prepares the variables for serialization, it will discard non-serializable names in the global namespace, but it **does not do this recursively**. So you can feel free to use non-serializable types in the global namespace, but if you use non-serializable types within lists, dictionaries, or attributes, an exception will be raised.
While **docassemble** will allow you to do many things with code in [`code`](https://docassemble.org/docs/code.html#code)
blocks, it is a best practice to put complicated code into modules, and only use rudimentary [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code in your interviews. Ideally, non-programmers should at least be able to read and edit interview files, because subject matter experts are often not adept at coding. The more [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code you put into an interview file, the more non-programmers will be intimidated by the interview file and be unwilling to work with it. If you can hide complexity behind a simple functional interface, you should do so.
* * *
---
# Question modifiers
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/modifiers.md "Edit this page on GitHub")
Question modifiers
==================
Contents
--------
* [1 Including audio](https://docassemble.org/docs/modifiers.html#audio)
* [2 Including video](https://docassemble.org/docs/modifiers.html#video)
* [3 Providing help text to users](https://docassemble.org/docs/modifiers.html#help)
* [4 Adding images to question: decoration](https://docassemble.org/docs/modifiers.html#decoration)
* [5 Adding JavaScript: script](https://docassemble.org/docs/modifiers.html#script)
* [6 Adding CSS: css](https://docassemble.org/docs/modifiers.html#css)
* [7 The progress bar](https://docassemble.org/docs/modifiers.html#progress)
* [8 The section navigation bar](https://docassemble.org/docs/modifiers.html#section)
* [9 Disable the back button: prevent going back](https://docassemble.org/docs/modifiers.html#prevent%20going%20back)
* [10 Adding a back button inside the question](https://docassemble.org/docs/modifiers.html#back%20button)
* [11 Changing the text of the back button](https://docassemble.org/docs/modifiers.html#back%20button%20label)
* [12 Vocabulary terms and auto terms](https://docassemble.org/docs/modifiers.html#terms)
* [13 The language of the question](https://docassemble.org/docs/modifiers.html#language)
* [14 Changing the continue button label](https://docassemble.org/docs/modifiers.html#continue%20button%20label)
* [15 Changing the continue button color](https://docassemble.org/docs/modifiers.html#continue%20button%20color)
* [16 Reusable questions: generic object](https://docassemble.org/docs/modifiers.html#generic%20object)
* [17 The role of the question](https://docassemble.org/docs/modifiers.html#role)
* [18 Automatically reload the screen](https://docassemble.org/docs/modifiers.html#reload)
* [19 Tag a block with a unique id](https://docassemble.org/docs/modifiers.html#id)
* [20 Google Analytics IDs](https://docassemble.org/docs/modifiers.html#ga%20id)
* [21 Segment IDs](https://docassemble.org/docs/modifiers.html#segment%20id)
* [22 Breadcrumb name](https://docassemble.org/docs/modifiers.html#breadcrumb)
* [23 Manually indicating that a block sets a variable](https://docassemble.org/docs/modifiers.html#sets)
* [24 Indicating that a block only sets certain variables](https://docassemble.org/docs/modifiers.html#only%20sets)
* [25 Indicating that a block is permitted to set a variable](https://docassemble.org/docs/modifiers.html#allowed%20to%20set)
* [26 Changing order of precedence](https://docassemble.org/docs/modifiers.html#precedence)
* [27 Putting conditions on whether a question is applicable](https://docassemble.org/docs/modifiers.html#if)
* [28 Conditionally hiding the Continue button based on a Python expression](https://docassemble.org/docs/modifiers.html#hide%20continue%20button)
* [29 Conditionally disabling the Continue button based on a Python expression](https://docassemble.org/docs/modifiers.html#disable%20continue%20button)
* [30 Turn off variable scanning](https://docassemble.org/docs/modifiers.html#scan%20for%20variables)
* [31 Indicate variables that are prerequisites](https://docassemble.org/docs/modifiers.html#need)
* [32 Indicate prerequisite variables that invalidate a block](https://docassemble.org/docs/modifiers.html#depends%20on)
* [33 Undefine variables when a question is asked](https://docassemble.org/docs/modifiers.html#undefine)
* [34 Obtain new values of variables when a question is asked](https://docassemble.org/docs/modifiers.html#reconsider)
* [35 Include additional buttons on the screen](https://docassemble.org/docs/modifiers.html#action%20buttons)
* [36 Hidden comments](https://docassemble.org/docs/modifiers.html#comment)
There are a number of optional modifiers that can be included in [`question`](https://docassemble.org/docs/questions.html#question)
blocks to control the appearance or behavior of the question. Some of these can also be applied to other types of blocks that set variables, such as `code` blocks.
Including `audio`[¶](https://docassemble.org/docs/modifiers.html#audio)
========================================================================
audio: schumann-clip-1.mp3
question: You need to relax.
subquestion: |
Listen to some Schumann, and then
proceed.
field: user_is_relaxed
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/audio.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/audio.yml&reset=1 "Click to try this interview")
The `audio` modifier allows you to add audio to your questions. An audio player will appear above the question, and the user can press play to hear the sound.
The filename can be constructed with [Mako](https://www.makotemplates.org/)
. A plain file path will be assumed to point to a file in the `static` directory of the package in which the [YAML](https://en.wikipedia.org/wiki/YAML)
file resides. A package reference may also be included: e.g., `docassemble.demo:data/static/schumann-clip-3.mp3`. A URL beginning with `http` or `https` may also be provided.
You can also play uploaded files:
question: Please record some audio.
fields:
- MP3 file: user_audio_file
datatype: file
---
question: |
Let's listen to what you recorded.
audio: ${ user_audio_file }
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/upload_audio.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/upload_audio.yml&reset=1 "Click to try this interview")
Note that in this example, we use `file` as the `datatype`, which is the standard way to upload files. You can also use the `datatype` of [`microphone`](https://docassemble.org/docs/fields.html#microphone)
, which in some browsers (mostly on mobile platforms) will launch an audio recording app to create the file to upload.
question: Please record some audio.
fields:
- MP3 file: user_audio_file
datatype: microphone
---
question: |
Let's listen to what you recorded.
audio: ${ user_audio_file }
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/upload_audio_microphone.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/upload_audio_microphone.yml&reset=1 "Click to try this interview")
**docassemble** uses the [HTML5 audio tag](https://www.w3schools.com/html/html5_audio.asp)
to allow users to play the audio. Not all browsers support every type of audio file. In order to make your audio files accessible to the greatest number of users, then if you provide static audio files, you should include files in both `mp3` and `ogg` format.
For example, if your `audio` specifier points to a file, such as `nyc_question.mp3`, then your interview package should contain a file called `nyc_question.mp3` in the `data/static` directory. If you also include an OGG version of this audio file, called `nyc_question.ogg`, in the same directory, then **docassemble** will make both files available to the user, and the user’s browser will use whichever file works. In your `audio` specifier, you can refer to either the `mp3` or the `ogg` file.
Or, if your `mp3` and `ogg` alternatives are located in different directories, you can do this:
---
question: Are you traveling to New York City?
yesno: going_to_nyc
audio:
- mp3/nyc_question.mp3
- ogg/nyc_question.ogg
---
Or, if you are using hyperlinks to files on another server, you can include different versions by doing something like this:
---
question: Are you traveling to New York City?
yesno: going_to_nyc
audio:
- http://example.com/files/audio/51/nyc_question.mp3
- http://example.com/files/audio/23/nyc_question.ogg
---
If you refer to an uploaded file, **docassemble** will take care of providing both `mp3` and `ogg` versions. When users upload an audio file, **docassemble** tries to convert it to the appropriate formats. For this to work, ffmpeg and pacpl must be installed on your system. Currently, **docassemble** can handle audio files uploaded in `mp3`, `ogg`, `3gpp`, and `wav` formats. If you need to be able to process another type of audio file, **docassemble**’s source code can probably be modified to support that audio type.
Note that there a number of limitations to playing audio in browsers. For example, older Android devices will not play audio retrieved through https, but will play the same audio retrieved through http.
See [special variables](https://docassemble.org/docs/special.html)
for information about **docassemble**’s automatic text-to-speech features.
Including `video`[¶](https://docassemble.org/docs/modifiers.html#video)
========================================================================
The `video` specifier is just like the [`audio`](https://docassemble.org/docs/modifiers.html#audio)
specifier except that it displays a video instead of an audio file.
---
question: Are you traveling to New York City?
yesno: going_to_nyc
video: nyc_tourism.mp4
---
**docassemble** uses the [HTML5 video tag](https://www.w3schools.com/html/html5_video.asp)
to allow users to play the audio. Just as you should include both `mp3` and `ogg` audio files, you should include both `mp4` and `ogv` video files, so that users of many different browsers will all be able to see your videos. These are the two formats that the [HTML5 video tag](https://www.w3schools.com/html/html5_video.asp)
most widely supports.
If you refer to an uploaded video file, **docassemble** will take care of providing both `mp4` and `ogv` versions. When users upload a video file, **docassemble** tries to convert it to the appropriate formats. For this to work, ffmpeg and pacpl must be installed on your system. Currently, **docassemble** can handle videos uploaded in `mp4`, `ogv`, and `mov` formats. If you need to be able to process another type of video, **docassemble**’s source code can probably be modified to support that video type.
You can also use the `video` specifier to embed [YouTube](https://www.youtube.com/)
videos. For example, if you want to embed a [YouTube](https://www.youtube.com/)
video and the URL for the video is `https://www.youtube.com/watch?v=9bZkp7q19f0` or `https://youtu.be/9bZkp7q19f0`, you would write something like this:
field: ready_to_proceed
question: |
Welcome to the interview.
subquestion: |
Please watch this introductory video
before proceeding with the interview.
video: |
[YOUTUBE 9bZkp7q19f0]
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/video.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/video.yml&reset=1 "Click to try this interview")
`[YOUTUBE ...]` assumes that the aspect ratio of the vide is 16:9. If the aspect ratio of the video is 4:3, you can use `[YOUTUBE4:3 ...]`. You can also explicitly state that the aspect ratio is 16:9 by using `[YOUTUBE16:9 ...]`.
Providing `help` text to users[¶](https://docassemble.org/docs/modifiers.html#help)
====================================================================================
question: |
How much money do you wish
to seek in damages?
fields:
- Money: damages_sought
datatype: currency
help: |
If you are not sure how much
money to seek in damages, just ask
for a million dollars, since you
want ${ defendant } to suffer.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/help-damages.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/help-damages.yml&reset=1 "Click to try this interview")
In the web app, users can use the navigation bar to toggle between the “Question” tab and the “Help” tab. The contents of the “Help” tab consist of the contents of any `help` specifiers in the question being presented, followed by the contents of any `interview help` blocks contained within the interview.
You can add audio to your help text:
question: |
How much money do you wish
to seek in damages?
fields:
- Money: damages_sought
datatype: currency
help:
content: |
If you are not sure how much
money to seek in damages, just ask
for a million dollars, since you
want ${ defendant } to suffer.
audio: |
message_re_damages.mp3
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/help-damages-audio.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/help-damages-audio.yml&reset=1 "Click to try this interview")
You can also add video to help text using the `video` specifier.
When [`interview help`](https://docassemble.org/docs/initial.html#interview%20help)
is available but question-specific `help` is not available, the help tab is merely labeled “Help.” When question-specific help is available, the help tab is bright yellow and is marked with a star. If you want the label to be something other than “Help”, you can add a `label` inside the `help` specifier:
question: |
How much money do you wish
to seek in damages?
fields:
- Money: damages_sought
datatype: currency
help:
label: |
More info
content: |
If you are not sure how much
money to seek in damages, just ask
for a million dollars, since you
want ${ defendant } to suffer.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/help-damages-label.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/help-damages-label.yml&reset=1 "Click to try this interview")
If the [`question help button`](https://docassemble.org/docs/initial.html#question%20help%20button)
feature is enabled, and question-specific `help` is available, a “Help” button will be available on the button bar, which when pressed will show the help tab. The button label is “Help” by default, but if a `label` is provided to the question-specific `help`, the button will bear this label instead. When a help button is present, the help tab in the navigation bar will always be labeled “Help,” and it will never be highlighted in yellow.
The default label “Help” can be changed on a per-interview basis. If you set an [`interview help`](https://docassemble.org/docs/initial.html#interview%20help)
initial block and provide a `label` as part of it, the value of this `label` will be used instead of “Help” as the name of the “Help” tab in the navigation bar. You can also use [screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
methods to set this value (which is called `help label`).
Adding images to question: `decoration`[¶](https://docassemble.org/docs/modifiers.html#decoration)
===================================================================================================
decoration: kids
question:
Do you have children?
yesno: has_children
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/decoration.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/decoration.yml&reset=1 "Click to try this interview")
The `decoration` modifier adds an icon to the right of the [`question`](https://docassemble.org/docs/questions.html#question)
text. In the example above, `kids` has been defined in an [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
or [`images`](https://docassemble.org/docs/initial.html#images)
block.
By default, if a `decoration` modifier refers to an image that has not been defined in an [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
or [`images`](https://docassemble.org/docs/initial.html#images)
block, users will see an error message. However, if you set the [`use font awesome`](https://docassemble.org/docs/config.html#use%20font%20awesome)
directive in the [Configuration](https://docassemble.org/docs/config.html)
to `True`, then any reference to an image not defined with [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
or [`images`](https://docassemble.org/docs/initial.html#images)
will be treated as the name of a [Font Awesome](https://fontawesome.com/)
icon.
mandatory: True
decoration: chart-bar
question: |
Third quarter metrics
subquestion: |
We are making more money
:far-fa-money-bill-alt: than
we did in the second quarter.
So you can sleep well tonight! :bed:
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/font-awesome.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/font-awesome.yml&reset=1 "Click to try this interview")
This method also works with [inline icons](https://docassemble.org/docs/markup.html#emoji)
.
Adding [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
: `script`[¶](https://docassemble.org/docs/modifiers.html#script)
================================================================================================================================
If you know how to write [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
and [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
, you can add [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
code and [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
formatting to a question.
question: |
Do you want to build a snowman?
subquestion: |
yesno: wants_to_build_snowman
script: |
setTimeout(function(){
$("#beg").html("Pretty please?");
}, 3000);
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/script.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/script.yml&reset=1 "Click to try this interview")
This [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
will be executed when the screen loads, prior to the triggering of the `daPageLoad` event.
To add [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
or [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
to all questions, you can use a [`features`](https://docassemble.org/docs/initial.html#features)
block to include [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
and [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
files on the web page.
Note that the **docassemble** front end is a “single page application,” which means that when the user presses the Continue button or the Back button, the page does not reload; rather, an Ajax request is sent to the server and then the DOM is redrawn. The code in the `script` modifier is run whenever the `question` is shown. That means that the `script` code may run more than once in the user’s browser session.
Therefore, you should not use code in a `script` modifier to attach a `daPageLoad` listener, because that could mean that the listener will be attached more than once. Therefore, only attach `daPageLoad` listeners from a [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
file that you include using the [`javascript`](https://docassemble.org/docs/initial.html#javascript)
feature.
In general, use of the `script` modifier is discouraged. It is generally better to use a JavaScript file attached using [`features`](https://docassemble.org/docs/initial.html#features)
, and to have code in that file that listens for the `daPageLoad` event.
Adding [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
: `css`[¶](https://docassemble.org/docs/modifiers.html#css)
===============================================================================================================================
The `css` modifier contains raw HTML that will be appended to the HTML ``.
question: |
Do you want to build a snowman?
yesno: wants_to_build_snowman
css: |
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/css.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/css.yml&reset=1 "Click to try this interview")
It is best only to include [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
that is tied to specified HTML elements you include in your questions, rather than include [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
that has global effects (like the example above). Because of the way **docassemble** interviews work, [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
applied in one question will affect later questions until the screen is reloaded.
In the vast majority of situations, you should not use the `css` modifier, but should instead write a CSS file that you include using [`css`](https://docassemble.org/docs/initial.html#css)
in a [`features`](https://docassemble.org/docs/initial.html#features)
block.
The progress bar[¶](https://docassemble.org/docs/modifiers.html#progress)
==========================================================================
A **docassemble** interview can be configured to show a [progress bar](https://docassemble.org/docs/initial.html#features)
. This will show the user a progress indicator to give the user a sense of how much longer the interview will take.
The progress along the bar at any question needs to be set with the `progress` modifier. For example:
features:
progress bar: True
---
question: Are you doing well?
yesno: user_is_well
progress: 20
---
question: Done with the interview.
subquestion: |
% if user_is_well:
I am glad you are doing well.
% else:
I am sorry to hear that!
% endif
progress: 100
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/progress.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/progress.yml&reset=1 "Click to try this interview")
The value of `progress` needs to be a number between 0 and 100. If the value is zero, the progress bar is hidden for the current question. If the value is greater than 100, a full progress bar will be shown. If the value is `None` (`null` in [YAML](https://en.wikipedia.org/wiki/YAML)
), then the progress bar will be hidden and will not advance until it is set to something else.
You can also control the progress meter with the [`get_progress()`](https://docassemble.org/docs/functions.html#get_progress)
and [`set_progress()`](https://docassemble.org/docs/functions.html#set_progress)
functions.
If the [progress bar](https://docassemble.org/docs/initial.html#features)
is enabled and the interview encounters a question that does not have a `progress` setting, the progress bar will advance automatically. The amount by which the progress bar automatically advances gets smaller as the progress bar gets closer to 100%.
As a result, you do not need to attach a `progress` setting to every question; you can just set `progress` on a few questions, and let the automatic advancing mechanism take care of increasing the progress.
If the interview reaches a question with a `progress` setting that is less than the current position of the [progress bar](https://docassemble.org/docs/initial.html#features)
, the position of the [progress bar](https://docassemble.org/docs/initial.html#features)
will stay the same. This ensures that the user does not see the [progress bar](https://docassemble.org/docs/initial.html#features)
go backward.
If you want the [progress bar](https://docassemble.org/docs/initial.html#features)
to go back or reset, you can use the [`set_progress()`](https://docassemble.org/docs/functions.html#set_progress)
function to force the [progress bar](https://docassemble.org/docs/initial.html#features)
setting to a particular value. For example:
mandatory: True
code: |
first_part_done
second_part_done
final_screen
---
code: |
user_is_well
user_is_bigger_than_a_breadbox
ready_for_second_part
set_progress(0)
first_part_done = True
---
code: |
user_likes_turnips
user_likes_clownfish
second_part_done = True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/progress-multi.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/progress-multi.yml&reset=1 "Click to try this interview")
The section navigation bar[¶](https://docassemble.org/docs/modifiers.html#section)
===================================================================================
A **docassemble** interview can be configured to show a left-hand [navigation bar](https://docassemble.org/docs/initial.html#navigation%20bar)
on screens large enough to show one. The navigation bar will contain a list of the sections in the interview, as specified in the [`sections`](https://docassemble.org/docs/initial.html#sections)
initial block or using the [`nav.set_sections()`](https://docassemble.org/docs/functions.html#DANav.set_sections)
function. In the navigation bar, the current section will be highlighted.
Adding the `section` modifier to a question will update the current section when the interview asks the question. This section will continue to be the current section until another question is reached that contains a `section` modifier that specifies a different section.
As explained in the documentation for the [`sections`](https://docassemble.org/docs/initial.html#sections)
initial block, you have the option of referring to a section by a keyword that is different from the name of the section that is displayed to the user. If you are using this feature, your `section` modifier needs to refer to the keyword, not the displayed name.
sections:
- Introduction
- About you:
- Contact info
- Demographics
- Preferences
- Conclusion
---
features:
navigation: True
progress bar: True
---
mandatory: True
code: |
menu_items = [ action_menu_item('Roadmap', 'road_map') ]
---
initial: True
code: |
if returning_user(minutes=0.5):
welcome_back
---
mandatory: True
question: |
Welcome to the interview
subquestion: |
If you are not on a
smartphone-sized device,
you should see a navigation
bar to the left.
field: sees_nav_bar
---
mandatory: True
question: |
I am going to ask you some
questions about yourself.
field: intro_to_about_you
section: About you
---
mandatory: True
question: |
What is your name?
fields:
- First Name: first_name
- Last Name: last_name
section: Contact info
---
mandatory: True
question: |
What is your e-mail address?
fields:
- E-mail: email_address
datatype: email
---
mandatory: True
question: |
What is your gender?
field: gender
choices:
- Male
- Female
- Something else
section: Demographics
---
mandatory: True
question: |
What kind of belly button
do you have?
subquestion: |
To see what a user would
see after returning to
the interview after a period
of absence, try waiting
thirty seconds, then
[click into the\
interview](${ interview_url(local=True) }).
In addition, there is a similar
screen available on the Menu in the
upper-right, under "Roadmap."
field: belly_button
choices:
- Innie
- Outie
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Favorite fruit: favorite_fruit
section: Preferences
---
mandatory: True
question: |
What is your favorite vegetable?
fields:
- Favorite vegetable: favorite_vegetable
---
progress: 100
mandatory: True
question: Thank you.
subquestion: |
${ first_name },
Your answers mean a lot to me.
I am going to go eat some
${ favorite_vegetable }
now.
section: Conclusion
---
event: welcome_back
question: |
Welcome back!
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to pick up
where you left off.
buttons:
Continue: continue
---
event: road_map
question: |
Roadmap
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to resume the
interview.
buttons:
Continue: continue
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections.yml&reset=1 "Click to try this interview")
The `section` can include [Mako](https://www.makotemplates.org/)
templating.
You can also set the current section using the [`nav.set_section()`](https://docassemble.org/docs/functions.html#DANav.set_section)
function.
Disable the back button: `prevent going back`[¶](https://docassemble.org/docs/modifiers.html#prevent%20going%20back)
=====================================================================================================================
Normally, **docassemble** allows the user to click the back button to get back to earlier steps in the interview. Sometimes, it is necessary to prevent the user from doing so.
If you add a `prevent going back` specifier to a [`question`](https://docassemble.org/docs/questions.html#question)
, the web app will not offer the user a back button while showing the question.
prevent going back: True
question: |
Your application for ${ service }
has been submitted.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/prevent-going-back.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/prevent-going-back.yml&reset=1 "Click to try this interview")
There is also a [`prevent_going_back()` function](https://docassemble.org/docs/functions.html#prevent_going_back)
that accomplishes the same thing from [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code. This may be more useful than the `prevent going back` modifier if the need to prevent the user from clicking the back button depends on the outcome of a process.
Adding a back button inside the question[¶](https://docassemble.org/docs/modifiers.html#back%20button)
=======================================================================================================
You can add a “Back” button to the buttons at the bottom of the screen by setting the `back button` modifier.
question: Is the sky blue?
yesno: sky_is_blue
back button: |
not user_is_well
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-back-button-sometimes.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-back-button-sometimes.yml&reset=1 "Click to try this interview")
If `back button` is set to `True` or to [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code that evaluates to a true value, then the button will be shown.
You can configure this on an interview-wide basis by setting the [`question back button`](https://docassemble.org/docs/initial.html#question%20back%20button)
feature.
Changing the text of the back button[¶](https://docassemble.org/docs/modifiers.html#back%20button%20label)
===========================================================================================================
When you add a “Back” button to the buttons at the bottom of the screen by setting the [`back button`](https://docassemble.org/docs/modifiers.html#back%20button)
modifier or the [`question back button`](https://docassemble.org/docs/initial.html#question%20back%20button)
feature, you can change the text of the button using the `back button label` modifier.
question: Is the sky blue?
yesno: sky_is_blue
back button: True
back button label: |
Wait, go back
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-back-button-sometimes-label.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-back-button-sometimes-label.yml&reset=1 "Click to try this interview")
The text of the label can include [Mako](https://www.makotemplates.org/)
templating.
You can change the back button in the upper-left corner by using the `corner back button label` modifier.
For information about other ways to set a default value for back button labels, see the [screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
section.
Vocabulary `terms` and `auto terms`[¶](https://docassemble.org/docs/modifiers.html#terms)
==========================================================================================
Using the modifiers `terms` or `auto terms`, you can specify the definitions of particular vocabulary terms, and **docassemble** will turn them into green hyperlinks. When the user clicks on the hyperlink, a popup appears with the word’s definition.
You can define the vocabulary terms using `terms` and then put curly brackets around the instances of the words that you want to become hyperlinks.
question: Have you ever met a {creeper}?
subquestion: |
If you have met a {zombie pigman}, you
have almost certainly met a creeper.
yesno: met_a_creeper
terms:
creeper: |
A tall ${ creeper_color } creature
that explodes if you get too close.
zombie pigman: |
A harmless creature who carries a gold
sword.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-terms.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-terms.yml&reset=1 "Click to try this interview")
Alternatively, you can define the vocabulary terms using `auto terms`, in which case you do not need to use curly brackets, and the terms will be highlighted in green every time they appear in the question.
question: Have you ever met a creeper?
subquestion: |
If you have met a zombie pigman, you
have almost certainly met a creeper.
yesno: met_a_creeper
auto terms:
creeper: |
A tall ${ creeper_color } creature
that explodes if you get too close.
zombie pigman: |
A harmless creature who carries a gold
sword.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-autoterms.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-autoterms.yml&reset=1 "Click to try this interview")
Using `auto terms` can lead to ambiguities, so it is generally better to use `terms` if you can. If you have two terms, `green apple` and `apple`, then `auto terms` will try to make a term within a term, which will lead to unpredictable behavior. `auto terms` can also cause problems if the term is contained inside of HTML. So while `auto terms` is convenient when it works, it might not always work, so using `terms` is preferable.
If you want to refer to a term but you want the text of the hyperlink to be different from the name of the term, you can use the pipe character `|` and write the alternate text after the `|`.
question: Have you ever met a {creeper}?
subquestion: |
If you have met a
{zombie pigman|zombified pigman},
you have almost certainly met a creeper.
yesno: met_a_creeper
terms:
creeper: |
A tall ${ creeper_color } creature
that explodes if you get too close.
zombie pigman: |
A harmless creature who carries a gold
sword.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-terms-alternate.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-terms-alternate.yml&reset=1 "Click to try this interview")
Alternatively, in your definition of the terms, you can specify that multiple phrases should be associated with a single definition. You write your terms as a list of dictionaries, and if a dictionary has two keys, `phrases` and `definition`, where `phrases` refers to a list of terms and `definition` refers to a definition, that definition will be used for each of the phrases:
terms:
- phrases:
- charged creeper
- creeper
definition: |
A tall green creature that explodes if
you get too close.
- zombie pigman: |
A harmless creature who carries a gold
sword.
If you want vocabulary terms to be highlighted throughout the interview, not just for a specific question, you can use `terms` and `auto terms` as [initial blocks](https://docassemble.org/docs/initial.html)
. You can also define interview-wide terms using [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code by calling the [`update_terms()`](https://docassemble.org/docs/functions.html#update_terms)
function.
The `language` of the question[¶](https://docassemble.org/docs/modifiers.html#language)
========================================================================================
---
question: |
What is the meaning of life?
fields:
- Meaning of life: meaning_life
---
language: es
question: |
¿Cuál es el significado de la vida?
fields:
- Significado de la Vida: meaning_life
---
**docassemble**’s [language support](https://docassemble.org/docs/language.html)
allows a single interview to ask questions different ways depending on the user’s language. You can write questions in different languages that set the same variables. **docassemble** will use whatever question matches the active language.
The value of `language` must be a two-character lowercase [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
or [ISO-639-3](https://en.wikipedia.org/wiki/List_of_ISO_639-3_codes)
code. For example, Spanish is `es`, French is `fr`, and Arabic is `ar`.
For more information about how to set the active language, see [language support](https://docassemble.org/docs/language.html)
.
Instead of explicitly setting a `language` for every question, you can use [default language](https://docassemble.org/docs/initial.html#default%20language)
to apply a particular language to the remaining questions in the file (see [initial blocks](https://docassemble.org/docs/initial.html)
).
Changing the `continue button label`[¶](https://docassemble.org/docs/modifiers.html#continue%20button%20label)
===============================================================================================================
Some types of questions feature a “Continue” button. If you want the label on the button to be something other than the word “Continue,” add a `continue button label` modifier.
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
continue button label: Keep going
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/continue-button-label.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/continue-button-label.yml&reset=1 "Click to try this interview")
The types of questions that feature a “Continue” button include:
* [`field` with `choices`](https://docassemble.org/docs/fields.html#field%20with%20choices)
* [`fields`](https://docassemble.org/docs/fields.html#fields)
* [`continue button field`](https://docassemble.org/docs/fields.html#continue%20button%20field)
This modifier also allows you to customize the “Done” button that appears in [`signature`](https://docassemble.org/docs/fields.html#signature)
questions.
For information about other ways to set a default value for the “Continue” button label, see the [screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
section.
Changing the `continue button color`[¶](https://docassemble.org/docs/modifiers.html#continue%20button%20color)
===============================================================================================================
In addition to modifying the label of the “Continue” button, you can set its color to one of the [Bootstrap colors](https://getbootstrap.com/docs/5.2/customize/color/)
.
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
continue button label: Keep going
continue button color: success
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/continue-button-color.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/continue-button-color.yml&reset=1 "Click to try this interview")
Reusable questions: `generic object`[¶](https://docassemble.org/docs/modifiers.html#generic%20object)
======================================================================================================
generic object: Individual
question: |
Does ${ x } like cats?
yesno: x.likes_cats
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/generic-object.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/generic-object.yml&reset=1 "Click to try this interview")
`generic object` is a very powerful feature in **docassemble** that allows interview developers to express questions in general terms.
The above example will cause **docassemble** to ask “Does Sally Smith like cats?” if the interview logic calls for `neighbor.likes_cats` and `neighbor` is an object of type [`Individual`](https://docassemble.org/docs/objects.html#Individual)
whose name has been set to “Sally Smith.” The same question will also ask “Does William Jones like cats?” if the interview logic calls for `teacher.likes_cats`, and `teacher` is an object of type [`Individual`](https://docassemble.org/docs/objects.html#Individual)
whose name has been set to “William Jones.”
`x` is a special variable that should only be used in `generic object` questions. The above question definition tells **docassemble** that if it ever needs the `likes_cats` attribute for any object of type [`Individual`](https://docassemble.org/docs/objects.html#Individual)
, it can get an answer by asking this question.
If your interview needs a definition for `spouse.likes_cats`, where `spouse` is an object of type `Individual`, **docassemble** will first look for a question that offers to define `spouse.likes_cats`. If no such question exists, it will then look for a question that offers to defined `x.likes_cats`, where the `generic object` is [`Individual`](https://docassemble.org/docs/objects.html#Individual)
. If no such question exists, it will look for `generic object` questions for the parent types of [`Individual`](https://docassemble.org/docs/objects.html#Individual)
. The variables that will be sought, in the order in which they will be sought, are:
* `spouse.likes_cats`
* `x.likes_cats` where `generic object` is [`Individual`](https://docassemble.org/docs/objects.html#Individual)
.
* `x.likes_cats` where `generic object` is [`Person`](https://docassemble.org/docs/objects.html#Person)
.
* `x.likes_cats` where `generic object` is [`DAObject`](https://docassemble.org/docs/objects.html#DAObject)
.
This way, you can provide layers of `generic object` blocks to handle special cases as well as general cases, based on the object type. For example, suppose your interview uses objects of type [`Individual`](https://docassemble.org/docs/objects.html#Individual)
, [`Organization`](https://docassemble.org/docs/objects.html#Organization)
, and [`Person`](https://docassemble.org/docs/objects.html#Person)
. An [`Individual`](https://docassemble.org/docs/objects.html#Individual)
is a special type of [`Person`](https://docassemble.org/docs/objects.html#Person)
, and an [`Organization`](https://docassemble.org/docs/objects.html#Organization)
is also a special type of [`Person`](https://docassemble.org/docs/objects.html#Person)
. Suppose you have a general way of asking for a mailing address (“What is so-and-so’s address?”), but you want to have a special way of asking the question if you need the mailing address of an [`Organization`](https://docassemble.org/docs/objects.html#Organization)
(e.g., “What is ABC Incorporated’s primary place of business?”). You would write a question with `generic object: Person` for the general case, and a question with `generic object: Organization` for the special case. The general question would be used for objects of type [`Individual`](https://docassemble.org/docs/objects.html#Individual)
and [`Person`](https://docassemble.org/docs/objects.html#Person)
, and the special question would be used for objects of type [`Organization`](https://docassemble.org/docs/objects.html#Organization)
.
You can also use `generic object` [`code`](https://docassemble.org/docs/code.html#code)
blocks in a [fallback](https://docassemble.org/docs/logic.html#fallback)
arrangement to capture special cases within object types. Suppose you have a function `retrieve_ein()` that can automatically determine an organization’s Employer Identification Number (EIN), but only for organizations organized as non-profits. For organizations not organized as non-profits, you will need to ask the user for the EIN. You could use the following two blocks to accomplish this:
generic object: Organization
question: |
What is the EIN of ${ x }?
fields:
- EIN: x.ein
---
generic object: Organization
code: |
if x.tax_status == '501c3':
x.ein = retrieve_ein(x.name)
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/generic-object-ein.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/generic-object-ein.yml&reset=1 "Click to try this interview")
Whenever the `.ein` of an organization is needed, the [`code`](https://docassemble.org/docs/code.html#code)
block will be run, but the attribute will not be set if the organization is not a non-profit. In that case, **docassemble** will notice that the attribute is still not defined, and it will “fall back” to the [`question`](https://docassemble.org/docs/questions.html#question)
that asks the user to manually enter the EIN.
As explained in the [fallback](https://docassemble.org/docs/logic.html#fallback)
section of the documentation, the order in which these two blocks appear in the [YAML](https://en.wikipedia.org/wiki/YAML)
file matters; the [`code`](https://docassemble.org/docs/code.html#code)
block will be tried first only if it appears later in the [YAML](https://en.wikipedia.org/wiki/YAML)
file than the [`question`](https://docassemble.org/docs/questions.html#question)
block.
The `generic object` modifier can be used on any block that sets a variable, including [`question`](https://docassemble.org/docs/questions.html#question)
, [`code`](https://docassemble.org/docs/code.html#code)
, [`template`](https://docassemble.org/docs/initial.html#template)
, [`table`](https://docassemble.org/docs/initial.html#table)
, [`attachment`](https://docassemble.org/docs/documents.html#attachment)
, and [`objects`](https://docassemble.org/docs/initial.html#objects)
, [`objects from file`](https://docassemble.org/docs/initial.html#objects%20from%20file)
, [`data`](https://docassemble.org/docs/initial.html#data)
, [`data from code`](https://docassemble.org/docs/initial.html#data%20from%20code)
.
A similar feature to `generic object` and its special variable `x` is the special [index variable `i`](https://docassemble.org/docs/fields.html#index%20variables)
. For more information about this feature, see the [index variable documentation](https://docassemble.org/docs/fields.html#index%20variables)
and the documentation in the [groups section](https://docassemble.org/docs/groups.html)
.
The `role` of the question[¶](https://docassemble.org/docs/modifiers.html#role)
================================================================================
---
role: advocate
question: Is the client's explanation a sound one?
subquestion: |
${ client } proposed the following explanation:
> ${ explanation }
Is this a legally sufficient explanation?
yesno: explanation_is_sound
---
If your interview uses the [roles](https://docassemble.org/docs/roles.html)
feature for multi-user interviews, the `role` modifier in a [`question`](https://docassemble.org/docs/questions.html#question)
block will tell **docassemble** that if it ever tries to ask this question, the user will need to have a particular role in order to proceed.
`role` can be a list.
role:
- advocate
- supervisor
In this case, the user’s role can either “advocate” or “supervisor” in order to be asked the question.
If the user does not have an appropriate role, **docassemble** will look for a question in the interview in which `event` has been set to `role_event`.
Automatically `reload` the screen[¶](https://docassemble.org/docs/modifiers.html#reload)
=========================================================================================
To cause the screen to reload in the web browser after a number of seconds, use the `reload` modifier.
reload: True
question: |
You have viewed this screen
${ counter }
${ noun_plural('time', counter) }.
field: acknowledged
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/reload.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/reload.yml&reset=1 "Click to try this interview")
If you set `reload` to `True`, the screen on which the question is asked will reload every 10 seconds. To use a different number of seconds, set `reload` to the number of seconds you wish to use. E.g.,
reload: 5
Since it is not good to reload the screen too quickly, you cannot use a number of seconds less than four. If the number of seconds is less than four, four seconds will be used as the number of seconds.
You can use [Mako](https://www.makotemplates.org/)
to determine the number of seconds. If the `reload` value evaluates to `False` or `None`, the screen will not reload.
Tag a block with a unique id[¶](https://docassemble.org/docs/modifiers.html#id)
================================================================================
In some situations, you may need to tag a block in your interview with a unique ID. You can use the `id` specifier to do so.
---
id: initialize
mandatory: True
code: |
initial_value = 48
---
In the absence of an `id` specifier, **docassemble** would refer to a block like this with a name like `Question_3` (if this block was the third block in the interview). But with `id` set to `initialize`, **docassemble** will internally refer to this block with the ID `initialize`.
In most cases, your blocks do not need to have unique IDs. However, there are some features in **docassemble**, such as the [Google Analytics integration](https://docassemble.org/docs/config.html#google%20analytics)
feature, the [CSS custom class](https://docassemble.org/docs/initial.html#css%20customization)
feature, and the [changing order of precedence](https://docassemble.org/docs/modifiers.html#precedence)
feature discussed [below](https://docassemble.org/docs/modifiers.html#precedence)
, all of which use `id` specifiers.
Also, in some situations, it can be important to tag your interview blocks with a unique name that does not change when the blocks in the interview [YAML](https://en.wikipedia.org/wiki/YAML)
file change. This is because when **docassemble** stores interview answers, it not only stores the current state of the interview variables, but it also stores information about which `mandatory` blocks have run to completion. When it does so, it tracks the block using the ID for the block. If the IDs are arbitrary names like `Question_3`, users could encounter problems
For example, think about what would happen if a user started working an interview on April 3, and got half-way through, and then saved her answers and logged out, intending to log back in on April 10. Then suppose that on April 8, you install a new version of the interview, adding new functionality. When the user logs back in on April 10, her interview answers might not be compatible with your new version of the interview. For example, suppose that on April 3, the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html#code)
block known as `Question_12` ran to completion. But when the user logs in on April 10 and resumes the interview, the code block formerly known as `Question_12` is now known as `Question_14`. When **docassemble** evaluates her interview session, it will determine that the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html#code)
block known as `Question_14` has not run yet, so it will run that code block. This might cause information in the user’s session to be overwritten. You can avoid problems like these by tagging your code blocks with `id` tags, so that the names of the blocks do not change between versions of your interview.
Another way to avoid problems with the impact of software upgrades on existing sessions is to use a different interview [YAML](https://en.wikipedia.org/wiki/YAML)
file for each version of an interview. So a user that starts `docassemble.tax:data/questions/tax-controversy-v2.yml` will always use the same [YAML](https://en.wikipedia.org/wiki/YAML)
file, even when users who started later are using `docassemble.tax:data/questions/tax-controversy-v3.yml`.
The `id` of a question needs to be set in order to use the [`forget_result_of()`](https://docassemble.org/docs/functions.html#forget_result_of)
function.
Google Analytics IDs[¶](https://docassemble.org/docs/modifiers.html#ga%20id)
=============================================================================
If you are using the [Google Analytics integration](https://docassemble.org/docs/config.html#google%20analytics)
feature, by default, the [`id`](https://docassemble.org/docs/modifiers.html#id)
will be used as part of the pseudo-path sent to Google Analytics. If you would rather use a different ID, you can explicitly set a `ga id`:
id: lead certification
ga id: landlordLeadCert
question: |
Does your landlord have a valid lead certification?
yesno: lead_certification_exists
Segment IDs[¶](https://docassemble.org/docs/modifiers.html#segment%20id)
=========================================================================
If you are using the [Segment integration](https://docassemble.org/docs/config.html#segment%20id)
feature, by default, the [`id`](https://docassemble.org/docs/modifiers.html#id)
will be used as part of the pseudo-path sent to Google Analytics. If you would rather use a different ID, you can explicitly set a `segment id`:
id: lead certification
segment id: landlordLeadCert
question: |
Does your landlord have a valid lead certification?
yesno: lead_certification_exists
You can also send [Segment](https://segment.com/)
messages with arguments, using the `segment` specifier pointing to a dictionary with keys for `id` and `arguments`:
id: lead certification
segment:
id: landlord
arguments:
certification: lead
question: |
Does your landlord have a valid lead certification?
yesno: lead_certification_exists
Breadcrumb name[¶](https://docassemble.org/docs/modifiers.html#breadcrumb)
===========================================================================
If you are using a [`DABreadCrumbs`](https://docassemble.org/docs/objects.html#DABreadCrumbs)
object to display breadcrumbs to the user, you can use the `breadcrumb` modifier on any [`question`](https://docassemble.org/docs/questions.html#question)
to indicate the title of the [`question`](https://docassemble.org/docs/questions.html#question)
as it should appear in the breadcrumbs display. [Mako](https://www.makotemplates.org/)
can be used in a `breadcrumb` modifier. If no `breadcrumb` modifier is present, the text of the `question` specifier is used instead. For more information about using breadcrumbs, see the documentation for [`DABreadCrumbs`](https://docassemble.org/docs/objects.html#DABreadCrumbs)
.
Manually indicating that a block sets a variable[¶](https://docassemble.org/docs/modifiers.html#sets)
======================================================================================================
Usually, **docassemble** can figure out which variables a block is capable of defining. If a code block consists of:
---
code: |
if hell.temperature_in_celcius == 0:
claim_is_valid = True
---
then **docassemble** will try to run it if it needs a definition for `claim_is_valid`. Sometimes, however, **docassemble** needs a hint.
You can explicitly indicate that a block sets a variable using `sets`:
---
sets: claim_is_valid
code: |
if hell.temperature_in_celcius == 0:
claim_is_valid = True
---
It also accepts multiple values:
---
sets:
- claim_is_valid
- type_of_claim
code: |
if hell.temperature_in_celcius == 0:
claim_is_valid = True
type_of_claim = 'tort'
---
Indicating that a block only sets certain variables[¶](https://docassemble.org/docs/modifiers.html#only%20sets)
================================================================================================================
The `only sets` modifier acts like `sets`, but also indicates `scan for variables: False`.
The following two blocks are equivalent.
scan for variables: False
sets: property_assessed
code: |
if total_property > 50000
property_deduction_available = False
property_assessed = True
only sets: property_assessed
code: |
if total_property > 50000
property_deduction_available = False
property_assessed = True
Indicating that a block is permitted to set a variable[¶](https://docassemble.org/docs/modifiers.html#allowed%20to%20set)
==========================================================================================================================
If you have enabled [`restrict input variables`](https://docassemble.org/docs/config.html#restrict%20input%20variables)
in your [Configuration](https://docassemble.org/docs/config.html)
, then you will not be able to use custom [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
to cause [`question`](https://docassemble.org/docs/questions.html#question)
blocks to set variables other than the variables specified in the [YAML](https://en.wikipedia.org/wiki/YAML)
of the [`question`](https://docassemble.org/docs/questions.html#question)
blocks. The `allowed to set` modifier allows you to get around this limitation by listing specific variable names that the [`question`](https://docassemble.org/docs/questions.html#question)
block should be allowed to set. You can set `allowed to set` to a [YAML](https://en.wikipedia.org/wiki/YAML)
list of variable names or to a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
that evaluates to a list of variable names.
Changing order of precedence[¶](https://docassemble.org/docs/modifiers.html#precedence)
========================================================================================
As explained in [how **docassemble** finds questions for variables](https://docassemble.org/docs/logic.html#variablesearching)
, if there is more than one [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html#code)
block that offers to define a particular variable, blocks that are later in the [YAML](https://en.wikipedia.org/wiki/YAML)
file will be tried first.
For example, suppose your friend developed a [YAML](https://en.wikipedia.org/wiki/YAML)
file with questions and code blocks that define the variables `client.age`, `client.eligible`, and `docket_number`. In your interview, you would like to define `client.age` and `client.eligible` the same way your friend does. You can accomplish this by using [`include`](https://docassemble.org/docs/initial.html#include)
to incorporate by reference your friend’s [YAML](https://en.wikipedia.org/wiki/YAML)
file. But suppose you don’t like the way your friend asks the question to determine `docket_number`. No problem; just write a [`question`](https://docassemble.org/docs/questions.html#question)
in your own [YAML](https://en.wikipedia.org/wiki/YAML)
file that defines `docket_number`, and make sure that this [`question`](https://docassemble.org/docs/questions.html#question)
appears after the [`include`](https://docassemble.org/docs/initial.html#include)
block that incorporates your friend’s [YAML](https://en.wikipedia.org/wiki/YAML)
file. That way, your question will be used instead of your friend’s.
However, there may be times when the relative placement of blocks within the [YAML](https://en.wikipedia.org/wiki/YAML)
file is not a convenient way for you to designate which questions override other questions.
For example, suppose there are two [`question`](https://docassemble.org/docs/questions.html#question)
blocks in your interview that define `favorite_fruit`. The second one is always used because it appears later in the [YAML](https://en.wikipedia.org/wiki/YAML)
; the second question supersedes the first.
question: |
What the heck is your favorite fruit?
fields:
Fruit: favorite_fruit
---
question: |
What is your favorite fruit?
fields:
Fruit: favorite_fruit
---
mandatory: True
question: |
Your favorite fruit is
${ favorite_fruit }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/supersede-regular.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/supersede-regular.yml&reset=1 "Click to try this interview")
If you wanted the first question to be asked instead, you could rearrange the order of questions, but what if you wanted to keep the order the same?
One alternative is to use the `id` and `supersedes` specifiers:
id: informal favorite fruit question
supersedes: regular favorite fruit question
question: |
What the heck is your favorite fruit?
fields:
Fruit: favorite_fruit
---
id: regular favorite fruit question
question: |
What is your favorite fruit?
fields:
Fruit: favorite_fruit
---
mandatory: True
question: |
Your favorite fruit is
${ favorite_fruit }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/supersede.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/supersede.yml&reset=1 "Click to try this interview")
In this example, the `id` and `supersedes` specifiers tell the interview that the first question takes precedence over the second.
Another way of changing the order of precedence is to use the [`order` initial block](https://docassemble.org/docs/initial.html#order)
.
Putting conditions on whether a question is applicable[¶](https://docassemble.org/docs/modifiers.html#if)
==========================================================================================================
If you have multiple [`question`](https://docassemble.org/docs/questions.html#question)
s in your interview that define a given variable, you can tell **docassemble** under what conditions a given question may be asked. You do so by using the `if` modifier.
question: |
Describe your intelligence.
field: user_intelligence
choices:
- Smart
- Dumb
---
if: |
user_intelligence == 'Smart'
question: |
What is the square root of 50% of 32?
fields:
- Answer: answer
datatype: integer
---
if: |
user_intelligence == 'Dumb'
question: |
What is 2+2?
fields:
- Answer: answer
datatype: integer
---
mandatory: True
question: |
% if answer == 4:
That is correct.
% else:
Wrong answer.
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/if.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/if.yml&reset=1 "Click to try this interview")
Here’s how this works:
* The [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question requires a definition of `answer`, so the interview looks for blocks that offer to define `answer`.
* The interview considers asking the “What is 2+2?” question. It considers this question first because it appears last in the [YAML](https://en.wikipedia.org/wiki/YAML)
source.
* This question has a condition, so the interview evaluates the [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
. However, the expression depends on the variable `user_intelligence`, which is undefined, so the interview asks a question to determine that value of this variable.
* When the user answers the `user_intelligence` question, the interview tries to ask the `mandatory` question again, then looks for a definition of `answer`, then considers asking the “What is 2+2?” question, then evaluates the `if` expression.
* If the expression evaluates to true, then the interview asks “What is 2+2?”
* If the expression evaluates to false, then the interview skips the question and moves on to the “What is the square root of 50% of 32?” question. It evaluates the `if` statement, and will ask the question if the expression evaluates to true.
The content of the `if` modifier must be a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
or a list of [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
s. If a list of expressions is provided, each expression must evaluate to true in order for the question to be asked.
Conditionally hiding the Continue button based on a Python expression[¶](https://docassemble.org/docs/modifiers.html#hide%20continue%20button)
===============================================================================================================================================
Many `question` blocks have “Continue” buttons. If you want the “Continue” button to be hidden in some circumstances, so that the user cannot continue, you can set `hide continue button` to a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
that, if true, will cause the Continue button to be unavailable.
You might use this on a `question` that contains an [action](https://docassemble.org/docs/functions.html#url_action)
the user can perform in order to make a correction, which may cause the Continue button to appear.
question: |
% if user_number == number_to_enter:
You have passed the short-term memory test.
% else:
You are wrong.
% endif
action buttons:
- label: |
Try again
action: user_number
show if: |
user_number != number_to_enter
color: primary
hide continue button: |
user_number != number_to_enter
continue button field: threshold_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/hide-continue-button.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/hide-continue-button.yml&reset=1 "Click to try this interview")
Note that the condition specified by `hide continue button` is a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
. This expression can only evaluate values of variables that have already been defined.
If you want a Continue button to be shown or hidden based on the values of fields on the screen, you would need to write your own [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
to reveal the button. To reveal the button with [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
, you can run `$("#da-continue-button").show()`.
Note that because the button can be revealed with [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
, the `hide continue button` feature should be considered cosmetic, not something that you should rely on for enforcing interview logic. A user with experience with [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
could figure out how to reveal the button and press it.
Unless you are a [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
programmer, you should not use `hide continue button` on a `question` that uses `fields`, `choices`, `dropdown`, etc., because the user would have no way to submit the input. `hide continue button` is primarily useful for a screen that should either be either a dead-end screen or a `continue button field` screen, depending on a condition expressed in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
.
Conditionally disabling the Continue button based on a Python expression[¶](https://docassemble.org/docs/modifiers.html#disable%20continue%20button)
=====================================================================================================================================================
`disable continue button` is just like [`hide continue button`](https://docassemble.org/docs/modifiers.html#hide%20continue%20button)
except that instead of hiding the Continue button, it shows the button with the [disabled attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled)
set.
question: |
% if user_number == number_to_enter:
You have passed the short-term memory test.
% else:
You are wrong.
% endif
subquestion: |
% if user_number != number_to_enter:
${ action_button_html(url_action('user_number'), label="Try again") }
% endif
disable continue button: |
user_number != number_to_enter
continue button field: threshold_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/disable-continue-button.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/disable-continue-button.yml&reset=1 "Click to try this interview")
To enable the button using [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
, run `$("#da-continue-button").prop("disabled", false)`.
Turn off variable scanning[¶](https://docassemble.org/docs/modifiers.html#scan%20for%20variables)
==================================================================================================
By default, **docassemble** looks at every block in your interview and automatically discerns what variables each block is capable of setting. Then, when it is evaluating the interview logic, if it encounters an undefined variable it goes through all the blocks that are capable of defining the variable. As discussed above, if there are multiple blocks that are capable of defining a variable, it tries the ones that are later in the file first, unless an [`order` initial block](https://docassemble.org/docs/initial.html#order)
or a [`supersedes` modifier](https://docassemble.org/docs/modifiers.html#precedence)
alters that order.
Sometimes, however, a block that **docassemble** tries to use to define a variable is one that you don’t want **docassemble** to even consider when looking for a way to define a variable.
This is particularly likely to happen when you have code that changes the values of previously-defined variables.
For example, in this interview, the intention is that:
* A variable is gathered from the user
* The variable is reported back to the user
* Then variable is changed through code
* The variable is reported to the user again.
question: |
What is the best color?
fields:
- Color: best_color
---
question: |
What is the time of day?
field: time_of_day
choices:
- Night
- Day
---
mandatory: True
question: |
The best color is
${ best_color }.
field: initial_color_seen
---
mandatory: True
code: |
if time_of_day == 'Night':
best_color = 'black'
else:
best_color = 'blue'
---
mandatory: True
question: |
The best color is now
${ best_color }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/scan-for-variables-original.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/scan-for-variables-original.yml&reset=1 "Click to try this interview")
However, this interview does something the interview developer did not intend: when it goes looking for a definition for `best_color`, the first thing it does is run the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
code block that depends on `time_of_day`. So the first question that gets asked is `time_of_day`, not `best_color`. “Ugh!” the developer thinks, “that’s not what I wanted! I only wanted that mandatory code block to be run later in the interview.”
To fix this problem, the developer can modify the code block with `scan for variables: False`:
question: |
What is the best color?
fields:
- Color: best_color
---
question: |
What is the time of day?
field: time_of_day
choices:
- Night
- Day
---
mandatory: True
question: |
The best color is
${ best_color }.
field: initial_color_seen
---
scan for variables: False
mandatory: True
code: |
if time_of_day == 'Night':
best_color = 'black'
else:
best_color = 'blue'
---
mandatory: True
question: |
The best color is now
${ best_color }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/scan-for-variables.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/scan-for-variables.yml&reset=1 "Click to try this interview")
Now, when **docassemble** goes searching for a block that will define `best_color`, it will disregard the [`code`](https://docassemble.org/docs/code.html#code)
block that depends on `time_of_day`.
This modifier can be used on any block that sets variables to make it effectively “invisible” to **docassemble**’s automatic logic. If a block is marked with `scan for variables: False`, the [`event`](https://docassemble.org/docs/fields.html#event)
and [`sets`](https://docassemble.org/docs/modifiers.html#sets)
modifiers will still be effective, so you can use them to explicitly indicate that a block should be tried when the interview needs a definition of a particular variable.
In this variation of the interview, for example, we first want to gather `best_color` from the user. Then we want to determine `best_thing` based on the time of day, and we want a side effect of setting `best_thing` to be setting `best_color` to something different.
question: |
What is the best color?
fields:
- Color: best_color
---
question: |
What is the time of day?
field: time_of_day
choices:
- Night
- Day
---
scan for variables: False
sets:
- best_thing
code: |
if time_of_day == 'Night':
best_thing = 'astronomy'
best_color = 'black'
else:
best_thing = 'the beach'
best_color = 'blue'
---
mandatory: True
field: initial_screen
question: |
According to you, the best color
is ${ best_color }.
---
mandatory: True
question: |
Since the best thing is
${ best_thing }, the best color is
${ best_color }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/scan-for-variables-sets.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/scan-for-variables-sets.yml&reset=1 "Click to try this interview")
If we did not use `scan for variables: False`, then the interview would never ask the user for `best_color`; the [`code`](https://docassemble.org/docs/code.html#code)
block would have been used to get an initial definition of `best_color`. But by turning off automatic variable scanning and explicitly indicating that the [`code`](https://docassemble.org/docs/code.html#code)
block should only be used for determining the definition of `best_thing`, we were able to get the interview to behave the way we wanted it to.
If you want to use `scan for variables: False` along with [`sets`](https://docassemble.org/docs/modifiers.html#sets)
, a shorthand is to use [`only sets`](https://docassemble.org/docs/modifiers.html#only%20sets)
.
question: |
What is the best color?
fields:
- Color: best_color
---
question: |
What is the time of day?
field: time_of_day
choices:
- Night
- Day
---
only sets: best_thing
code: |
if time_of_day == 'Night':
best_thing = 'astronomy'
best_color = 'black'
else:
best_thing = 'the beach'
best_color = 'blue'
---
mandatory: True
field: initial_screen
question: |
According to you, the best color
is ${ best_color }.
---
mandatory: True
question: |
Since the best thing is
${ best_thing }, the best color is
${ best_color }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/only-sets.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/only-sets.yml&reset=1 "Click to try this interview")
Indicate variables that are prerequisites[¶](https://docassemble.org/docs/modifiers.html#need)
===============================================================================================
You can add a `need` specifier to a block to indicate that before the block is used, the definitions of one or more variables must be obtained. This specifier is explained in the [`need`](https://docassemble.org/docs/logic.html#need)
subsection of the [Logic section](https://docassemble.org/docs/logic.html)
.
Indicate prerequisite variables that invalidate a block[¶](https://docassemble.org/docs/modifiers.html#depends%20on)
=====================================================================================================================
You can add a [`depends on`](https://docassemble.org/docs/logic.html#depends%20on)
specifier to a block to indicate a list of variables that, when changed, should invalidate the variables set by the block. It also indicates that the listed variables should be asked first (the functionality of the [`need`](https://docassemble.org/docs/logic.html#need)
modifier). This specifier is explained in the [`depends on`](https://docassemble.org/docs/logic.html#depends%20on)
subsection of the [Logic section](https://docassemble.org/docs/logic.html)
.
Undefine variables when a `question` is asked[¶](https://docassemble.org/docs/modifiers.html#undefine)
=======================================================================================================
You can add an `undefine` specifier to a block to indicate that before the block is used, one or more variables must be undefined if they are defined. This specifier is explained in the [`undefine`](https://docassemble.org/docs/logic.html#undefine)
subsection of the [Logic section](https://docassemble.org/docs/logic.html)
.
Obtain new values of variables when a `question` is asked[¶](https://docassemble.org/docs/modifiers.html#reconsider)
=====================================================================================================================
You can add a `reconsider` specifier to a block to indicate that before the block is used, the definitions of one or more variables must be obtained and re-obtained if they are already obtained. This specifier is explained in the [`reconsider`](https://docassemble.org/docs/logic.html#reconsider)
subsection of the [Logic section](https://docassemble.org/docs/logic.html)
.
Include additional buttons on the screen[¶](https://docassemble.org/docs/modifiers.html#action%20buttons)
==========================================================================================================
The [`action buttons`](https://docassemble.org/docs/questions.html#action%20buttons)
modifier allows you to include additional buttons on the screen.
Hidden `comment`s[¶](https://docassemble.org/docs/modifiers.html#comment)
==========================================================================
To make a note to yourself about a question, which will not be seen by the end user, you can use a `comment` specifier. It will be ignored by **docassemble**, so it can contain any valid [YAML](https://en.wikipedia.org/wiki/YAML)
.
question: |
Do you agree the weather
is nice today?
yesno: day_is_nice
comment: |
We might wish to consider
taking out this question.
It does not seem necessary.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/comment-weather.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/comment-weather.yml&reset=1 "Click to try this interview")
* * *
---
# Marking up text
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/markup.md "Edit this page on GitHub")
Marking up text
===============
Contents
--------
* [1 Markdown](https://docassemble.org/docs/markup.html#markdown)
* [1.1 When you don’t want text interpreted as Markdown](https://docassemble.org/docs/markup.html#avoiding)
* [1.2 Mixing Markdown with HTML](https://docassemble.org/docs/markup.html#markdownhtml)
* [2 Using Mako for logic and generated text](https://docassemble.org/docs/markup.html#mako)
* [2.1 Formatting variables](https://docassemble.org/docs/markup.html#formatting)
* [3 Inserting images](https://docassemble.org/docs/markup.html#inserting%20images)
* [4 Inserting inline icons](https://docassemble.org/docs/markup.html#emoji)
* [5 Inserting audio and video](https://docassemble.org/docs/markup.html#audio%20and%20video)
* [6 Inserting QR codes](https://docassemble.org/docs/markup.html#qr)
* [7 Inserting other types of files](https://docassemble.org/docs/markup.html#inserting%20other)
* [8 Inserting tables](https://docassemble.org/docs/markup.html#tables)
* [9 Embedding fields](https://docassemble.org/docs/markup.html#field)
* [10 Embedding areas for interim information](https://docassemble.org/docs/markup.html#target)
**docassemble** allows you to format your text using [Markdown](https://daringfireball.net/projects/markdown/)
and to use [Mako](https://www.makotemplates.org/)
to make your text “smart.” These [mark up](https://en.wikipedia.org/wiki/Markup_language)
methods are available for use in [`question`](https://docassemble.org/docs/questions.html#question)
text, field labels, [`interview help`](https://docassemble.org/docs/initial.html#interview%20help)
text, the content of [documents](https://docassemble.org/docs/documents.html)
, and other text elements.
Markdown[¶](https://docassemble.org/docs/markup.html#markdown)
===============================================================
The syntax of [Markdown](https://daringfireball.net/projects/markdown/)
is explained well [elsewhere](https://daringfireball.net/projects/markdown/)
.
When generating [documents](https://docassemble.org/docs/documents.html)
from [Markdown](https://daringfireball.net/projects/markdown/)
, **docassemble** uses [Pandoc](https://pandoc.org/)
to convert [Markdown](https://daringfireball.net/projects/markdown/)
to PDF, RTF, and HTML. (Unless you are using [Microsoft Word templates](https://docassemble.org/docs/documents.html#docx%20template%20file)
, in which case you will use the [Jinja2](https://jinja.palletsprojects.com/en/3.0.x/)
templating language in the Word document.)
Here are some examples of things you can do with Markdown.
question: Markdown demonstration
subquestion: |
This is *italic text*.
This is **bold text**.
This is __also bold text__.
> This is some block-quoted
> text
### This is a heading
This is an image from the internet:

Here is a bullet list:
* Apple
* Peach
* Pear
Here is a numbered list:
1. Nutmeg
2. Celery
3. Oregano
Here is a
[link to a web site](http://google.com).
mandatory: true
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/markdown-demo.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/markdown-demo.yml&reset=1 "Click to try this interview")
All of these types of markup will format text in questions as well as text in assembled documents (with the exception of the `!` image insertion command, which does not work within PDF and RTF documents).
When [Markdown](https://daringfireball.net/projects/markdown/)
is converted to [HTML](https://en.wikipedia.org/wiki/HTML)
, external hyperlinks (as well as internal hyperlinks to documents) will open in a separate tab, but internal links will open in the same tab. To force an internal link to open in a separate tab, you can write the links this way:
Check out [other interviews](${ url_of('dispatch') }){:target="_blank"}.
If you want to force an external hyperlink to open in the same window, write raw HTML like this instead of a [Markdown](https://daringfireball.net/projects/markdown/)
hyperlink:
Check out the web site
When you don’t want text interpreted as Markdown[¶](https://docassemble.org/docs/markup.html#avoiding)
-------------------------------------------------------------------------------------------------------
Markdown interprets characters like `>`, `*`, `_`, `#`, `.`, and spaces at the beginning of a line as formatting marks. However, sometimes you want to use these characters literally. For example:
* `# of items` means “number of items,” not a section heading.
* `> 18` means “over eighteen,” not a block-quoted “18.”
Or you might want to write:
The fourth and sixth rules are the most stringent.
4. Brush your teeth before going to bed.
6. Don't run red lights.
and Markdown will give you:
1. Brush your teeth before going to bed.
2. Don’t run red lights.
If you don’t want text to be transformed by the Markdown formatter, you can insert the escape character `\` before a special character to indicate that you do not want the Markdown formatter to interpret the special character as a formatting mark.
\# of items`
\> 18`
The fourth and sixth rules are the most stringent.
4\. Brush your teeth before going to bed.
6\. Don't run red lights.
This will result in the text you want:
\# of items
\> 18
The fourth and sixth rules are the most stringent.
4\. Brush your teeth before going to bed.
6\. Don’t run red lights.
These are the rules of Markdown. When you are writing Markdown inside of [YAML](https://en.wikipedia.org/wiki/YAML)
, you need to account for the fact that [YAML](https://en.wikipedia.org/wiki/YAML)
processes the `\` character in special ways in certain circumstances. Inside of a [YAML](https://en.wikipedia.org/wiki/YAML)
double-quoted string, you need to write `\\#`, `\\>`, and `\\.` instead of `\#`, `\>`, and `\.`
question: What do you choose?
fields:
- Item: the_item
input type: radio
choices:
- "\\> 18": over_eighteen
- "< 60": under_sixty
- "\\# of items": number_of_items
- "b \\*a\\* c": with_asterisks
- "b \\_a\\_ c": with_underscores
Inside of a [YAML](https://en.wikipedia.org/wiki/YAML)
block quote, or inside of single quotes, or when you do not indicate a quoting method, you only need to use one `\` character.
question: |
Please choose b \_a\_ c
fields:
- Item: the_item
input type: radio
choices:
- 'b \_a\_ c': with_underscores
- c \_a\_ a: also_with_underscores
Using quotation marks in [YAML](https://en.wikipedia.org/wiki/YAML)
is usually a good idea, because [YAML](https://en.wikipedia.org/wiki/YAML)
has a lot of complicated rules, and you never know when the punctuation in your text is going to trigger one of those rules.
Mixing Markdown with HTML[¶](https://docassemble.org/docs/markup.html#markdownhtml)
------------------------------------------------------------------------------------
Markdown is not a syntax for formatting; it is a deliberately simplified format that supports only a few formatting features. If you want to customize the details of how the web interface works, you can mix HTML with Markdown.
**docassemble**’s Markdown-to-HTML converter uses the [Markdown in HTML extension](https://python-markdown.github.io/extensions/md_in_html/)
. This means that by default, anything inside of an HTML tag, like `**Hello, world!**` will not be treated as [Markdown](https://daringfireball.net/projects/markdown/)
.
However, if you want text that is inside of HTML tags to be processed as Markdown, you can add attributes to your HTML tags to tell the Markdown-to-HTML converter to treat the content as [Markdown](https://daringfireball.net/projects/markdown/)
. If you write `**Hello, world!**` then the content of the paragraph will be translated as [Markdown](https://daringfireball.net/projects/markdown/)
.
For more information about how this works, see the documentation for the [Markdown in HTML extension](https://python-markdown.github.io/extensions/md_in_html/)
.
Using Mako for logic and generated text[¶](https://docassemble.org/docs/markup.html#mako)
==========================================================================================
**docassemble** uses a templating system called [Mako](https://www.makotemplates.org/)
to allow developers to insert variables and code into questions and documents.
You can insert the values of variables into question text using [Mako](https://www.makotemplates.org/)
’s `${ ... }` syntax.
mandatory: True
question: |
A summary
subquestion: |
You like ${ favorite_fruit }
and ${ favorite_vegetable }.
---
code: |
favorite_fruit = 'apples'
favorite_vegetable = 'potatoes'
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-01.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-01.yml&reset=1 "Click to try this interview")
You can use [Mako](https://www.makotemplates.org/)
’s `if/endif` syntax to insert text conditionally:
mandatory: True
question: |
Hello!
subquestion: |
I hope you are having a good day.
% if day_of_month == 1:
Don't forget to change your wall calendar!
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-02.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-02.yml&reset=1 "Click to try this interview")
You can also express more complicated logic:
mandatory: True
question: |
Commentary on the day of the month
subquestion: |
Let me tell you about today.
% if day_of_month < 3:
The month just started!
% elif day_of_month < 15:
It is the beginning part of
the month.
% else:
It is the latter part of the month.
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-03.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-03.yml&reset=1 "Click to try this interview")
The [Mako](https://www.makotemplates.org/)
syntax for if/then/else statements is based on [Python’s `if` statement](https://docs.python.org/3.12/tutorial/controlflow.html#if-statements)
, but is a little bit different.
The `%` at the beginning of the line signifies that you are doing something special with [Mako](https://www.makotemplates.org/)
.
[Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
itself does not use `endif` – it only uses indentation to designate where the if/then/else statement ends. [Mako](https://www.makotemplates.org/)
requires the use of `endif` because it does not see indentation.
In [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, `elif` is short for “else if.” In the example above, the if/then/else statement means:
> If the day of the month is less than three, write “The month just started!”, but otherwise if the day of the month is less than 15, write “It is the beginning part of the month.”; otherwise, write “It is the latter part of the month.”
As with [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, it is critical that you include `:` at the end of any line where you indicate a condition.
You can put `if/endif` statements inside of other `if/endif` statements:
mandatory: True
question: |
Commentary on the day of the month
subquestion: |
Let me tell you about today.
% if day_of_month < 3:
The month just started!
% elif day_of_month < 15:
It is the beginning part of
the month.
% else:
% if month_of_year == 12:
It is almost New Year's!
% else:
It is the latter part of the month.
% endif
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-04.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-04.yml&reset=1 "Click to try this interview")
In this example, the `% if`, `% else`, and `% endif` lines are indented, but they do not have to be. Since nested if/then/else statements can be hard to read, the indentation helps make the statement more readable. Note that the the actual text itself is not indented, even though the `%` lines are indented; this is because indentation means something in [Markdown](https://daringfireball.net/projects/markdown/)
. If you indent a line by four spaces, [Markdown](https://daringfireball.net/projects/markdown/)
will treat the line as a [code block](https://daringfireball.net/projects/markdown/syntax#precode)
, which might not be what you want.
[Mako](https://www.makotemplates.org/)
also allows you to work with lists of things using `% for` and `% endfor`:
mandatory: True
question: |
Foods I like
subquestion: |
% for food in ['plums', 'pears', 'peas']:
I like ${ food }.
% endfor
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-05.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-05.yml&reset=1 "Click to try this interview")
This is based on [Python’s `for` statement](https://docs.python.org/3.12/tutorial/controlflow.html#for-statements)
.
The `for` loop is useful for working with groups of [objects](https://docassemble.org/docs/objects.html)
:
modules:
- docassemble.base.legal
---
objects:
- witness: PartyList
---
mandatory: True
question: |
The ${ witness.as_noun() }
subquestion: |
% for person in witness:
${ person } is a witness.
% endfor
---
question: |
What is the name of the
${ ordinal(i) } witness?
fields:
- First Name: witness[i].name.first
- Last Name: witness[i].name.last
---
question: |
Are there any other witnesses?
yesno: witness.there_is_another
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-06.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-06.yml&reset=1 "Click to try this interview")
Within `for` loops, [Mako](https://www.makotemplates.org/)
provides a useful object called [`loop`](https://docs.makotemplates.org/en/latest/runtime.html#loop-context)
, which contains information about the current iteration of the loop.
mandatory: True
question: |
Foods I like
subquestion: |
% for food in ['apples', 'peaches', 'pears', 'plums', 'turnips', 'raspberries']:
% if loop.first:
First, I like ${ food }.
% elif loop.last:
Last but not least, I am a
big fan of ${ food }.
% elif loop.even:
I also like ${ food }.
% elif loop.odd:
The ${ ordinal(loop.index) } food
I like is ${ food }.
% endif
% endfor
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-09.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-09.yml&reset=1 "Click to try this interview")
Note that `loop.index` is a number in a range that starts with zero. The [`ordinal()`](https://docassemble.org/docs/functions.html#ordinal)
function converts these numbers to words.
For more information about working with groups of things, see [groups](https://docassemble.org/docs/groups.html)
.
In addition to allowing you to insert [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
expressions with the `${ ... }` syntax, [Mako](https://www.makotemplates.org/)
allows you to embed [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
statements using the `<%`/`%>` syntax:
mandatory: True
question: |
<%
a = 2
b = 3
the_answer = a + b
%>
The answer is ${ the_answer }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-07.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-07.yml&reset=1 "Click to try this interview")
[Mako](https://www.makotemplates.org/)
also allows you to insert special code that cuts short the text being rendered:
mandatory: True
question: |
Apples
subquestion: |
% if not likes_apples:
Oh well, never mind.
<% return STOP_RENDERING %>
% endif
Apples are red.
They can also be green.
They have stems and seeds.
They are juicy and sweet.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mako-08.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mako-08.yml&reset=1 "Click to try this interview")
The same thing could also be accomplished with an `else` statement, but using [`STOP_RENDERING`](https://docs.makotemplates.org/en/latest/syntax.html#exiting-early-from-a-template)
may be more readable.
For more information about [Mako](https://www.makotemplates.org/)
, see the [Mako documentation](https://docs.makotemplates.org/en/latest/index.html)
. Note, however, that not all features of [Mako](https://www.makotemplates.org/)
are available in **docassemble**. For example, in normal [Mako](https://www.makotemplates.org/)
, you can write:
% if some_variable is UNDEFINED:
...
% endif
In **docassemble**, this will not work as intended. Instead, you would use the [`defined()` function](https://docassemble.org/docs/functions.html#defined)
:
% if not defined('some_variable'):
...
% endif
If you want to use the [`<%def>`](https://docs.makotemplates.org/en/latest/defs.html#using-defs)
construct of [Mako](https://www.makotemplates.org/)
, see the [`def` initial block](https://docassemble.org/docs/initial.html#def)
.
Formatting variables[¶](https://docassemble.org/docs/markup.html#formatting)
-----------------------------------------------------------------------------
When the variable you insert with `${ ... }` is a number, the way that it is formatted may not be to your liking. There are a variety of ways to format numbers in [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
.
code: |
monthly_income = 43143.26/12
---
question: |
Your monthly income
subquestion: |
Your monthly income is
${ monthly_income }
dollars per month.
But it would be better to say
your monthly income is
${ '%.2f' % monthly_income }
dollars per month, or
${ '{:.2f}'.format(monthly_income) }
dollars per month, or
${ '{:,.2f}'.format(monthly_income) }
dollars per month, or
${ int(monthly_income) }
dollars per month, or
best of all,
${ currency(monthly_income) }
per month.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/number-formatting.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/number-formatting.yml&reset=1 "Click to try this interview")
Inserting images[¶](https://docassemble.org/docs/markup.html#inserting%20images)
=================================================================================
To insert an image that is located in the `static` folder of a custom Python package, use the `FILE` command. This works within PDF, RTF, and DOCX documents as well as within questions.
For example:
---
question: |
Did your attacker look like this?
subquestion: |
Please study the face below closely before answering.
[FILE docassemble.crimesolver:mugshot.jpg]
yesno: suspect_identified
This example presumes that there is a Python package called `docassemble.crimesolver` installed on the server, and there is a file `mugshot.jpg` located within the `static` directory inside that package.
If you omit the package name (e.g., `[FILE mugshot.jpg]`), **docassemble** will assume you are referring to a file located in the `static` directory of the package in which the question appears.
Optionally, you can set the width of the image:
[FILE docassemble.crimesolver:mugshot.jpg, 100%]
or:
[FILE docassemble.crimesolver:mugshot.jpg, 150px]
You can also set the [alt text](https://moz.com/learn/seo/alt-text)
of the image:
[FILE docassemble.crimesolver:mugshot.jpg, 150px, Mugshot photograph]
If you want to set the [alt text](https://moz.com/learn/seo/alt-text)
without setting a width, use `None` (with a capital N) as the width:
[FILE docassemble.crimesolver:mugshot.jpg, None, Mugshot photograph]
You can use any characters in the [alt text](https://moz.com/learn/seo/alt-text)
except for the right bracket. If you need to use the right bracket in [alt text](https://moz.com/learn/seo/alt-text)
, use one of the other methods of inserting images, such as creating a [`DAStaticFile`](https://docassemble.org/docs/objects.html#DAStaticFile)
object.
Instead of referring to a file name, you can refer to the name of an image that is defined in an [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
or [`images`](https://docassemble.org/docs/initial.html#images)
block.
images:
bills: money146.svg
children: children2.svg
---
mandatory: True
question: Do you have children?
subquestion: |
[FILE children, 100%]
yesno: has_children
To insert an image that has been uploaded, or created using a [signature field](https://docassemble.org/docs/fields.html#signature)
, simply refer to the variable using [Mako](https://www.makotemplates.org/)
. For example:
question: |
Please upload a picture of yourself.
fields:
- Picture: user_picture
datatype: file
---
question: |
You're so adorable, François!
subquestion: |
${ user_picture }
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/upload.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/upload.yml&reset=1 "Click to try this interview")
Alternatively, you can call the [`show()`](https://docassemble.org/docs/objects.html#DAFile.show)
method on the file object:
question: |
You're so adorable!
subquestion: |
${ user_picture.show() }
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/upload-show.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/upload-show.yml&reset=1 "Click to try this interview")
The [`show()`](https://docassemble.org/docs/objects.html#DAFile.show)
method takes an optional argument, `width`:
question: |
You're so adorable!
subquestion: |
${ user_picture.show(width='250px') }
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/upload-show-width.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/upload-show-width.yml&reset=1 "Click to try this interview")
In the above example, the picture will be shrunk or expanded so that its width is 250 pixels.
Inserting inline icons[¶](https://docassemble.org/docs/markup.html#emoji)
==========================================================================
If you have defined “decorations” in an [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
block (see [initial blocks](https://docassemble.org/docs/initial.html)
), you can include these decorations as icons (having the same size as the text) by referencing them “emoji-style,” putting colons around the decoration name. This works not only in `question` and [`subquestion`](https://docassemble.org/docs/questions.html#subquestion)
areas, but also in question choices.
This works within PDF and RTF documents as well as within questions.
image sets:
freepik:
attribution: |
Icon made by [Freepik](http://www.flaticon.com/authors/freepik)
images:
male: male244.svg
female: female243.svg
---
question: |
What is your gender?
field: user.gender
choices:
- "Male :male:": male
- "Female :female:": female
- "Other": other
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/emoji-inline.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/emoji-inline.yml&reset=1 "Click to try this interview")
By default, if an “emoji-style” reference refers to an image that has not been defined in an [`image sets`](https://docassemble.org/docs/initial.html#image%20sets)
or [`images`](https://docassemble.org/docs/initial.html#images)
block, the reference will be treated as a reference to a [Font Awesome](https://fontawesome.com/)
icon.
mandatory: True
decoration: chart-bar
question: |
Third quarter metrics
subquestion: |
We are making more money
:far-fa-money-bill-alt: than
we did in the second quarter.
So you can sleep well tonight! :bed:
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/font-awesome.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/font-awesome.yml&reset=1 "Click to try this interview")
As explained in the [Configuration](https://docassemble.org/docs/config.html)
, only one “style” of [Font Awesome](https://fontawesome.com/)
icon (by default, the “solid” style) can be used at one time. If you need to use a different “style” for a particular icon, you need to specify the CSS classes more explicitly. For example, you can write `:fab-fa-amazon:` to get the `amazon` icon in the “brand” style (`fab`).
If you want to apply additional formatting to an icon, you can include the raw [HTML](https://en.wikipedia.org/wiki/HTML)
for the icon. For example:
---
question: |
Social media usage
subquestion: |
Do you use ?
yesno: user_is_on_facebook
---
Note that while ordinary inline icon references work in documents as well as on the web, [Font Awesome](https://fontawesome.com/)
references only work in questions, not in documents.
You can turn off inline icon conversion for a block of text by writing `[NO_EMOJIS]` somewhere in the text.
mandatory: True
question: |
Third quarter metrics
subquestion: |
[NO_EMOJIS]
We are making more money
:far-fa-money-bill-alt: than
we did in the second quarter.
So you can sleep well tonight! :bed:
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/no-emojis.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/no-emojis.yml&reset=1 "Click to try this interview")
Inserting audio and video[¶](https://docassemble.org/docs/markup.html#audio%20and%20video)
===========================================================================================
In addition to using the [`audio`](https://docassemble.org/docs/modifiers.html#audio)
and [`video`](https://docassemble.org/docs/modifiers.html#video)
[question modifiers](https://docassemble.org/docs/modifiers.html)
, you can insert audio and video into your [Mako](https://www.makotemplates.org/)
text in questions.
question: Upload an audio file.
fields:
- no label: my_file
datatype: microphone
---
mandatory: True
question: Listen to this!
subquestion: |
Best song ever:
${ my_file }
Don't you think so?
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/audio-upload.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/audio-upload.yml&reset=1 "Click to try this interview")
Or, if you have a file in `data/static`, you can write:
---
question: Listen to this!
subquestion: |
This excerpt of whalesong will give you goosebumps.
[FILE whale_song.mp3]
---
It works the same with videos.
---
question: Watch this!
subquestion: |
This video of otters sunbathing is going to go viral.
[FILE awesome_otters.mp4]
---
You can also embed [YouTube](https://www.youtube.com/)
and [Vimeo](https://vimeo.com/)
videos (which is far preferable to working with video files, which are enormous). For example, if you want to embed a [YouTube](https://www.youtube.com/)
video for which the URL is `https://www.youtube.com/watch?v=RpgYyuLt7Dx` or `https://youtu.be/RpgYyuLt7Dx`, you would write this:
---
question: Are you traveling to New York City?
yesno: going_to_nyc
video: |
New York is such a happening place. Check it out:
[YOUTUBE RpgYyuLt7Dx]
---
See [question modifiers](https://docassemble.org/docs/modifiers.html)
for more information about including audio and video.
Inserting QR codes[¶](https://docassemble.org/docs/markup.html#qr)
===================================================================
You can also display or insert QR codes using `[QR ...]`, where `...` is the text you want to encode. This works like `[FILE ...]` in that you can give the image a width and [alt text](https://moz.com/learn/seo/alt-text)
. The QR code images can be displayed on the screen or inserted into a document.
This works within PDF and RTF documents as well as within questions.
For example, this interview provides a QR code that directs the user to [Google News](https://news.google.com/)
:
mandatory: True
question: Here is a URL for you in a QR code
subquestion: |
[QR https://news.google.com, 200px, Google News]
attachment:
name: Your QR code
filename: your_code
content: |
Use the QR reader on your smartphone to take a picture of this:
[QR https://news.google.com]
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/qr-code-demo.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/qr-code-demo.yml&reset=1 "Click to try this interview")
See also the [`qr_code()`](https://docassemble.org/docs/functions.html#qr_code)
function, which allows you to insert the `[QR ...]` markup using [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
.
Inserting other types of files[¶](https://docassemble.org/docs/markup.html#inserting%20other)
==============================================================================================
Just as you can insert images with `[FILE docassemble.crimesolver:mugshot.jpg]` or `${ user_picture }`, you can also insert other types of files.
If you insert a text file (MIME type `text/plain`), the raw contents of the file will be inserted.
If you insert a [Markdown](https://daringfireball.net/projects/markdown/)
file (MIME type `text/markdown`), the contents of the file will be treated as a [`DATemplate`](https://docassemble.org/docs/objects.html#DATemplate)
.
The behavior when you insert a PDF file depends on the context:
* In a `question`, the user will see a thumbnail of the first page of the document, and clicking the thumbnail will open the PDF file.
* In a [document](https://docassemble.org/docs/documents.html)
created by converting [Markdown](https://daringfireball.net/projects/markdown/)
to PDF, the PDF pages will be inserted into the document.
* When assembling documents in other formats, the pages of the PDF will be converted to images and inserted into the document in the same way images are inserted.
However, PDF thumbnail conversion does not work with static PDF files; it only works with generated or uploaded PDF files.
When you insert a word processing file, the file will be converted to PDF and inserted into the document the way a PDF file is inserted. However, if you include a DOCX file inside a DOCX file created using [`docx template file`](https://docassemble.org/docs/documents.html#docx%20template%20file)
, the result is like that of calling [`include_docx_template()`](https://docassemble.org/docs/functions.html#include_docx_template)
.
Inserting tables[¶](https://docassemble.org/docs/markup.html#tables)
=====================================================================
Tables can be inserted in the format known as [PHP Markdown Extra](https://michelf.ca/projects/php-markdown/extra/#table)
.
mandatory: true
question: |
Your fruit inventory
subquestion: |
This describes your fruit
collection.
Fruit | How many
-------|---------
Apple | 4
Orange | 3
Pear | 6
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-markdown.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-markdown.yml&reset=1 "Click to try this interview")
If you want to construct a table based on information in a list, the best practice is to collect the list information into an [object](https://docassemble.org/docs/objects.html)
and then use the [`table` block](https://docassemble.org/docs/initial.html#table)
to create a template for the table.
If you want to write tables in [Markdown](https://daringfireball.net/projects/markdown/)
manually, note that the alignment characters do not have do be perfectly aligned from row to row.
mandatory: true
question: |
Your vegetable inventory
subquestion: |
This describes your vegetable
collection.
Vegetable|How many
------|----
Potato|4
Brocolli|3
Beet|6
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-markdown-unaligned.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-markdown-unaligned.yml&reset=1 "Click to try this interview")
Under the [Markdown](https://daringfireball.net/projects/markdown/)
rules, the text for each row needs to be all on the same line in your [Markdown](https://daringfireball.net/projects/markdown/)
text. If you want to include a line break within a cell, use the `[BR]` tag, which is documented in the [document markup](https://docassemble.org/docs/documents.html#markup)
section.
Exactly how your text is converted from [Markdown](https://daringfireball.net/projects/markdown/)
into an actual table depends on the output format. If you are including a table that is viewed on the screen, see [tables in HTML](https://python-markdown.github.io/extensions/tables/)
for the details. If you are including a table that is inserted into an attachment, see [tables in attachments](https://pandoc.org/MANUAL.html#extension-pipe_tables)
.
If you want to have fine-grained control over the formatting of tables, [Markdown](https://daringfireball.net/projects/markdown/)
will disappoint you. If you want a very specific type of table, you can use [raw HTML](https://www.w3schools.com/html/html_tables.asp)
for a table that displays in a question or [raw LaTeX](https://en.wikibooks.org/wiki/LaTeX/Tables)
for a table that displays in a PDF-only [`attachment`](https://docassemble.org/docs/documents.html#attachment)
.
The [PHP Markdown Extra](https://michelf.ca/projects/php-markdown/extra/#table)
format _requires_ that you include a header in your table, even if you do not want one. You can try to make the header row blank with the following trick.
mandatory: true
question: |
Your vegetable inventory
subquestion: |
This describes your vegetable
collection.
|
---------|------
Potato |4
Brocolli |3
Beet |6
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-markdown-noheader.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-markdown-noheader.yml&reset=1 "Click to try this interview")
The styling of tables converted from [Markdown](https://daringfireball.net/projects/markdown/)
to [HTML](https://en.wikipedia.org/wiki/HTML)
can be customized using [`table css class`](https://docassemble.org/docs/questions.html#table%20css%20class)
.
When using [tables in HTML](https://python-markdown.github.io/extensions/tables/)
, text in each cell is aligned left by default. Although [PHP Markdown Extra](https://michelf.ca/projects/php-markdown/extra/#table)
has a feature for changing the alignment of columns using the `:` character in the header separation line, this feature is not supported when inserting [tables in HTML](https://python-markdown.github.io/extensions/tables/)
. Instead, you can change the CSS class of each cell individually using the following markup.
mandatory: true
question: |
Your vegetable inventory
subquestion: |
This describes your vegetable collection.
Vegetable { .text-center } | How many { .text-center }
---------------------------|--------------------------
Potato | 4 { .text-end }
Brocolli | 3 { .text-end }
Beet | 6 { .text-end }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-markdown-class.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-markdown-class.yml&reset=1 "Click to try this interview")
The CSS classes `text-center` and `text-end` come from [Bootstrap 5](https://getbootstrap.com/docs/5.2/utilities/text/#text-alignment)
.
If you want a simple two-column table that fills the width of the page, note that there are special [document markup](https://docassemble.org/docs/documents.html#markup)
tags for this special case: you can write `[BEGIN_TWOCOL]` (text of first column) `[BREAK]` (text of second column) `[END_TWOCOL]`.
Embedding fields[¶](https://docassemble.org/docs/markup.html#field)
====================================================================
In a [`fields`](https://docassemble.org/docs/fields.html#fields)
block, you can use the markup syntax `[FIELD ...]` to embed fields within the [`subquestion`](https://docassemble.org/docs/questions.html#subquestion)
text. For more information about this feature, see the section on [Embedding fields within a paragraph](https://docassemble.org/docs/fields.html#embed)
.
Embedding areas for interim information[¶](https://docassemble.org/docs/markup.html#target)
============================================================================================
If you include the markup `[TARGET ...]` within text, you will create an invisible area where text can be placed by [`code`](https://docassemble.org/docs/code.html)
. For more information about this feature, see the section on [Processing interim user input](https://docassemble.org/docs/background.html#target)
.
* * *
---
# External data
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/external.md "Edit this page on GitHub")
External data
=============
Contents
--------
* [1 URL arguments](https://docassemble.org/docs/external.html#url_args)
* [2 Using the “actions” system](https://docassemble.org/docs/external.html#actions)
* [2.1 Pushing information into a session](https://docassemble.org/docs/external.html#push)
* [2.2 Pulling information out of a session](https://docassemble.org/docs/external.html#pull)
* [3 Using the API](https://docassemble.org/docs/external.html#api)
* [4 E-mail](https://docassemble.org/docs/external.html#email%20from)
* [4.1 Sending e-mail from a session](https://docassemble.org/docs/external.html#email%20from)
* [4.2 Sending e-mail to a session](https://docassemble.org/docs/external.html#email%20to)
* [5 Sending text messages from a session](https://docassemble.org/docs/external.html#sms)
* [6 Persisting data](https://docassemble.org/docs/external.html#persist%20data)
* [6.1 Persistent interview sessions](https://docassemble.org/docs/external.html#persist%20sessions)
* [6.2 Persistent files](https://docassemble.org/docs/external.html#persist%20files)
* [6.3 Saving to SQL](https://docassemble.org/docs/external.html#sql)
* [6.4 Saving to Redis](https://docassemble.org/docs/external.html#redis)
* [7 Using third-party APIs](https://docassemble.org/docs/external.html#third%20party%20api)
* [8 Sharing information across sessions](https://docassemble.org/docs/external.html#across%20sessions)
There are a variety of ways that you can move information into and out of interview sessions, besides obtaining information from the user.
URL arguments[¶](https://docassemble.org/docs/external.html#url_args)
======================================================================
As explained in the [invocation](https://docassemble.org/docs/interviews.html#invocation)
section, when a user starts an interview, the user visits a URL. This URL can contains parameters of your own choosing (as long as the names do not conflict with any of the [reserved URL parameters](https://docassemble.org/docs/special.html#reserved%20url%20parameters)
). Data passed using these parameters is available inside the interview session using the [`url_args` special variable](https://docassemble.org/docs/special.html#url_args)
.
You can also change the [`url_args`](https://docassemble.org/docs/special.html#url_args)
during an active interview session. If the user’s browser is logged into a session, and the user clicks on a hyperlink to the same interview with URL parameters set, the [`url_args`](https://docassemble.org/docs/special.html#url_args)
will be updated.
Using the “actions” system[¶](https://docassemble.org/docs/external.html#actions)
==================================================================================
The normal flow of an interview in **docassemble** is as follows:
1. The screen loads, and the user sees whatever [`question`](https://docassemble.org/docs/questions.html#question)
is the next step in the [interview logic](https://docassemble.org/docs/logic.html)
, given the current state of the interview answers.
2. The user enters some information (if the screen asks for information) and then presses a button.
3. The interview answers are updated with the new information.
4. Go back to step 1.
Eventually, the interview will reach a logical endpoint. This flow makes sense for the main path of the interview, but sometimes the user needs to deviate from the main path. For example, they might want to adjust their answers to previous questions.
In **docassemble**, “actions” are used to trigger deviations from the main path of the [interview logic](https://docassemble.org/docs/logic.html)
. For example, suppose that under the main path of the [interview logic](https://docassemble.org/docs/logic.html)
, the next piece of information that is necessary to gather is `favorite_legume`. The interview will show a [`question`](https://docassemble.org/docs/questions.html#question)
that offers to define `favorite_legume`. But suppose you want the user to be able to go back to the `favorite_vegetable` question. (Perhaps a common mistake is for users to list a legume as their favorite vegetable). You can allow the user to launch an “action” that causes the interview to seek (or in this case, re-seeks) the variable `favorite_vegetable` instead of the variable `favorite_legume`.
question: |
What is your favorite legume?
subquestion: |
You said your
[favorite vegetable](${ url_action('favorite_vegetable') })
was
${ favorite_vegetable }.
fields:
Legume: favorite_legume
For more information about “actions,” see the documentation for [`url_action()`](https://docassemble.org/docs/functions.html#url_action)
and [`url_ask()`](https://docassemble.org/docs/functions.html#url_ask)
.
Another type of deviation from the main interview logic is a [background action](https://docassemble.org/docs/background.html#background)
. This is where some code runs on the server, in the background, where the user can’t see anything. This is typically used for code that takes a long time to run, where you don’t want the user to have to wait for the result, or there is a danger that the user’s browser will time out if the server does not respond quickly enough.
Pushing information into a session[¶](https://docassemble.org/docs/external.html#push)
---------------------------------------------------------------------------------------
Typically, users launch actions by clicking hyperlinks within the **docassemble** application. However, it is also possible to click hyperlinks outside of the application that run actions inside the session.
For more information about this feature, see the [`interview_url_action()`](https://docassemble.org/docs/functions.html#interview_url_action)
function. This function creates a URL that embeds the session ID.
Pulling information out of a session[¶](https://docassemble.org/docs/external.html#pull)
-----------------------------------------------------------------------------------------
The [`interview_url_action()`](https://docassemble.org/docs/functions.html#interview_url_action)
function also allows the extraction of information from an interview. You can call [`interview_url_action()`](https://docassemble.org/docs/functions.html#interview_url_action)
with a reference to an [`event`](https://docassemble.org/docs/fields.html#event)
that runs [`json_response()`](https://docassemble.org/docs/functions.html#json_response)
to return selected information in a machine-readable format. This allows you to create a customizable “API” for your interview.
objects:
- user: Individual
---
mandatory: True
code: |
multi_user = True
---
event: query_fruit
code: |
data = {'fruit': favorite_fruit, 'pieces': number_of_pieces}
json_response(data)
---
mandatory: True
question: |
You currently have
${ nice_number(number_of_pieces) }
${ noun_plural('piece', number_of_pieces) }
of
${ noun_singular(favorite_fruit) }.
subquestion: |
Use
[this link](${ interview_url_action('query_fruit') })
to query the information from
another application.
You can also change the
[fruit](${ url_action('favorite_fruit') })
and the
[number of pieces](${ url_action('number_of_pieces') }).
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/response-json.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/response-json.yml&reset=1 "Click to try this interview")
If you go through the interview and obtain the URL, you can try loading it in a different browser to verify that another application (not having the same browser cookies) can access the information in [JSON](https://en.wikipedia.org/wiki/JSON)
format.
Using the API[¶](https://docassemble.org/docs/external.html#api)
=================================================================
You can also manipulate interview sessions using the **docassemble** [API](https://docassemble.org/docs/api.html)
.
E-mail[¶](https://docassemble.org/docs/external.html#email%20from)
===================================================================
Sending e-mail from a session[¶](https://docassemble.org/docs/external.html#email%20from)
------------------------------------------------------------------------------------------
You can use the [`send_email()`](https://docassemble.org/docs/functions.html#send_email)
function to send e-mails from an interview session to the outside world. You will first need to [configure your server](https://docassemble.org/docs/config.html#mail)
to send e-mail.
Sending e-mail to a session[¶](https://docassemble.org/docs/external.html#email%20to)
--------------------------------------------------------------------------------------
If you are using [Docker](https://docassemble.org/docs/docker.html)
to deploy your server, and you have [configured your server to receive e-mail](https://docassemble.org/docs/config.html#setup_email)
, you can use **docassemble**’s e-mail-to-session feature.
This involves generating a special e-mail address using [`interview_email()`](https://docassemble.org/docs/functions.html#interview_email)
. Any e-mails sent to that address and received by the server will be processed and made available in the interview session for retrieval using [`get_emails()`](https://docassemble.org/docs/functions.html#get_emails)
.
Sending text messages from a session[¶](https://docassemble.org/docs/external.html#sms)
========================================================================================
You can use the [`send_sms()`](https://docassemble.org/docs/functions.html#send_sms)
function to send text messages from an interview session to the outside world. You will first need to [configure your server](https://docassemble.org/docs/config.html#twilio)
to send text messages. Despite the function name (“SMS”), this function can be used to send messages through [Twilio](https://twilio.com/)
’s [WhatsApp API](https://www.twilio.com/whatsapp)
.
Persisting data[¶](https://docassemble.org/docs/external.html#persist%20data)
==============================================================================
Persistent interview sessions[¶](https://docassemble.org/docs/external.html#persist%20sessions)
------------------------------------------------------------------------------------------------
The interview answers in an interview session are stored in encrypted form inside of rows in a [SQL](https://en.wikipedia.org/wiki/SQL)
table. Documents are stored on the server or in a cloud storage system. If a user who is not logged in completes an interview, they will not be able to access their interview session again after closing their browser, because the encryption key will be lost. However, if they log in, their interview session will be tied to their account, and their password will become the decryption key for the session.
The encryption of interview answers makes it impossible for someone other than the original user to access the data in the interview session, unless the decryption key (the user’s password) is known. However, if the interview sets [`multi_user`](https://docassemble.org/docs/special.html#multi_user)
to `False`, then functions like [`interview_url_action()`](https://docassemble.org/docs/functions.html#interview_url_action)
can be used to access the data in the interview as long as the interview session exists.
By default, interview sessions are deleted after 90 days of inactivity. This feature can be modified or turned off using the [`interview delete days`](https://docassemble.org/docs/config.html#interview%20delete%20days)
configuration directive.
Persistent files[¶](https://docassemble.org/docs/external.html#persist%20files)
--------------------------------------------------------------------------------
When an interview session is deleted, the files associated with the interview session are also deleted.
If you want a file to continue exist after its associated interview session has been deleted, you can use the [`.set_attributes()`](https://docassemble.org/docs/objects.html#DAFile.set_attributes)
method of the [`DAFile`](https://docassemble.org/docs/objects.html#DAFile)
object in order to indicate that the file should not be deleted when the interview session is deleted.
Saving to SQL[¶](https://docassemble.org/docs/external.html#sql)
-----------------------------------------------------------------
A session’s interview answers are stored in a [SQL](https://en.wikipedia.org/wiki/SQL)
server, but not in a way that is easily accessible across interview sessions. Interview sessions are not persistent; the user can delete a session, and a session may be deleted due to inactivity (unless the [`interview delete days`](https://docassemble.org/docs/config.html#interview%20delete%20days)
configuration directive is set to disable automatic deletion).
If you want to save information in [SQL](https://en.wikipedia.org/wiki/SQL)
in a way that will persist indefinitely and that will not be encrypted, you can use the [`store_variables_snapshot()`](https://docassemble.org/docs/functions.html#store_variables_snapshot)
function to store the interview answers to a special [SQL](https://en.wikipedia.org/wiki/SQL)
table in a [JSON](https://en.wikipedia.org/wiki/JSON)
format that allows you to write [SQL](https://en.wikipedia.org/wiki/SQL)
queries that access individual variables inside the data structure. You can also use [`write_record()`](https://docassemble.org/docs/functions.html#write_record)
, [`delete_record()`](https://docassemble.org/docs/functions.html#delete_record)
and [`read_records()`](https://docassemble.org/docs/functions.html#read_records)
functions to store data (including Python objects) in [SQL](https://en.wikipedia.org/wiki/SQL)
records. These methods do not preserve server-side encryption, however.
You can save information in encrypted form in [SQL](https://en.wikipedia.org/wiki/SQL)
in a [Redis](https://redis.io/)
\-like fashion using a [`DAStore`](https://docassemble.org/docs/objects.html#DAStore)
object.
It is also possible for [`DAObject`](https://docassemble.org/docs/objects.html#DAObject)
s to “mirror” rows in a [SQL](https://en.wikipedia.org/wiki/SQL)
database. To do this, you need to write custom classes that are subclasses of [`DAObject`](https://docassemble.org/docs/objects.html#DAObject)
and the special object [`SQLObject`](https://docassemble.org/docs/objects.html#SQLObject)
. For more information, see the documentation for [`SQLObject`](https://docassemble.org/docs/objects.html#SQLObject)
.
Saving to Redis[¶](https://docassemble.org/docs/external.html#redis)
---------------------------------------------------------------------
Instead of saving to the [SQL](https://en.wikipedia.org/wiki/SQL)
database, you can write persistent data to [Redis](https://redis.io/)
using an instance of the [`DARedis`](https://docassemble.org/docs/objects.html#DARedis)
object.
Using third-party APIs[¶](https://docassemble.org/docs/external.html#third%20party%20api)
==========================================================================================
Your interview can also communicate with the outside world using any [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
module that provides the functionality you want.
Here is a simple example that calls the [World Clock API](http://worldclockapi.com/)
to obtain the current time (which you don’t really need an API to do).
The API call is in a [Python module](https://docs.python.org/3/tutorial/modules.html)
called [`gettime.py`](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/gettime.py)
in the [`docassemble.demo`](https://github.com/jhpyle/docassemble/tree/master/docassemble_demo/docassemble/demo)
package. The contents of this file are:
import requests
def get_time():
r = requests.get('http://worldclockapi.com/api/json/est/now')
if r.status_code != 200:
raise Exception("Could not obtain the time")
return r.json()['currentDateTime']
The [World Clock API](http://worldclockapi.com/)
returns output in [JSON](https://en.wikipedia.org/wiki/JSON)
format, such as:
{"$id":"1","currentDateTime":"2019-03-04T22:43-05:00","utcOffset":"-05:00:00","isDayLightSavingsTime":false,"dayOfTheWeek":"Monday","timeZoneName":"Eastern Standard Time","currentFileTime":131962129911877536,"ordinalDate":"2019-63","serviceResponse":null}
The `get_time()` function uses the [`requests`](https://requests.readthedocs.io/en/master/)
library. The variable `r` represents the response of the [World Clock API](http://worldclockapi.com/)
’s server to the attempt to your code’s attempt to obtain the contents of the given URL. This object has a handy method `.json()` that converts the output of the request to a data structure, assuming that the request returns [JSON](https://en.wikipedia.org/wiki/JSON)
. The `get_time()` function returns the `currentDateTime` part of the [World Clock API](http://worldclockapi.com/)
’s response.
Here is an interview that calls the `get_time()` function:
modules:
- docassemble.demo.gettime
---
mandatory: True
question: |
The time
subquestion: |
According to the World Clock API, the
current time in the Eastern time zone is
${ get_time() }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/get-time.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/get-time.yml&reset=1 "Click to try this interview")
For more complex examples, see the [sample interviews in the documentation](https://docassemble.org/docs/functions.html#google%20sheets%20example)
that read data from a [Google Sheet](https://sheets.google.com/)
and write data to a [Google Sheet](https://sheets.google.com/)
.
Sharing information across sessions[¶](https://docassemble.org/docs/external.html#across%20sessions)
=====================================================================================================
Users’ answers to questions in an interview are stored in the “interview answers,” which is a pickled Python dictionary stored on the [SQL](https://en.wikipedia.org/wiki/SQL)
server. Each set of “interview answers” is specific to a particular interview (e.g., `docassemble.demo:data/questions/questions.yml`) and specific to a particular session in that interview (e.g., session ID `BoJSwJHppIPFYtFEOeBU1y4koaM3L9zP`).
Every time the user proceeds from one screen to another, the interview answers are changed, the interview logic is evaluated (which may change the interview answers further) and a new set of interview answers is saved. The user can incrementally “undo” changes to the interview answers by pressing the “back” button, which restores an earlier saved state of the interview answers.
**docassemble**’s interview logic system is based on this system of the “interview answers.” If the interview logic calls for a value of a variable that is undefined, **docassemble** will ask a `question` or run `code` to obtain a definition of that variable. **docassemble** asks for information that is missing in the interview answers.
By default, the interview answers are encrypted on the server and can only be accessed when the user provides the decryption key via a web browser cookie. This gives the user control over their information.
The way that **docassemble** works with interview answers is very different from the way that a typical web-based database works with data. A web-based database will typically use a [SQL](https://en.wikipedia.org/wiki/SQL)
backend, where the front end allows the user to populate fields of data records, where the fields are columns and the data records are rows in [SQL](https://en.wikipedia.org/wiki/SQL)
tables. These applications are known as “[CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
” applications, because the front end facilitates creating, retrieving, updating, and deleting rows in tables. In **docassemble**, by contrast, the interview answers are not values in two-dimensional data tables, but rather Python data structures that at any point in time may be only be partially populated.
If you want to share information across sessions, you can do so, but the methods for doing so are not going to be the same as the methods for accessing data in a [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
application.
There are multiple ways that you can share information across sessions:
* Push data: from session A, you can populate variables in session B by calling [`set_session_variables()`](https://docassemble.org/docs/functions.html#set_session_variables)
. If session B does not exist yet, you can first initiate it by calling [`create_session()`](https://docassemble.org/docs/functions.html#create_session)
.
* Pull data: from session B, you can retrieve variables from session A by calling [`get_session_variables()`](https://docassemble.org/docs/functions.html#get_session_variables)
.
* Initialize variables from values stored in a common storage area: from session A, you can use a [`DAStore`](https://docassemble.org/docs/objects.html#DAStore)
object to save data to a storage area that is specific to the user but can be accessed from any session. In session B, you can write `code` blocks that initialize variables to values that are stored in that storage area, if the storage area exists. By default, data are encrypted using the same encryption system as interview answers, and encryption can be turned off.
* Share global variables across sessions: in session A, you can use a [`DAGlobal`](https://docassemble.org/docs/objects.html#DAGlobal)
object to store the part of the interview answers you want to share. In session B, you can use a [`DAGlobal`](https://docassemble.org/docs/objects.html#DAGlobal)
object with the same “base” and “key,” and the attributes of the object in both sessions will be stored in a common storage area rather than in the interview answers of either session. Encryption is not available with this option.
* Share data using [Redis](https://redis.io/)
: [`DARedis`](https://docassemble.org/docs/objects.html#DARedis)
can also be used as a storage area. From session A, you can use the [`.set_data()`](https://docassemble.org/docs/objects.html#DARedis.set_data)
method of [`DARedis`](https://docassemble.org/docs/objects.html#DARedis)
to save a Python data structure to [Redis](https://redis.io/)
, and then inside of session B, you can call the [`.get_data()`](https://docassemble.org/docs/objects.html#DARedis.get_data)
method of [`DARedis`](https://docassemble.org/docs/objects.html#DARedis)
to retrieve the data structure. [Redis](https://redis.io/)
is a fast, in-memory data storage area, so it is best used with information that only needs to be stored temporarily. Encryption is not available.
* Share data in a list of records: the [`write_record()`](https://docassemble.org/docs/functions.html#write_record)
and [`read_records()`](https://docassemble.org/docs/functions.html#read_records)
functions are useful for storing lists of data records, where each list is identified by a key that is global to the server. Inside of session A, you can call [`write_record()`](https://docassemble.org/docs/functions.html#write_record)
to store a row in a list. Inside of session B, you can call [`read_records()`](https://docassemble.org/docs/functions.html#read_records)
to retrieve all of the records in that list. Encryption is not available.
* Sync data to a SQL database: if you would like to use a traditional [SQL](https://en.wikipedia.org/wiki/SQL)
database as a “single source of truth” for variables in your interview, you can use the [`SQLObject`](https://docassemble.org/docs/objects.html#SQLObject)
system. This allows you to “sync” variables in your interview answers with a [SQL](https://en.wikipedia.org/wiki/SQL)
database. Encryption is not supported.
* Stash data temporarily in an encrypted data store: from session A, you can call [`stash_data()`](https://docassemble.org/docs/functions.html#stash_data)
to store particular interview answers in an encrypted location. [`stash_data()`](https://docassemble.org/docs/functions.html#stash_data)
returns tokens that can be used to access the stashed data. Session A can offer the user URLs to other interviews, in which the tokens are URL parameters. If the user clicks one of these URLs and starts session B, the interview logic in session B can get look for the tokens in `url_args` and call [`retrieve_stashed_data()`](https://docassemble.org/docs/functions.html#retrieve_stashed_data)
to retrieve the interview answers that had been stored by session A. The stored data can be deleted once they are retrieved, and can automatically expire after a period of time. This is a secure way to pass data from one session to another. It avoids storing human-readable information in the URL parameters when passing data from one session to a prospective session. It avoids the unnecessary creation of a new session if the user does not want to proceed to start a new session. The encryption system is independent of the encryption system for interview answers, so that knowledge of the tokens does not lead to knowledge of any interview answers. There are [API interfaces](https://docassemble.org/docs/api.html#stash_data)
for stashing data and retrieving stashed data.
* * *
---
# Interview logic
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/logic.md "Edit this page on GitHub")
Interview logic
===============
Contents
--------
* [1 Introduction](https://docassemble.org/docs/logic.html#intro)
* [1.1 Simple interviews: all blocks mandatory](https://docassemble.org/docs/logic.html#intro_mandatory)
* [1.2 Complex interviews: dependency satisfaction](https://docassemble.org/docs/logic.html#intro_dependency)
* [1.3 Manually specifying the order of questions](https://docassemble.org/docs/logic.html#intro_manual_order)
* [1.4 Writing law as code to drive the interview](https://docassemble.org/docs/logic.html#legal_logic)
* [2 How rules determine interview process](https://docassemble.org/docs/logic.html#interview_logic)
* [2.1 Beware of non-idempotency](https://docassemble.org/docs/logic.html#idempotency)
* [3 The logical order of an interview](https://docassemble.org/docs/logic.html#order)
* [3.1 Overriding one question with another](https://docassemble.org/docs/logic.html#overriding)
* [3.2 Fallback questions](https://docassemble.org/docs/logic.html#fallback)
* [4 How docassemble runs your code](https://docassemble.org/docs/logic.html#howitworks)
* [5 How docassemble finds questions for variables](https://docassemble.org/docs/logic.html#variablesearching)
* [6 Specifiers that control interview logic](https://docassemble.org/docs/logic.html#specifiers)
* [6.1 mandatory](https://docassemble.org/docs/logic.html#mandatory)
* [6.2 initial](https://docassemble.org/docs/logic.html#initial)
* [6.3 need](https://docassemble.org/docs/logic.html#need)
* [6.4 depends on](https://docassemble.org/docs/logic.html#depends%20on)
* [6.5 reconsider](https://docassemble.org/docs/logic.html#reconsider)
* [6.6 undefine](https://docassemble.org/docs/logic.html#undefine)
* [7 Combining multiple interviews into one](https://docassemble.org/docs/logic.html#multiple%20interviews)
* [7.1 Using an umbrella YAML file](https://docassemble.org/docs/logic.html#multiple%20interviews%20umbrella)
* [7.2 Using hyperlinks](https://docassemble.org/docs/logic.html#multiple%20interviews%20links)
* [7.3 A/B testing with redirects](https://docassemble.org/docs/logic.html#multiple%20interviews%20redirect)
* [7.4 Using multiple endpoints in a single interview](https://docassemble.org/docs/logic.html#subinterview)
* [8 Best practices for interview logic and organization](https://docassemble.org/docs/logic.html#bplogic)
* [9 Best practices for sharing with others](https://docassemble.org/docs/logic.html#bpsharing)
Introduction[¶](https://docassemble.org/docs/logic.html#intro)
===============================================================
Unlike other guided interview systems, in which the interview developer maps out a decision tree or flowchart to indicate which questions should be asked and in which order, **docassemble** figures out what questions to ask and when to ask them based on rules that you specify. You specify these rules using [YAML](https://en.wikipedia.org/wiki/YAML)
blocks.
Simple interviews: all blocks mandatory[¶](https://docassemble.org/docs/logic.html#intro_mandatory)
----------------------------------------------------------------------------------------------------
The simplest type of rule you can specify is marking a block as [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
.
mandatory: True
question: |
Welcome to the interview!
continue button field: intro
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
---
mandatory: True
question: |
What is your favorite vegetable?
fields:
- Vegetable: favorite_vegetable
---
mandatory: True
question: |
Here is your document.
attachment:
content: |
Your favorite fruit is ${ favorite_fruit }.
% if favorite_fruit == 'apple':
You will never need to see a doctor.
% endif
Your favorite vegetable is ${ favorite_vegetable }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/all-mandatory.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/all-mandatory.yml&reset=1 "Click to try this interview")
When **docassemble** runs an interview, it looks at the [YAML](https://en.wikipedia.org/wiki/YAML)
and tries to run each block that is marked as “mandatory.” It will run them in the order in which they appear in the [YAML](https://en.wikipedia.org/wiki/YAML)
. In this example, first the “Welcome to the interview!” [`question`](https://docassemble.org/docs/questions.html#question)
is asked. When the user clicks the “Continue” button, **docassemble** moves on to the second [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block, which asks “What is your favorite fruit?” When that question is answered, **docassemble** asks “What is your favorite vegetable?” When that question is answered **docassemble** moves on to the final [`question`](https://docassemble.org/docs/questions.html#question)
, “Here is your document,” which lets the user download a document. This is a very simple interview because there is no branching logic.
Suppose that instead of asking for the user’s favorite vegetable, you wanted to ask for the user’s favorite apple, but only if the user said that their favorite fruit is “apple.” In the previous interview, we set [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
to `True` every time, but we can actually set [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
to a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
that evaluates to a true or false value. For example:
mandatory: True
question: |
Welcome to the interview!
continue button field: intro
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
---
mandatory: favorite_fruit == 'apple'
question: |
What is your favorite type of apple?
fields:
- Type of apple: favorite_apple
---
mandatory: True
question: |
Here is your document.
attachment:
content: |
Your favorite fruit is ${ favorite_fruit }.
% if favorite_fruit == 'apple':
Your favorite type of apple is ${ favorite_apple }.
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/branch-mandatory.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/branch-mandatory.yml&reset=1 "Click to try this interview")
Here, the “What is your favorite type of fruit” [`question`](https://docassemble.org/docs/questions.html#question)
is only “mandatory” if the user says that `apple` is their favorite type of fruit. Thus, if the `favorite_fruit` variable is `'banana'`, then **docassemble** will skip over the “What is your favorite type of apple?” [`question`](https://docassemble.org/docs/questions.html#question)
and proceed directly to the “Here is your document” [`question`](https://docassemble.org/docs/questions.html#question)
.
By setting the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
directive to a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
that uses variables defined in previous [`question`](https://docassemble.org/docs/questions.html#question)
blocks, you can write complex interviews that branch in a lot of different directions depending on the interview answers.
However, in a complex interview with a number of nested branches of logic, the [Python expressions](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
you will need to write to indicate whether a [`question`](https://docassemble.org/docs/questions.html#question)
should be asked could be very long and complicated. In the next section, we will discuss another way of implementing branching logic that avoids this complication.
Complex interviews: dependency satisfaction[¶](https://docassemble.org/docs/logic.html#intro_dependency)
---------------------------------------------------------------------------------------------------------
As explained in the previous section, when **docassemble** runs your interview, it goes through your [YAML](https://en.wikipedia.org/wiki/YAML)
from beginning to end and attempts to run each block that is marked as [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
. Marking a [`question`](https://docassemble.org/docs/questions.html#question)
as “mandatory” is one way to tell **docassemble** you want a [`question`](https://docassemble.org/docs/questions.html#question)
to be displayed.
**docassemble** can also do “dependency satisfaction.” For example, you can write an interview like this:
mandatory: True
question: |
Your favorite fruit is ${ favorite_fruit }.
subquestion: |
% if favorite_fruit == 'grapes':
Your favorite vineyard is ${ favorite_vineyard }.
% endif
---
question: |
Which vineyard do you think produces the best grapes?
fields:
- Vineyard: favorite_vineyard
---
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/dependency-demo.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/dependency-demo.yml&reset=1 "Click to try this interview")
In this interview, there is a mandatory question and then two questions that do not have a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
directive on them. If you run the interview, the first question asked is “What is your favorite fruit?” How did **docassemble** know it needed to ask that question, even though it was not marked as [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
? What happened was that **docassemble** tried to display the “Your favorite fruit is …” question, but in the process of doing so, it encountered an undefined variable `favorite_fruit`. So then it looked for a block that defines the ‘favorite\_fruit’ variable, and it found one, so it asked the “What is your favorite fruit?” question.
If the user types `grapes` in answer to that question, the interview asks a follow-up question, “Which vineyard do you think produces the best grapes?” and then proceeds to the final screen, which says “Your favorite fruit is grapes.” However, if the user says their favorite fruit is “apples,” the interview will skip the “Which vineyard do you think produces the best grapes?” question and will proceed directly to the final screen, which says “Your favorite fruit is apples.” Thus, with a single [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question, the interview does branching logic.
The branching logic is a by-product of the attempt to display the single [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question. If you were to change the text of the question and remove the reference to `favorite_vineyard`, the “Which vineyard do you think produces the best grapes?” question would never be asked. Or, if you were to change the `question` text to `Your favorite fruit is ${ favorite_fruit } and your favorite vegetable is ${ favorite_vegetable }.` then the order of questions would change, and the question that defines `favorite_vegetable` question would be asked right after the `favorite_fruit` question. When dependency satisfaction is used to ask questions, the order of questions is determined by which variables **docassemble** sees first.
Note that the order in which non-[`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
questions appear in the [YAML](https://en.wikipedia.org/wiki/YAML)
does not affect the order in which questions are asked. Each block in your [YAML](https://en.wikipedia.org/wiki/YAML)
is a just a “rule,” and you can specify as many “rules” in your [YAML](https://en.wikipedia.org/wiki/YAML)
as you want, in any order.
For example, the following block is a rule that indicates whether the user is eligible for a benefit.
code: |
if user.age_in_years() >= 60 or user.is_disabled:
user.eligible = True
else:
user.eligible = False
The rule says that the user is eligible if they are 60 or older, or if they are disabled, otherwise the user is not eligible.
Rules in **docassemble** are instructions for how to define a particular variable. The [`code`](https://docassemble.org/docs/code.html)
block above is a rule for how to define `user.eligible`. [`question`](https://docassemble.org/docs/questions.html#question)
blocks are also rules. Here is a rule that specifies how to define `user.is_disabled`:
question: Are you disabled?
yesno: user.is_disabled
This says that the rule for defining `user.disabled` is to ask the user a yes/no question.
It is possible to specify rules in fairly complex ways, as we will see later; you can write multiple blocks that define the same variable, so you can have alternative rules for different circumstances, or you can have a general rule that is overridden by a more specific rule in certain circumstances. You can write generic rules that apply to a variety of different variables.
When **docassemble** runs your interview, it will try to run your [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks, in the order in which the blocks appear in your [YAML](https://en.wikipedia.org/wiki/YAML)
. In the course of trying to run a block, **docassemble** might encounter a variable that hasn’t been defined yet. When this happens, **docassemble** will evaluate the “rules” you have defined, and it will run [`code`](https://docassemble.org/docs/code.html)
blocks, [`question`](https://docassemble.org/docs/questions.html#question)
blocks, or other types of blocks in order to try to obtain a definition of the undefined variable.
In the course of trying to define a variable, **docassemble** might encounter yet another undefined variable, in which case it will try to obtain a definition of that variable, and in the course of trying to define that variable, it may encounter yet another undefined variable. A rule that defines a variable may “depend on” the values of other variables. **docassemble**’s logic engine will perform “dependency satisfaction” by automatically figuring out what variable definitions are necessary and running the appropriate [`code`](https://docassemble.org/docs/code.html)
blocks or showing the appropriate [`question`](https://docassemble.org/docs/questions.html#question)
screens to the user.
This allows you, as the interview author, to specify rules and use variables in your interview or in your documents as you see fit, while **docassemble** does all the thinking about which questions need to be asked and in what order to ask them.
**docassemble** automatically refrains from asking unnecessary questions. For example, consider this example:
code: |
if user.age_in_years() >= 60 or user.is_disabled:
user.eligible = True
else:
user.eligible = False
If the user is 60 or older, there is no need to ask the user if they are disabled. It would waste the user’s time to ask that question. **docassemble** infers this from the rule. Thus “how to conduct the interview” and “what the legal rules are” are effectively the same thing, and can be specified in a single location.
Manually specifying the order of questions[¶](https://docassemble.org/docs/logic.html#intro_manual_order)
----------------------------------------------------------------------------------------------------------
Sometimes, you might not want the order of questions in the interview to be implicitly determined by the way **docassemble** processes rules; you might want to explicitly specify the order of questions. You can do this using a [`code`](https://docassemble.org/docs/code.html)
block.
mandatory: True
code: |
if favorite_fruit == 'grapes':
favorite_vineyard
favorite_vegetable
final_screen
---
event: final_screen
question: |
Your favorite fruit is ${ favorite_fruit }.
subquestion: |
Your favorite vegetable is ${ favorite_vegetable }.
% if favorite_fruit == 'grapes':
Your favorite vineyard is ${ favorite_vineyard }.
% endif
---
question: |
Which vineyard do you think produces the best grapes?
fields:
- Vineyard: favorite_vineyard
---
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
---
question: |
What is your favorite vegetable?
fields:
- Vegetable: favorite_vegetable
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/dependency-demo-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/dependency-demo-code.yml&reset=1 "Click to try this interview")
In this interview, the mandatory [`code`](https://docassemble.org/docs/code.html)
block drives the interview using dependency satisfaction, but in an explicit order. This block contains a few lines of [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code. The first variable encountered is `favorite_fruit`, which means that the `favorite_fruit` question will be asked. If `favorite_fruit` is `'grapes'`, then `favorite_vineyard` is evaluated, which means that `favorite_vineyard` will be asked. Then `favorite_vegetable` is asked, and then `final_screen` is sought. Because `final_screen` is a special screen, and the `event` directive is set to `final_screen`, the variable `final_screen` will actually not be defined; the screen is a dead-end screen with no fields and no “Continue” button.
If the mandatory [`code`](https://docassemble.org/docs/code.html)
block was not present, and instead the `final_screen` block was marked [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
, then the questions would have been asked in a different order: first `favorite_fruit`, then `favorite_vegetable`, and then `favorite_vineyard` (if the `favorite_fruit` was `'grapes'`). We were able to instruct **docassemble** to ask for `favorite_vineyard` immediately after `favorite_fruit` by specifying different interview logic in the [`code`](https://docassemble.org/docs/code.html)
block.
This mandatory [`code`](https://docassemble.org/docs/code.html)
block serves as an “outline” for the interview. Instead of ordering blocks in your [YAML](https://en.wikipedia.org/wiki/YAML)
, you can simply order lines in your mandatory [`code`](https://docassemble.org/docs/code.html)
block. The [`code`](https://docassemble.org/docs/code.html)
block lets you see the order of your interview at a glance, without having to page through a long interview. The indentation of text under `if` statements makes clear where there is a “branch” in the logic.
If you are familiar with [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, you might think that the mandatory [`code`](https://docassemble.org/docs/code.html)
block is weird, because simply putting the name of a variable by itself on a line doesn’t do anything; it’s not something that programmers normally do. However, it does something in **docassemble**, because if the variable is undefined, a [Python exception](https://docs.python.org/3/tutorial/errors.html)
will be “raised,” and the raising of that exception will tell **docassemble** that a definition of that variable needs to be obtained. **docassemble**’s dependency satisfaction system operates through the triggering of undefined variable exceptions.
So far, we have discussed three different techniques for specifying interview logic in **docassemble**:
1. A series of [`question`](https://docassemble.org/docs/questions.html#question)
blocks with [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
directives on them;
2. Allowing `question` blocks to be asked implicitly as a result of dependency satisfaction; and
3. Writing a [`code`](https://docassemble.org/docs/code.html)
block marked as [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
containing an explicit outline of the variables that need to be gathered and the conditions under which each variable definition should be sought.
These three techniques are not mutually exclusive; you can use them together. For example, you might have a mandatory [`question`](https://docassemble.org/docs/questions.html#question)
block followed by a mandatory [`code`](https://docassemble.org/docs/code.html)
block, followed by a mandatory [`question`](https://docassemble.org/docs/questions.html#question)
block.
mandatory: True
question: |
Welcome!
continue button field: intro_screen
---
mandatory: True
code: |
intro_screen
user.name.first
final_screen
---
mandatory: True
question: |
Your preferences.
subquestion: |
Your favorite fruit is ${ favorite_fruit }.
Your favorite vegetable is ${ favorite_vegetable }.
% if favorite_vegetable == 'turnip' and user.grows_own_turnips:
I grow turnips too!
% endif
Or you could have a mandatory [`code`](https://docassemble.org/docs/code.html)
block that only partially specifies the order of questions, and allows many questions to be asked explicitly. For example:
mandatory: True
code: |
intro_screen
user.name.first
final_screen
---
question: |
Welcome!
continue button field: intro_screen
---
event: final_screen
question: |
Your preferences.
subquestion: |
Your favorite fruit is ${ favorite_fruit }.
Your favorite vegetable is ${ favorite_vegetable }.
% if favorite_vegetable == 'turnip' and user.grows_own_turnips:
I grow turnips too!
% endif
Here, the mandatory [`code`](https://docassemble.org/docs/code.html)
block ensures that `intro_screen` and `user.name.first` are asked up front, but then uses dependency satisfaction to trigger the asking of `favorite_fruit` and `favorite_vegetable`, as well as the display of the `final_screen`.
Writing law as code to drive the interview[¶](https://docassemble.org/docs/logic.html#legal_logic)
---------------------------------------------------------------------------------------------------
**docassemble**’s “rules”-based logic system is particularly well-suited for legal applications. You can write legal logic in Python code, and **docassemble** will figure out how to ask the necessary questions to arrive at a legal judgment.
For example, suppose your interview needs to determine whether the user has legal standing as a grandparent to seek custody of a child. The relevant statute states that a grandparent can seek custody under the following circumstance:
(3) A grandparent of the child who is not in loco parentis to the child:
(i) whose relationship with the child began either with the consent
of a parent of the child or under a court order;
(ii) who assumes or is willing to assume responsibility for the
child; and
(iii) when one of the following conditions is met:
(A) the child has been determined to be a dependent child under
42 Pa.C.S. Ch. 63 (relating to juvenile matters);
(B) the child is substantially at risk due to parental abuse,
neglect, drug or alcohol abuse or incapacity; or
(C) the child has, for a period of at least 12 consecutive
months, resided with the grandparent, excluding brief
temporary absences of the child from the home, and is
removed from the home by the parents, in which case the
action must be filed within six months after the removal of
the child from the home.
The interview developer can rewrite this statute in Python, converting each legal concept into a variable.
comment: 23 Pa. C.S.A. 5324(3)
code: |
if relationship == 'Grandparent' \
and (relationship_began_with_consent \
or relationship_began_with_court_order) \
and willing_to_assume_responsibility \
and (child_is_dependent \
or child_is_at_risk \
or cared_for_child_for_a_year):
has_grandparent_standing = True
else:
has_grandparent_standing = False
Note: if you are wondering why there are `\` marks at the end of some of the lines, this is Python syntax for formatting source code and avoiding writing a very long line of code. If the `\` was not present, there would be a syntax error, because Python would interpret the newline to mean that you were done specifying a condition, and it would think you forgot to write a `:` to indicate the end of the condition. The `\` basically means “ignore the following newline and pretend this is all one long line.”
The values of a variable like `relationship_began_with_consent` could be determined by asking the user a `question`.
question: |
At some point, did one of the child's parents agree to let you care
for the child?
yesno: relationship_began_with_consent
Other variables, like `cared_for_child_for_a_year`, might be too complex to reduce to a single question. In that case, rather than using a [`question`](https://docassemble.org/docs/questions.html#question)
as the “rule” for what the variable means, you can use a [`code`](https://docassemble.org/docs/code.html)
block instead, and you can break the legal concept down into smaller pieces.
code: |
if (not child_lives_with_client) \
and child_used_to_live_with_client \
and child_taken_from_client_by_parent \
and child_taken_within_last_six_months \
and child_moved_in_at_least_12_months_before \
and child_lived_with_client_continuously:
cared_for_child_for_a_year = True
else:
cared_for_child_for_a_year = False
The rules for what these variables mean can in turn be specified as [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html)
blocks:
question: |
Does the child currently live with you?
yesno: child_lives_with_client
---
code: |
if as_datetime(date_child_taken_away) >= date_as_of_six_months_ago:
child_taken_within_last_six_months = True
else:
child_taken_within_last_six_months = False
Given [YAML](https://en.wikipedia.org/wiki/YAML)
rules like this, **docassemble** can automatically conduct a parsimonious interview; that is, it will not ask any unnecessary questions. For example, if `willing_to_assume_responsibility` is `False`, it will not ask `child_is_dependent`. The only thing you need to do to trigger this process is to set up a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block that requires a definition of `has_grandparent_standing`.
mandatory: True
code: |
if relationship == 'Grandparent' and not has_grandparent_standing:
grandparent_not_eligible
final_screen
---
event: grandparent_not_eligible
question: |
Sorry, you do not have standing as a grandparent to seek custody
under Pennsylvania law.
Many beginners find this style of rule-based logic specificiation to be confusing; they would rather specify exactly which questions are asked, and exactly what happens as a result of the answer to each question. However, when there are numerous legal rules and the interaction of the legal rules leads to a large number of possible scenarios, planning in advance the interview process for each one of these scenarios is time-consuming, and the work involved is mechanical rather than substantive. If you are going to offer users the ability to spot-edit any of their prior answers to questions in the middle of the interview, you will need to think about exactly which follow-up processes are necessary when the user makes such changes.
The “declarative” style of logic is very useful in these circumstances. All you need to do is the work of a lawyer – concentrate on specifying rules that are legally correct. You can specify multiple overlapping rules, covering special cases and general cases.
How rules determine interview process[¶](https://docassemble.org/docs/logic.html#interview_logic)
==================================================================================================
Many people envision a guided interview as a process whereby an interviewee starts at the beginning screen, then moves through a series of screens and then arrives at the end screen of the interview. At any point in time, the interviewee is envisioned as being located at a certain “place” in the interview process.
However, when the interview is driven by rules, this way of envisioning the interview process is misleading. For example, consider the following structure for an interview:
1. Ask “What is your name?”
2. Ask “When were you injured?”
3. If the injury took place more than two years ago, say, “Sorry, the statute of limitations has expired, so you cannot file a complaint.”
4. Ask “Where did the injury take place?”
5. Ask “How much did you pay in medical bills?”
6. Ask “How much time did you have to take off from work?”
7. etc.
8. Here is a complaint you can file in court.
Suppose the user started the interview one day before the statute of limitations expired, and proceeded as far as the “How much did you pay in medical bills?” question, but then took a few days to locate their medical bills, and didn’t complete the interview until a week after the statute of limitations expired. Should the guided interview allow the user to download a complaint, or should it tell the user, “Sorry, the statute of limitations has expired, so you cannot file a complaint.”? If you think of the interview process as one where the interviewee is “located” at a particular “place” in the interview, you would say that since the user has “gone past” the part of the interview process that checked for a statute of limitations problem, the user should be allowed to proceed.
The philosophy behind **docassemble** is that a robust interview process is one where the “current question” in the interview is determined not by which question “comes after” the previous question, but rather by the application of a set of rules to a set of facts. If there is a legal rule about the statute of limitations, it should be applied every time the screen loads, not just once at the beginning of the interview.
In **docassemble**, the interview logic is envisioned more as a “checklist” than a process. Each time the screen loads, **docassemble** reviews the checklist. What it does next depends on the application of the checklist to the current state of affairs.
By analogy, an airplane pilot will go through a checklist prior to takeoff. Whether the airline pilot turns onto the runway or goes back to the gate depends on the application of the checklist to the state of the aircraft and external factors like the weather. Likewise, what **docassemble** does when the screen loads depends on the application of the interview logic (specified in the [YAML](https://en.wikipedia.org/wiki/YAML)
) to the current state of the interview answers and external factors like the date.
From the user’s perspective, the **docassemble** interview process looks like something that has a beginning, an end, and a “current location,” but this is really just the by-product of **docassemble** running through a checklist every time the screen loads. “Do we know the user’s name? Check. Has the statute of limitations expired? Check. Do we know where the injury took place? Check.”
If you observe an airplane pilot at work, you might think, “gosh, the pilot spends so much time going through boring repetitive checklists, can’t he just grab the controls and fly the plane?” Similarly, you might look at what **docassemble** does every time the screen loads, and you might think, “ugh, why is it wasting time going through all of this logic, can’t it just move on to the next question?” Although the checklist method is repetitive, it is robust and is capable of catching hard-to-foresee problems.
The **docassemble** interview developer’s job is to design the checklist that leads to the interview process, not to specify the process directly. In most situations, this is a distinction without a difference, because the developer can write something like this:
mandatory: True
code: |
user.name.first
favorite_fruit
favorite_vegetable
final_screen
This is a checklist for what should be considered every time the screen loads: “if the name is not known, ask the name. If the favorite fruit is not known, ask for the favorite fruit. If the favorite vegetable is not known, ask for the favorite vegetable. Then show the ‘final\_screen’ screen.” This translates directly into a process: “first ask for the name, then the favorite fruit, then the favorite vegetable, then show the final screen.”
In more complicated interviews, the connection between the checklist and the process is less explicit. For example:
mandatory: True
code: |
user.name.first
plaintiffs.gather()
defendants.gather()
if not jurisdiction_is_proper:
kick_out_user
final_screen
In this interview, after the user is asked for their name, **docassemble** gathers a list of plaintiffs and then gathers a list of defendants. The process of [gathering groups](https://docassemble.org/docs/groups.html)
is complex and involves multiple `question` blocks. The line `plaintiffs.gather()` is effectively a checklist item that means “make sure the plaintiffs are gathered.” Groups can be gathered in a variety of ways. The questions might be “What is the name of the first plaintiff?”, “Are there any other plaintiffs?”, “What is the name of the second plaintiff?”, “Are there any other plaintiffs?”, etc. The line `if not jurisdiction_is_proper` implicitly triggers the defining of `jurisdiction_is_proper`, which is defined by a [`code`](https://docassemble.org/docs/code.html)
block.
By specifying a checklist, you can ensure the integrity of your interview’s logic, control the order of questions, and use to trigger the asking of questions that it would be too tedious to specify individually. There are things you need to think about, however, to ensure that your checklist results in a process that makes sense.
Beware of non-idempotency[¶](https://docassemble.org/docs/logic.html#idempotency)
----------------------------------------------------------------------------------
When designing the checklist that **docassemble** runs every time the screen loads, you need to be careful about how you specify the checklist items. For example, you wouldn’t want the checklist to be the following:
1. Ask for the user’s name.
2. Ask for the user’s date of birth.
3. Give the user an assembled document.
That would mean that every time the screen loads, it would ask for the user’s name. Instead, the checklist should be:
1. If the user’s name is unknown, ask them for it.
2. If the user’s date of birth is unknown, ask them for it.
3. Give the user an assembled document.
When you write a checklist in Python format, it looks like this:
mandatory: True
code: |
user.name.first
user.birthdate
final_screen
In **docassemble**, referencing the name of a variable like `user.birthdate` effectively means “if `user.birthdate` is undefined, stop what we are doing and seek out a definition of `user.birthdate`; otherwise, proceed to the next line.”
By contrast, if you use the [`force_ask()`](https://docassemble.org/docs/functions.html#force_ask)
function, it will always ask the question:
mandatory: True
code: |
force_ask('user.name.first')
force_ask('user.birthdate')
final_screen
Here, the first “checklist” item says that **docassemble** must ask a question to determine the value of `user.name.first` even if the variable is already defined. This is not what you want to do in a checklist; the user will be confused about why the interview asks for their name again when they just provided it. (This is is one of the reasons why the [`force_ask()`](https://docassemble.org/docs/functions.html#force_ask)
function is rarely used.)
**docassemble** allows you to run Python functions inside of a checklist, and in most situations this works as expected. For example:
mandatory: True
code: |
user.name.first
user.birthdate
if not record_exists_in_database_for(user):
error_screen
final_screen
Here, there is a function called `record_exists_in_database_for()` that looks up the user based on the user’s name and birthdate. It is ok if this function runs every time the screen loads.
However, beginning developers sometimes assume that they can do this:
mandatory: True
code: |
user.name.first
user.email
send_email(to=user, subject="Welcome", body="Welcome to the interview!")
user.birthdate
final_screen
This means that after the user provides their name and e-mail address, **docassemble** will send them an e-mail. However, it also means that every time the screen loads thereafter, **docassemble** will send another e-mail! The checklist item should have been written in such a way that the e-mail is only sent once:
mandatory: True
code: |
user.name.first
user.email
if not task_performed('welcome_email'):
send_email(to=user, subject="Welcome", body="Welcome to the interview!", task='welcome_email')
user.birthdate
final_screen
The [`task_performed()`](https://docassemble.org/docs/functions.html#task_performed)
function, combined with the `task` paramater of the [`send_email()`](https://docassemble.org/docs/functions.html#send_email)
function, is one way to ensure that code only runs once. Another method is to use a separate [`code`](https://docassemble.org/docs/code.html)
block that defines a variable:
mandatory: True
code: |
user.name.first
user.email
email_sent
user.birthdate
final_screen
---
code: |
send_email(to=user, subject="Welcome", body="Welcome to the interview!")
email_sent = True
The logic behind the `email_sent` line is: “if `email_sent` is not defined, run the code block in order to define it; otherwise continue to the next line.”
Another mistake that beginning developers sometimes make is writing a checklist that results in the user seeing a different screen if they refresh the screen without providing input. For example, consider this interview:
mandatory: True
code: |
user.name.first
user.email
if not task_performed('data_stored'):
store_data(user)
mark_task_as_performed('data_stored')
user.wants_email
if user.wants_email:
send_email(to=user, template=confirmation_email)
user.birthdate
final_screen
---
question: |
Do you want a confirmation e-mail?
yesno: user.wants_email
In this example, after the user provides their e-mail address, **docassemble** will run the `store_data()` function, then it will mark the `data_stored` “task” as having been completed, and then it will see that `user.wants_email` is undefined, so it will ask the user if they would like to receive a confirmation e-mail. Suppose that the user, instead of answering the question, refreshes the screen. The interview logic will be evaluated again. Now, since the `data_stored` “task” has been marked as complete, the Python code skips the `if` clause and asks the user for `user.birthdate`. But this defeats the user’s expectation; the user reasonably expects that when they refresh the screen, they will see the `user.wants_email` question again. The problem is with the interview logic.
Software developers use the term “idempotent” to describe a system that produces the same result if an action is repeated. The interview logic in this circumstance is not idempotent because when it is repeated, a different result is produced.
Normally, your “checklist” should be designed to result in idempotent behavior. The exception would be if the passage of time has made the “current question” obsolete. For example, if the user started the interview before the statute of limitations period expired, and then tried to continue with the interview after the statute of limitations period expired, it would be reasonable for the user to see a different screen when they refreshed the screen.
Another consequence of non-idempotent logic is that users might see a pop-up message saying “Input not processed.” This is because of a security feature in **docassemble**: if the browser tries to submit input for a `question` that is different from what the current `question` is according to the interview logic, **docassemble** will reject the browser’s attempt to change the interview answers. In the example above, if the user clicked “Yes” or “No” in response to the question “Do you want a confirmation e-mail?”, the user would have seen an “Input not processed” error and been sent to the `user.birthdate` question.
To fix the idempotency problem, you could take the e-mail sending code out of the conditional statement:
mandatory: True
code: |
user.name.first
user.email
if not task_performed('data_stored'):
store_data(user)
mark_task_as_performed('data_stored')
user.wants_email
if user.wants_email:
send_email(to=user, template=confirmation_email)
user.birthdate
final_screen
Writing idempotent logic is also important because of the way that **docassemble** runs [`code`](https://docassemble.org/docs/code.html)
blocks. Consider the following interview, which has a [`code`](https://docassemble.org/docs/code.html)
block for calculating the user’s total income:
mandatory: True
question: |
Tell me about your income and expenses.
fields:
- Benefits income: benefits_income
datatype: currency
- Business income: business_income
datatype: currency
- Business expenses: business_expenses
datatype: currency
---
mandatory: True
code: |
total_income = 0.0
---
mandatory: True
code: |
total_income = total_income + benefits_income
total_income = total_income + net_business_income
---
code: |
net_business_income = business_income - business_expenses
---
mandatory: True
question: |
Your total income is ${ currency(total_income) }.
At first glance, this logic looks correct; the interview gathers information from the user, initializes `total_income` to zero, then adds the benefits income and the net business income to `total_income`. However, you will find that the calculation is incorrect; `benefits_income` will be counted twice.
The problem is that this [`code`](https://docassemble.org/docs/code.html)
block is not idempotent:
mandatory: True
code: |
total_income = total_income + benefits_income
total_income = total_income + net_business_income
If this code runs more than once, the `total_income` will be increased each time. If you try to run this interview, this code block will run more than once. The first time it runs, it adds `benefits_income` to `total_income`, but then stops because `net_business_income` is undefined. **docassemble** obtains a definition of `net_business_income` in microseconds by running the [`code`](https://docassemble.org/docs/code.html)
block that defines `net_business_income`. But after it does that, it does not resume where it left off (adding `net_business_income` to `total_income`). It will repeat the [`code`](https://docassemble.org/docs/code.html)
block again, from the beginning. So `benefits_income` will be added to `total_income` a second time, and then `net_business_income` will be added, and then the “mandatory” block will be marked as having been completed, because it ran through all the way to the end.
If you are familiar with computer programming, **docassemble** works by trapping exceptions. When Python encounters an undefined variable, it raises an exception. **docassemble** traps that exception, figures out what variable was undefined, and then tries to define it. It does this either by asking the user a `question` or by running a ‘code’ block. Either way, the exception halts code execution, and Python is unable pick up exactly where it left off when the exception was raised.
The solution to this problem is to write the [`code`](https://docassemble.org/docs/code.html)
block so that it can be run repeatedly without making a miscalculation:
mandatory: True
code: |
total_income = 0
total_income = total_income + benefits_income
total_income = total_income + net_business_income
This way, the code will produce the correct total no matter how many undefined variables **docassemble** encounters along the way.
Inexperienced developers also sometimes make the error of assuming that all [`code`](https://docassemble.org/docs/code.html)
blocks will run to completion. For example, suppose that the above interview was written like this, with only one `mandatory` block:
question: |
Tell me about your income and expenses.
fields:
- Benefits income: benefits_income
datatype: currency
- Business income: business_income
datatype: currency
- Business expenses: business_expenses
datatype: currency
---
code: |
total_income = 0.0
total_income = total_income + benefits_income
total_income = total_income + net_business_income
---
code: |
net_business_income = business_income - business_expenses
---
mandatory: True
question: |
Your total income is ${ currency(total_income) }.
This interview appears to be reasonable, but actually it contains a flaw. When **docassemble** tries to show the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question, it encounters an undefined variable `total_income`, so it seeks out a definition of `total_income`. It tries to run this [`code`](https://docassemble.org/docs/code.html)
block:
code: |
total_income = 0.0
total_income = total_income + benefits_income
total_income = total_income + net_business_income
**docassemble** sets `total_income` to zero, and then encounters an undefined variable, `benefits_income`, so it asks the `question` that defines `benefits_income`. However, what if the user refreshed the screen on the question that asks for the `benefits_income`? **docassemble** would attempt to show the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question again, and this time `total_income` is defined, so **docassemble** can display the screen, which says that the total income is zero. “But wait,” you say, “it didn’t finish running the [`code`](https://docassemble.org/docs/code.html)
block that defines `total_income`!” True, but the rule of **docassemble**’s logic is that it goes through your [YAML](https://en.wikipedia.org/wiki/YAML)
, runs [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks that haven’t been run before, and tries to obtain definitions for any undefined variables that are encountered along the way. Nothing in this rule says that it will remember if it left a [`code`](https://docassemble.org/docs/code.html)
block early and go back to it.
The moral of the story is that if you are going to use dependency satisfaction, do not allow your dependencies to be satisfied prematurely. The [`code`](https://docassemble.org/docs/code.html)
block should be written instead as:
code: |
total_income = benefits_income + net_business_income
or
code: |
total_income = benefits_income + net_business_income
draft_total_income = 0.0
draft_total_income = total_income + benefits_income
draft_total_income = total_income + net_business_income
total_income = draft_total_income
del draft_total_income
You can think of the undefined-ness of a variable as **docassemble**’s incentive to obtain the definition of the variable. It is like hiring a busy contractor to do work on your house; if you give the contractor his final payment after he is only halfway done with the job, he might leave at the end of the day and forget to come back later to finish the work.
You might be tempted to combine the definition of several variables in a single [`code`](https://docassemble.org/docs/code.html)
block, perhaps because you think it saves space or is easier to read:
code: |
subtotal = 0
for item in asset:
if item.countable:
subtotal = subtotal + item.value
total_assets = subtotal
temp_list = []
for item in income:
if item.included and item.type not in income.items:
temp_list.append(item.type)
income_items = temp_list
Think about what will happen if the interview needs a value of `total_assets`. It will run the [`code`](https://docassemble.org/docs/code.html)
block, and halfway through, the value of `total_assets` will be obtained. But the code will not stop executing; it will go on to start building the `income_items` list. This will work fine if the `income` list has been completely gathered, but what if it has not been? Then the [`code`](https://docassemble.org/docs/code.html)
block may result in the asking of a question about the `income` list, but if the user refreshes the screen, that question will go away. This introduces an idempotency problem.
It is a much better practice to separate your code into single-purpose `code` blocks:
code: |
subtotal = 0
for item in asset:
if item.countable:
subtotal = subtotal + item.value
total_assets = subtotal
---
code: |
temp_list = []
for item in income:
if item.included and item.type not in income.items:
temp_list.append(item.type)
income_items = temp_list
This way, no matter whether your interview needs `total_assets` first or `income_items` first, and regardless of whether it has already gathered `income` or `asset`, these [`code`](https://docassemble.org/docs/code.html)
blocks will perform their function and deliver a definition without causing any non-idempotent questions to be asked.
As a general rule, let each [`code`](https://docassemble.org/docs/code.html)
block serve a single purpose, or a set of closely-related purposes, and let it deliver its award (the defining of the variable sought) on the last line. If you get into this habit, you will avoid hard-to-debug logic errors.
Although typically your non-`mandatory` [`code`](https://docassemble.org/docs/code.html)
blocks should only set one variable at a time, it is ok if they set other variables incidentally. However, in that situation you should probably use the [`only sets`](https://docassemble.org/docs/modifiers.html#only%20sets)
modifier.
For example, suppose you have an interview in which you want to ask the user, “Do you receive income from public benefits?”, and you want to set the variable `has_benefits` to the answer, but as a double-check on the validity of the answer, you want to set `has_benefits` to `True` during the income gathering process if the user indicates that they have income from disability or welfare income.
question: |
Do you receive income from public benefits?
yesno: has_benefits
---
code: |
temp_total = []
for item in income:
if item.included and item.type not in income.items:
temp_total.append(item.type)
if item.type in ['disability', 'welfare']:
has_benefits = True
total_income = temp_total
The problem here is that the income gathering question will now be called upon to set `has_benefits`. If `has_benefits` is needed before `total_income`, the interview will start asking about income items, but will mysteriously stop in the middle of the process if the user enters a disability or welfare income item. Then, when `total_income` is needed later in the interview, it will resume asking questions about the income items. It would be better if you used `only sets`:
only sets: total_income
code: |
temp_total = []
for item in income:
if item.included and item.type not in income.items:
temp_total.append(item.type)
if item.type in ['disability', 'welfare']:
has_benefits = True
total_income = temp_total
That way, the [`code`](https://docassemble.org/docs/code.html)
block will only be called upon to define `total_income`. It can still have the side effect of setting `has_benefits` to `True`, but it will not be called upon to define anything other than `total_income`.
The logical order of an interview[¶](https://docassemble.org/docs/logic.html#order)
====================================================================================
In the previous sections, we have explained that when **docassemble** runs your interview, it goes through your [YAML](https://en.wikipedia.org/wiki/YAML)
looking for blocks that are [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
, and it tries to run them in order. When it encounters an undefined variable, it stops what it is doing and tries to obtain a definition of that undefined variable.
If a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`question`](https://docassemble.org/docs/questions.html#question)
is answered, or a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html)
block’s [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code runs all the way through to end, then **docassemble** remembers that the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block has been completed, and the next time it evaluates the interview logic, it will skip over the block.
(Technical note: how does **docassemble** remember that a block has been completed? It stores a variable in the interview answers, inside of a special dictionary called `_internal`. In order to identify the blocks that have been completed, it uses the block’s [`id`](https://docassemble.org/docs/modifiers.html#id)
. If you do not specify an [`id`](https://docassemble.org/docs/modifiers.html#id)
on a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block, **docassemble** will generate an identifier like `Question_0` or `Question_1` for the first and second blocks in your [YAML](https://en.wikipedia.org/wiki/YAML)
file. This means that if you have an interview that is “in production” and users have active sessions in that interview, and then you change the [YAML](https://en.wikipedia.org/wiki/YAML)
to insert new blocks or move them around, you could cause these identifiers to change, and then users who started sessions before you changed the YAML could experience problems where questions they have already answered are re-asked. In order to avoid this problem, make sure to attach a unique [`id`](https://docassemble.org/docs/modifiers.html#id)
to each [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block in your interview. That way, even if you rearrange the [YAML](https://en.wikipedia.org/wiki/YAML)
, users with existing sessions will not experience problems.)
In addition to [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
, there is a second type of modifier you can use to force a [`code`](https://docassemble.org/docs/code.html)
block to be processed. If you mark a [`code`](https://docassemble.org/docs/code.html)
block with `initial: True`, then the block will be run every time the screen loads, even if it has run before. The block is “initial” in the sense that it initializes the interview logic that will be evaluated during the screen load.
[`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
and [`initial`](https://docassemble.org/docs/logic.html#initial)
blocks are evaluated in the order they appear in the question file. Therefore, the location in the interview of [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
and [`initial`](https://docassemble.org/docs/logic.html#initial)
blocks, relative to each other, is important.
The order in which non-[`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
and non-[`initial`](https://docassemble.org/docs/logic.html#initial)
questions appear is usually not important. If **docassemble** needs a definition of a variable, it will go looking for a block that defines the variable.
Consider the following example:
question: |
Do you like penguins?
yesno: user_likes_penguins
---
question: |
Do you like elephants?
yesno: user_likes_elephants
---
mandatory: true
question: |
Hello!
buttons:
- Hello to you: continue
---
mandatory: true
question: |
What is your name?
fields:
- Name: user_name
---
mandatory: true
question: |
Your favorite food is
${ favorite_food }.
% if user_likes_penguins:
You are a fan of penguins.
% else:
You detest penguins, for some
strange reason.
% endif
---
question: |
What is your favorite food?
fields:
- Favorite food: favorite_food
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/order-of-blocks.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/order-of-blocks.yml&reset=1 "Click to try this interview")
The order of the questions is:
1. Hello!
2. What is your name?
3. What is your favorite food?
4. Do you like penguins?
The first two questions are asked because the corresponding [`question`](https://docassemble.org/docs/questions.html#question)
blocks are marked as [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
. They are asked in the order in which they are asked because of the way the [`question`](https://docassemble.org/docs/questions.html#question)
blocks are ordered in the [YAML](https://en.wikipedia.org/wiki/YAML)
file.
The next two questions are asked implicitly. The third and final [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block makes reference to two variables: `favorite_food` and `user_likes_penguins`. Since the [`question`](https://docassemble.org/docs/questions.html#question)
s that define these variables are not `mandatory`, they can appear anywhere in the [YAML](https://en.wikipedia.org/wiki/YAML)
file, in any order you want. In this case, the `favorite_food` [`question`](https://docassemble.org/docs/questions.html#question)
block is at the end of the [YAML](https://en.wikipedia.org/wiki/YAML)
file, and the `user_likes_penguins` [`question`](https://docassemble.org/docs/questions.html#question)
block is at the start of the [YAML](https://en.wikipedia.org/wiki/YAML)
file.
The order in which these two questions are asked is determined by the order of the variables in the text of the final [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
question. Since `favorite_food` is referenced first, and `user_likes_penguins` is referenced afterwards, the user is asked about food and then asked about penguins.
Note that there is also an extraneous question in the interview that defines `user_likes_elephants`; the presence of this [`question`](https://docassemble.org/docs/questions.html#question)
block in the [YAML](https://en.wikipedia.org/wiki/YAML)
file has no effect on the interview.
Generally, you can order non-[`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks in your [YAML](https://en.wikipedia.org/wiki/YAML)
file any way you want. You may want to group them by subject matter into separate [YAML](https://en.wikipedia.org/wiki/YAML)
files that you [`include`](https://docassemble.org/docs/initial.html#include)
in your main [YAML](https://en.wikipedia.org/wiki/YAML)
file. When your interviews get complicated, there is no natural order to questions. In some situations, a question may be asked early, and in other situations, a question may be asked later.
Overriding one question with another[¶](https://docassemble.org/docs/logic.html#overriding)
--------------------------------------------------------------------------------------------
The order in which non-[`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks appear in the [YAML](https://en.wikipedia.org/wiki/YAML)
file is only important if you have multiple blocks that each offer to define the same variable. In that case, the order of these blocks relative to each other is important. When looking for blocks that offer to define a variable, **docassemble** will use later-defined blocks first. Later blocks “supersede” the blocks that came before.
This allows you to [`include`](https://docassemble.org/docs/initial.html#include)
“libraries” of questions in your interview while retaining the ability to customize how any particular question is asked.
As explained in the [initial blocks](https://docassemble.org/docs/initial.html)
section, the effect of an [`include`](https://docassemble.org/docs/initial.html#include)
block is basically equivalent to copying and pasting the contents of the included file into the original file.
For example, suppose that there is a [YAML](https://en.wikipedia.org/wiki/YAML)
file called `question-library.yml`, which someone else wrote, which consists of the following questions:
question: |
Nice evening, isn't it?
yesno: user_agrees_it_is_a_nice_evening
---
question: |
Interested in going to the dance tonight?
yesno: user_wants_to_go_to_dance
You can write an interview that uses this question library:
include:
- question-library.yml
---
mandatory: True
code: |
if user_agrees_it_is_a_nice_evening and user_wants_to_go_to_dance:
good_news
---
mandatory: True
question: |
Say, I have to run. Bye!
---
event: good_news
question: |
That is splendid news!
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/use-question-library.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/use-question-library.yml&reset=1 "Click to try this interview")
When **docassemble** needs to know the definition of `user_agrees_it_is_a_nice_evening` or `user_wants_to_go_to_dance`, it will be able to find a block in `question-library.yml` that offers to define the variable.
Suppose, however, that you thought of a better way to ask the `user_wants_to_go_to_dance` question, but you don’t want to get rid of `question-library.yml` entirely. You could override the `user_wants_to_go_to_dance` question in `question-library.yml` by doing the following:
include:
- question-library.yml
---
question: |
So, about that dance tonight . . .
wanna go?
yesno: user_wants_to_go_to_dance
---
mandatory: True
code: |
if user_agrees_it_is_a_nice_evening and user_wants_to_go_to_dance:
good_news
---
mandatory: True
question: |
Say, I have to run. Bye!
---
event: good_news
question: |
That is splendid news!
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/override.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/override.yml&reset=1 "Click to try this interview")
This interview file loads the two questions defined in `question-library.yml`, but then, later in the list of questions, provides a different way to get the value of `user_wants_to_go_to_dance`. When **docassemble** goes looking for a question to provide a definition of `user_wants_to_go_to_dance`, it starts with the questions that were defined last, and it will prioritize your question over the question in `question-library.yml`. Your [`question`](https://docassemble.org/docs/questions.html#question)
block takes priority because it is located _later_ in the [YAML](https://en.wikipedia.org/wiki/YAML)
file.
This is similar to the way law works: old laws do not disappear from the law books, but they can get superseded by newer laws. “Current law” is simply “old law” that has not yet been superseded.
A big advantage of this feature is that you can include “libraries” written by other people without having to edit those other files in order to tweak them. You can use another person’s work without taking on the responsibility of maintaining that person’s work over time; you can just incorporate by reference that person’s file, which they continue to maintain.
For example, if someone else has developed interview questions that determine a user’s eligibility for food stamps, you can incorporate by reference that developer’s [YAML](https://en.wikipedia.org/wiki/YAML)
file into an interview that assesses whether a user is maximizing his or her public benefits. When the law about food stamps changes, that developer will be responsible for updating his or her [YAML](https://en.wikipedia.org/wiki/YAML)
file; your interview will not need to change. This allows for a division of labor. All you will need to do is make sure that the **docassemble** [package](https://docassemble.org/docs/packages.html)
containing the food stamp [YAML](https://en.wikipedia.org/wiki/YAML)
file gets updated on the server when the law changes.
Fallback questions[¶](https://docassemble.org/docs/logic.html#fallback)
------------------------------------------------------------------------
If a [`code`](https://docassemble.org/docs/code.html)
block does not, for whatever reason, actually define the variable, **docassemble** will “fall back” to a block that is located earlier in the [YAML](https://en.wikipedia.org/wiki/YAML)
file. For example:
include:
- question-library.yml
---
question: |
I forgot, did we already agree to go to the dance together?
yesno: we_already_agreed_to_go
---
code: |
if we_already_agreed_to_go:
user_wants_to_go_to_dance = True
---
mandatory: True
code: |
if user_agrees_it_is_a_nice_evening and user_wants_to_go_to_dance:
good_news
---
mandatory: True
question: |
Say, I have to run. Bye!
---
event: good_news
question: |
That is splendid news!
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/fallback2.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/fallback2.yml&reset=1 "Click to try this interview")
In this case, when **docassemble** tries to get a definition of `user_wants_to_go_to_dance`, it will first try running the [`code`](https://docassemble.org/docs/code.html)
block, and then it will encounter `we_already_agreed_to_go` and seek its definition. If the value of `we_already_agreed_to_go` turns out to be false, the [`code`](https://docassemble.org/docs/code.html)
block will run its course without setting a value for `user_wants_to_go_to_dance`. Not giving up, **docassemble** will keep going backwards through the blocks in the [YAML](https://en.wikipedia.org/wiki/YAML)
file, looking for one that offers to define `user_wants_to_go_to_dance`. It will find such a question among the questions included by reference from `question_library.yml`, namely the question “Interested in going to the dance tonight?”
This “fall back” process can also happen with special [`question`](https://docassemble.org/docs/questions.html#question)
blocks that use the [`continue`](https://docassemble.org/docs/questions.html#continue)
option.
include:
- question-library.yml
---
question: Which of these statements is true?
choices:
- "I am old-fashioned":
question: |
My darling, would you do me the
honor of accompanying me to
the dance this fine evening?
yesno: user_wants_to_go_to_dance
- "I don't care for flowerly language": continue
---
mandatory: True
code: |
if user_agrees_it_is_a_nice_evening and user_wants_to_go_to_dance:
good_news
---
mandatory: True
question: |
Say, I have to run. Bye!
---
event: good_news
question: |
That is splendid news!
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/fallback.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/fallback.yml&reset=1 "Click to try this interview")
In this case, the special [`continue`](https://docassemble.org/docs/questions.html#continue)
choice causes **docassemble** to skip the [`question`](https://docassemble.org/docs/questions.html#question)
block and look elsewhere for a definition of `user_wants_to_go_to_dance`. **docassemble** will “fall back” to the version of the question that exists within `question-library.yml`. When looking for a block that offers to define a variable, **docassemble** starts at the bottom and works its way up.
(Note that [`question`](https://docassemble.org/docs/questions.html#question)
s using [`continue`](https://docassemble.org/docs/questions.html#continue)
are of limited utility because they cannot use the [`generic object` modifier](https://docassemble.org/docs/fields.html#generic)
or [index variables](https://docassemble.org/docs/fields.html#index%20variables)
. However, [`code`](https://docassemble.org/docs/code.html)
blocks do not have this limitation.)
So, to recapitulate: when **docassemble** considers what blocks it _must_ process, it goes from top to bottom through your interview [YAML](https://en.wikipedia.org/wiki/YAML)
file, looking for [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
and [`initial`](https://docassemble.org/docs/logic.html#initial)
blocks; if a block is later in the file, it is processed later in time. However, when **docassemble** considers what question it should ask to define a particular variable, it goes from bottom to top; if a block is later in the file, it is considered to “supersede” blocks that are earlier in the file.
As explained [below](https://docassemble.org/docs/logic.html#precedence)
, however, instead of relying on relative placement of blocks in the [YAML](https://en.wikipedia.org/wiki/YAML)
file, you can explicitly indicate which blocks take precedence over other blocks.
How **docassemble** runs your code[¶](https://docassemble.org/docs/logic.html#howitworks)
==========================================================================================
**docassemble** goes through your interview [YAML](https://en.wikipedia.org/wiki/YAML)
file from start to finish, incorporating [`include`](https://docassemble.org/docs/initial.html#include)
d files as it goes. It always executes [`initial`](https://docassemble.org/docs/logic.html#initial)
code when it sees it. It executes any [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html)
blocks that have not been successfully executed yet. If it encounters a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`question`](https://docassemble.org/docs/questions.html#question)
that it has not been successfully asked yet, it will stop and ask the question.
If at any time it encounters a variable that is undefined, for example while trying to formulate a question, it will interrupt itself in order to go find the a definition for that variable.
Whenever **docassemble** comes back from one of these excursions to find the definition of a variable, it does not pick up where it left off; it starts from the beginning again.
Therefore, when writing code for an interview, you need to keep in mind that any particular block of code may be re-run from the beginning multiple times.
For example, consider the following code:
---
mandatory: True
code: |
if user_has_car:
user_net_worth = user_net_worth + resale_value_of_user_car
if user_car_brand == 'Toyota':
user_is_sensible = True
elif user_car_is_convertible:
user_is_sensible = False
---
The intention of this code is to increase the user’s net worth by the resale value of the user’s car, if the user has a car. If the code only ran once, it would work as intended. However, because of **docassemble**’s design, which is to ask questions “as needed,” the code actually runs like this:
1. **docassemble** starts running the code; it encounters `user_has_car`, which is undefined. It finds a question that defines `user_has_car` and asks it. (We will assume `user_has_car` is set to True.)
2. **docassemble** runs the code again, and tries to increment the `user_net_worth` (which we can assume is already defined); it encounters `resale_value_of_user_car`, which is undefined. It finds a question that defines `resale_value_of_user_car` and asks it.
3. **docassemble** runs the code again. The value of `user_net_worth` is increased. Then the code encounters `user_car_brand`, which is undefined. It finds a question that defines `user_car_brand` and asks it.
4. **docassemble** runs the code again. The value of `user_net_worth` is increased (again). If `user_car_brand` is equal to “Toyota,” then `user_is_sensible` is set. In that case, the code runs successfully to the end, and the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
code block is marked as completed, so that it will not be run again.
5. However, if `user_car_brand` is not equal to “Toyota,” the code will encounter `user_car_is_convertible`, which is undefined. **docassemble** will find a question that defines `user_car_is_convertible` and ask it. **docassemble** will then run the code again, the value of `user_net_worth` will increase yet again, and then (finally) the code will run successfully to the end.
The solution here is to make sure that your code is prepared to be stopped and restarted. For example, you could have a separate code block to compute `user_net_worth`:
---
mandatory: True
code: |
user_net_worth = 0
if user_has_car:
user_net_worth = user_net_worth + resale_value_of_user_car
if user_has_house:
user_net_worth = user_net_worth + resale_value_of_user_house
---
Note that [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
must be true for this to work sensibly. If this were an optional code block, it would not run to completion because `user_net_worth` would already be defined when **docassemble** came back from asking whether the user has a car.
How **docassemble** finds questions for variables[¶](https://docassemble.org/docs/logic.html#variablesearching)
================================================================================================================
There can be multiple questions or code blocks in an interview that can define a given variable. You can write [`generic object`](https://docassemble.org/docs/modifiers.html#generic%20object)
questions in order to define attributes of objects, and you can use [index variables](https://docassemble.org/docs/fields.html#index%20variables)
to refer to any given item in a [`DAList`](https://docassemble.org/docs/objects.html#DAList)
or [`DADict`](https://docassemble.org/docs/objects.html#DADict)
(or a subtype of these objects). Which one will be used?
In general, if you have multiple questions or code blocks that are capable of defining a variable, **docassemble** will try the more specific ones first, and then the more general ones.
For example, if the interview needs a definition of `fruit['a'].seed_info.tally['b'].molecules[4].name`, it will look for questions that offer to define the following variables, in this order:
fruit['a'].seed_info.tally['b'].molecules[4].name
fruit[i].seed_info.tally['b'].molecules[4].name
fruit['a'].seed_info.tally[i].molecules[4].name
fruit['a'].seed_info.tally['b'].molecules[i].name
fruit[i].seed_info.tally[j].molecules[4].name
fruit[i].seed_info.tally['b'].molecules[j].name
fruit['a'].seed_info.tally[i].molecules[j].name
fruit[i].seed_info.tally[j].molecules[k].name
Then it will look for [`generic object`](https://docassemble.org/docs/modifiers.html#generic%20object)
blocks that offer to define the following variables, in this order:
x['a'].seed_info.tally['b'].molecules[4].name
x[i].seed_info.tally['b'].molecules[4].name
x['a'].seed_info.tally[i].molecules[4].name
x['a'].seed_info.tally['b'].molecules[i].name
x[i].seed_info.tally[j].molecules[4].name
x[i].seed_info.tally['b'].molecules[j].name
x['a'].seed_info.tally[i].molecules[j].name
x[i].seed_info.tally[j].molecules[k].name
x.seed_info.tally['b'].molecules[4].name
x.seed_info.tally[i].molecules[4].name
x.seed_info.tally['b'].molecules[i].name
x.seed_info.tally[i].molecules[j].name
x.tally['b'].molecules[4].name
x.tally[i].molecules[4].name
x.tally['b'].molecules[i].name
x.tally[i].molecules[j].name
x['b'].molecules[4].name
x[i].molecules[4].name
x['b'].molecules[i].name
x[i].molecules[j].name
x.molecules[4].name
x.molecules[i].name
x[4].name
x[i].name
x.name
Moreover, when **docassemble** searches for a [`generic object`](https://docassemble.org/docs/modifiers.html#generic%20object)
question for a given variable, it first look for [`generic object`](https://docassemble.org/docs/modifiers.html#generic%20object)
questions with the object type of `x` (e.g., [`Individual`](https://docassemble.org/docs/objects.html#Individual)
). Then it will look for [`generic object`](https://docassemble.org/docs/modifiers.html#generic%20object)
questions with the parent type of object type of `x` (e.g., [`Person`](https://docassemble.org/docs/objects.html#Person)
). It will keep going through the ancestors, stopping at the most general object type, [`DAObject`](https://docassemble.org/docs/objects.html#DAObject)
.
Note that the order of questions or code blocks in the [YAML](https://en.wikipedia.org/wiki/YAML)
matters where the variable name is the same; the blocks that appear later in the [YAML](https://en.wikipedia.org/wiki/YAML)
will be tried first. But when the variable name is different, the order of the blocks in the [YAML](https://en.wikipedia.org/wiki/YAML)
does not matter. If your interview has a question that offers to define `seeds['apple']` and another question that offers to define `seeds[i]`, the `seeds['apple']` question will be tried first, regardless of where the question is located in the the [YAML](https://en.wikipedia.org/wiki/YAML)
.
Here is an example in which a relatively specific question, which sets `veggies[i][1]`, will be used instead of a more general question, which sets `veggies[i][j]`, when applicable:
question: |
How much does the other
${ i } weigh?
fields:
- Grams: veggies[i][1]
datatype: number
---
question: |
How much does the
${ ordinal(j) }
${ i } weigh?
fields:
- Grams: veggies[i][j]
datatype: number
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/nested-veggies-override.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/nested-veggies-override.yml&reset=1 "Click to try this interview")
These rules about which blocks are tried before other blocks can be overriden using the [`order` initial block](https://docassemble.org/docs/initial.html#order)
or the [`id` and `supersedes`](https://docassemble.org/docs/modifiers.html#precedence)
modifiers. You can use the [`if` modifier](https://docassemble.org/docs/modifiers.html#if)
to indicate that a given [`question`](https://docassemble.org/docs/questions.html#question)
should only be asked under certain conditions. You can use the [`scan for variables` modifier](https://docassemble.org/docs/modifiers.html#scan%20for%20variables)
to indicate that a [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html)
block should only be considered when looking to define a particular variable or set of variables, even though it is capable of defining other variables.
Specifiers that control interview logic[¶](https://docassemble.org/docs/logic.html#specifiers)
===============================================================================================
`mandatory`[¶](https://docassemble.org/docs/logic.html#mandatory)
------------------------------------------------------------------
By default, all blocks in an interview are optional; they will be called upon only if needed to retrieve the value of a variable. However, if all blocks are optional, the interview has nothing to do. You can use the [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
modifier to indicate that a block must be run. The first [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block in your interview will be the starting point of the interview logic when the user first starts the interview.
Consider the following as a complete interview file:
---
question: What is the capital of Maine?
fields:
- Capital: maine_capital
---
question: Are you sitting down?
yesno: user_sitting_down
mandatory: True
---
question: Your socks do not match.
mandatory: True
---
The interview will ask “Are you sitting down” and then it will say “Your socks do not match.” It will not ask “What is the capital of Maine?”
Another way to control the logic of an interview is to have a single, simple [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html)
block that sets the interview in motion.
For example:
---
mandatory: True
code: |
if user_sitting_down:
user_informed_that_socks_do_not_match
else:
user_will_not_sit_down
---
question: What is the capital of Maine?
fields:
- Capital: maine_capital
---
question: Are you sitting down?
yesno: user_sitting_down
---
question: Your socks do not match.
sets: user_informed_that_socks_do_not_match
---
question: You really should have sat down.
subquestion: I had something important to tell you.
sets: user_will_not_sit_down
---
Here, the single [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block contains simple [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code that contains the entire logic of the interview.
If a [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
specifier is not present within a block, it is as though [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
was set to `False`.
The value of [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
can be a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
expression. If it is a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
expression, the [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html)
block will be treated as mandatory if the expression evaluates to a true value.
mandatory: |
favorite_food == "apples"
question: |
You have good taste in food.
buttons:
- Continue: continue
---
mandatory: True
question: |
Thank you for your input.
---
question: |
What is your favorite food?
fields:
- no label: favorite_food
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/mandatory-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/mandatory-code.yml&reset=1 "Click to try this interview")
It is a best practice to tag all [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks with an [`id`](https://docassemble.org/docs/modifiers.html#id)
.
`initial`[¶](https://docassemble.org/docs/logic.html#initial)
--------------------------------------------------------------
The `initial` modifier causes the [`code`](https://docassemble.org/docs/code.html)
block to be run every time **docassemble** processes your interview (i.e., every time the screen loads during an interview). [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks, by contrast, are never run again during the session if they are successfully “asked” once. **docassemble** executes the code in an [`initial`](https://docassemble.org/docs/logic.html#initial)
block in the same way it executes the code of [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks, except that running to completion does not mean the block will not be executed again.
`initial` blocks should be used in the following situations:
* “Initializing” the Python context in a [multi-user interview](https://docassemble.org/docs/roles.html)
depending on who the user is. For example, if your interview uses a variable `user` that should always refer to an [`Individual`](https://docassemble.org/docs/objects.html#Individual)
object corresponding to the user, you can write an `initial` block that looks at `user_info().email` to figure out who the logged-in user is.
* When you are using the [actions](https://docassemble.org/docs/functions.html#actions)
feature and you want to make sure the [actions](https://docassemble.org/docs/functions.html#actions)
are processed only in particular circumstances.
Here is an example that illustrates how `initial` blocks work:
mandatory: True
code: |
counter = 0
---
initial: True
code: |
counter = counter + 1
---
question: |
How many peaches do you have?
subquestion: |
The value of the counter
is ${ counter }.
fields:
- no label: peaches
datatype: integer
---
code: |
fruits = peaches + pears
---
question: |
How many pears do you have?
subquestion: |
The value of the counter
is ${ counter }.
fields:
- no label: pears
datatype: integer
---
question: |
You have ${ fruits } pieces of fruit.
subquestion: |
The value of the counter
is ${ counter }.
mandatory: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/initial.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/initial.yml&reset=1 "Click to try this interview")
Note in this example that from screen to screen, the `counter` increments from 1 to 3 and then to 6. The counter does not count the number of screens displayed, but rather the number of times the interview logic was evaluated. The number of times the interview logic gets evaluated is hard to predict, but you can count on it being evaluated many times, so you need to make sure you write your logic in an [idempotent](https://docassemble.org/docs/logic.html#idempotency)
manner.
On every screen load, the interview logic is evaluated prior to processing the input in order to ensure that the input is responding to whatever the current `question` is. **docassemble** needs to evaluate the interview logic in order to know what the current `question` is. It is then evaluated after the input is processed, so that the user can be presented with the next `question`. In addition, the interview logic will be re-evaluated when an undefined variable is encountered and a `code` block provides the value of a variable.
1. The interview logic is evaluated, but the evaluation stops when the undefined variable `fruit` is encountered. The interview then tries to run the [`code`](https://docassemble.org/docs/code.html)
block to get `fruit`, but encounters an undefined variable `peaches`, so it asks a question to gather `peaches`.
2. The interview logic is evaluated, but the evaluation stops when the undefined variable `fruit` is encountered. The interview then tries to run the [`code`](https://docassemble.org/docs/code.html)
block to get `fruit`, but encounters an undefined variable `pears`, so it asks a question to gather `pears`.
3. The interview logic is evaluated, but the evaluation stops when the undefined variable `fruit` is encountered. The interview then runs the [`code`](https://docassemble.org/docs/code.html)
block, and this time, `fruit` is successfully defined.
4. The interview logic is evaluated again, and the final question is displayed.
Like [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
, `initial` can be set to `True`, `False`, or to [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code that will be evaluated to see whether it evaluates to a true or false value.
If your interview has a single [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
code block and it is incapable of running to completion, then you don’t really need an `initial` block because you can put the logic that needs to run every time the screen loads at the beginning of that [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
block.
`need`[¶](https://docassemble.org/docs/logic.html#need)
--------------------------------------------------------
The `need` specifier allows you to manually specify the prerequisites of a [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html)
block. This can be helpful for tweaking the order in which questions are asked.
mandatory: True
need:
- number_of_years_old
- favorite_animal
question: |
Thank you for that information.
subquestion: |
My favorite animal is
${ favorite_animal },
too!
% if number_of_years_old < 10:
You're growing so fast. Pretty
soon you'll be driving a
${ favorite_color } car!
% endif
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/need-specifier.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/need-specifier.yml&reset=1 "Click to try this interview")
In this example, the ordinary course of the interview logic would ask “What is your favorite animal?” as the first question. However, everyone knows that the first question you should ask of a child is “How old are you?” The `need` specifier indicates that before **docassemble** should even try to present the “Thank you for that information” screen, it should ensure that `number_of_years_old` old is defined, then ensure that `favorite_animal`, and then try to present the screen.
The variables listed in a `need` specifier do not have to actually be used by the question. Also, if your question uses variables that are not mentioned in the `need` list, **docassemble** will still pursue definitions of those variables.
If any of the variables listed under `need` are undefined, **docassemble** will obtain their definitions before processing the content of the [`question`](https://docassemble.org/docs/questions.html#question)
. For example, suppose you have the following [`question`](https://docassemble.org/docs/questions.html#question)
:
need:
- favorite_fruit
question: |
Your favorite apple is ${ favorite_apple }.
continue button field: fruit_verified
If both `favorite_fruit` and `favorite_apple` are undefined, the definition of `favorite_fruit` will be sought first.
What if you wanted `favorite_fruit` to be sought **after** `favorite_apple`? To do this, you can use the following special form of `need`:
need:
post:
- favorite_fruit
question: |
Your favorite apple is ${ favorite_apple }.
continue button field: fruit_verified
In this case, the definition of `favorite_fruit` will be sought after all of the prerequisites of the `question` have been satisfied.
You can organize your `need` items into `pre` and `post` items:
need:
pre:
- favorite_vegetable
post:
- favorite_fruit
You can also include `post` among a list of other items:
need:
- favorite_vegetable
- post:
- favorite_fruit
`depends on`[¶](https://docassemble.org/docs/logic.html#depends%20on)
----------------------------------------------------------------------
The `depends on` specifier indicates that if the listed variables change, the results of the [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html)
block should be invalidated.
question: |
What is A?
fields:
- A: a
datatype: number
---
question: |
What is the square of ${ a }?
fields:
- B: b
datatype: number
depends on:
- a
---
code: |
c = a + b
depends on:
- a
- b
---
event: review_screen
skip undefined: False
question: |
Review your answers.
review:
- label: Edit A
field: a
button: |
A is ${ a }.
- label: Edit B
field: b
button: |
B is ${ b }.
- note: |
C is ${ c }.
---
mandatory: True
code: |
need(a, b, c)
review_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/depends-on.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/depends-on.yml&reset=1 "Click to try this interview")
In this example, if the user goes through the interview to the end, but then edits `a`, then if and when `a` is changed to a different value, `c` and `b` will be undefined. The original value of `b` will be remembered, so that when the interview logic asks the question to define `b`, the original value will be presented as a default. When `a` is set, `c` is also undefined, so that when the interview logic requires a definition of `c`, the [`code`](https://docassemble.org/docs/code.html)
block will be run to recompute the value of `c`.
If the user goes through the interview and then edits `b`, a change in `b` will trigger the invalidation of `c`.
The `depends on` specifier will also cause variables to be invalidated when they are changed by a [`code`](https://docassemble.org/docs/code.html)
block.
question: |
What is A?
fields:
- A: a
datatype: number
---
code: |
b = a * a
depends on:
- a
---
code: |
c = a + b
depends on:
- a
- b
---
event: review_screen
skip undefined: False
question: |
Review your answers.
review:
- label: Edit A
field: a
button: |
A is ${ a }.
- note: |
B is ${ b }.
- note: |
C is ${ c }.
---
mandatory: True
code: |
need(a, b, c)
review_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/depends-on-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/depends-on-code.yml&reset=1 "Click to try this interview")
In this interview, the variable `b` is set by a [`code`](https://docassemble.org/docs/code.html)
block. If the user edits `a` to a different value, the `depends on` specifier on the [`code`](https://docassemble.org/docs/code.html)
block causes the [`code`](https://docassemble.org/docs/code.html)
block to be re-run. The change in `b` causes the value of `c` to be invalidated. As a result, `c` is automatically updated when `a` changes.
Note that the `depends on` specifier results in invalidation when a variable is changed, not when it is defined. If a variable is undefined and is then defined, this is not considered a change for purposes of the `depends on` specifier. If a user presses Continue on a screen but does not change the value of a variable, the `depends on` logic is not triggered.
The `depends on` specifier can be used with iterator variables.
objects:
- people: DAList.using(object_type=Individual, there_are_any=True, complete_attribute='complete')
---
question: |
How much does ${ people[i] }
get paid per pay period?
fields:
- Amount: people[i].income
datatype: currency
depends on:
- people[i].pay_period
---
question: |
How frequently does
${ people[i] } get paid?
fields:
- no label: people[i].pay_period
datatype: number
choices:
- Monthly: 12.0
- Biweekly: 26.0
- Semi-monthly: 24.0
- Weekly: 52.0
---
code: |
people[i].annual_income = people[i].income * people[i].pay_period
depends on:
- people[i].pay_period
- people[i].income
---
question: |
What is the name of the
${ ordinal(i) } person?
fields:
- First name: people[i].name.first
- Last name: people[i].name.last
---
code: |
people[i].name.first
people[i].annual_income
people[i].complete = True
---
question: |
Is there another person?
yesno: people.there_is_another
---
field: review_screen
question: Review
subquestion: |
${ people.table }
${ people.add_action() }
---
table: people.table
rows: people
columns:
- Name: row_item.name
- Annual income: currency(row_item.annual_income)
edit:
- name.first
- pay_period
---
event: final_screen
question: Summary
subquestion: |
% for person in people:
${ person }
makes
${ currency(person.income) }
${ nice_number(person.pay_period) }
times per year, for an annual income of
${ currency(person.annual_income) }.
% endfor
[Review](${ url_action('review_screen') })
---
mandatory: True
code: |
people.gather()
people.table
final_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/depends-on-iterator.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/depends-on-iterator.yml&reset=1 "Click to try this interview")
In this interview, the “Edit” button on the table only triggers the asking of the `.pay_period` question, but the `depends on` logic will cause the `.income` question to be re-asked, and the `.annual_income` amount to be re-calculated, if the `.pay_period` answer changes.
In situations where variables that are part of a list need to be invalidated when a variable that is not part of the same list item changes, the [`on change`](https://docassemble.org/docs/initial.html#on%20change)
block can be used.
`reconsider`[¶](https://docassemble.org/docs/logic.html#reconsider)
--------------------------------------------------------------------
The `reconsider` modifier can be used in two ways: it can be set to a list of variables, or it can be set to `True`.
### Effect when set to a list of variable names
When you set `reconsider` to a list of variable names, then before the `question` is asked, the variables will be undefined (if they are defined at all), and then the definition of each variable will be sought again.
reconsider:
- minutes_since_world_series
question: |
It has been ${ minutes_since_world_series } minutes since
your team won the world series. Have you gotten over
your excitement yet?
yesno: gotten_over_excitement
This can be useful when your [`question`](https://docassemble.org/docs/questions.html#question)
refers to a computed variable that might have become out-of-date since the last time it was computed.
### Effect when set to `True`
If `reconsider` is set to `True` on a [`code`](https://docassemble.org/docs/code.html)
block, then **docassemble** will always “reconsider” the values of any of the variables set by the block. That is, every time the interview is assembled (every time the screen loads) **docassemble** will forget about the value of any of the variables set by the [`code`](https://docassemble.org/docs/code.html)
block.
You will want to set `reconsider` to `True` if your interview flow is such that you want **docassemble** to reconsider its definition of a variable based on information that might be gathered in the future.
For example, see if you can find the problem with the interview below.
---
code: |
cat_food_cans_needed = number_of_cats * 4
---
question: |
Does your neighbor's cat sometimes eat at your house?
subquestion: |
To feed your own cat, you will need ${ cat_food_cans_needed } cans
of cat food, but you might need more for your neighbor's cat.
buttons:
- "Yes":
code: |
number_of_cats = number_of_cats + 1
has_neighboring_cat = True
- "No":
code: |
has_neighboring_cat = False
---
question: How many cats do you have?
fields:
- Cats: number_of_cats
datatype: integer
---
question: |
To feed your cat
% if has_neighboring_cat:
and your neighbor's cat
% endif
you will need to buy ${ cat_food_cans_needed } cans of cat food.
sets: all_done
---
mandatory: True
code: all_done
The problem with this interview is that it will compute the number of cans of cat food needed when it says “To feed your own cat, you will need . . . cans of cat food,” but it will not increase the number of cans of cat food to account for later-acquired information (i.e. the fact that the neighbor’s cat comes over). Once `cat_food_cans_needed` has been defined once, **docassemble** will continue to use that definition whenever the interview calls for the definition of `cat_food_cans_needed`.
This problem can be fixed by adding `reconsider: True` to the [`code`](https://docassemble.org/docs/code.html)
block:
---
code: |
cat_food_cans_needed = number_of_cats * 4
reconsider: True
---
The `reconsider` modifier tells **docassemble** to always reconsider the variables in the [`code`](https://docassemble.org/docs/code.html)
block. When the final screen comes up, **docassemble** will have forgotten about the earlier-defined value of `cat_food_cans_needed` and will therefore re-define the value by re-running the [`code`](https://docassemble.org/docs/code.html)
block.
code: |
cat_food_cans_needed = number_of_cats * 4
reconsider: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/reconsider.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/reconsider.yml&reset=1 "Click to try this interview")
The `reconsider` modifier is particularly important to use when you allow interviewees to go back and modify past answers using a [`review`](https://docassemble.org/docs/fields.html#review)
block. For more information about how to implement such features, see [`review`](https://docassemble.org/docs/fields.html#review)
, [`event`](https://docassemble.org/docs/fields.html#event)
, [`url_action()`](https://docassemble.org/docs/functions.html#url_action)
, [`process_action()`](https://docassemble.org/docs/functions.html#process_action)
, [`action_menu_item()`](https://docassemble.org/docs/functions.html#action_menu_item)
, and [`menu_items`](https://docassemble.org/docs/special.html#menu_items)
.
**docassemble** also offers the [`reset` initial block](https://docassemble.org/docs/initial.html#reset)
, which has the same effect as the `reconsider` modifier, but using a different way of specifying which variables should be reconsidered. Whether you use the [`reset` initial block](https://docassemble.org/docs/initial.html#reset)
or the `reconsider` modifier is a question of what you consider to be more convenient and/or readable.
`undefine`[¶](https://docassemble.org/docs/logic.html#undefine)
----------------------------------------------------------------
When you set `undefine` to a list of variable names, then before the `question` is asked, the variables will be undefined.
undefine:
- favorite_foods
question: |
What is your favorite fruit?
fields:
- Favorite fruit: favorite_fruit
---
code: |
favorite_foods = [favorite_vegetable, favorite_fruit]
---
mandatory: True
question: |
Your favorite foods are
${ comma_and_list(favorite_foods) }.
This can be useful when you allow users to change their answers using review screens. Sometimes a change to one variable will invalidate answers to other [`question`](https://docassemble.org/docs/questions.html#question)
s, or to computations made by [`code`](https://docassemble.org/docs/code.html)
blocks.
Combining multiple interviews into one[¶](https://docassemble.org/docs/logic.html#multiple%20interviews)
=========================================================================================================
Using an umbrella YAML file[¶](https://docassemble.org/docs/logic.html#multiple%20interviews%20umbrella)
---------------------------------------------------------------------------------------------------------
If you have multiple interviews and you want the user to choose which interview to run, you could offer the multiple interviews as a single interview, where there is an “umbrella” [YAML](https://en.wikipedia.org/wiki/YAML)
file that [`include`](https://docassemble.org/docs/initial.html#include)
s the others.
For example:
include:
- interview-fruit.yml
- interview-vegetables.yml
- interview-flowers.yml
---
question: |
What interview would you like to do?
field: interview_choice
choices:
- Fruit
- Vegetables
- Flowers
---
mandatory: true
code: |
if interview_choice == 'Fruit':
interview_fruit_done
elif interview_choice == 'Vegetables':
interview_vegetables_done
elif interview_choice == 'Flowers':
interview_flowers_done
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/umbrella-interview.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/umbrella-interview.yml&reset=1 "Click to try this interview")
Note that this interview [`include`](https://docassemble.org/docs/initial.html#include)
s three separate [YAML](https://en.wikipedia.org/wiki/YAML)
files. The controlling logic is the [`code`](https://docassemble.org/docs/code.html)
block in the “umbrella” interview that pursues a different endpoint depending on the value of `interview_choice`.
The three interview files included are:
* [interview-fruit.yml](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/interview-fruit.yml)
* [interview-vegetables.yml](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/interview-vegetables.yml)
* [interview-flowers.yml](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/interview-flowers.yml)
Note that these interview files contain everything needed for the interview except for any [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
blocks that would define an interview endpoint; that function is reserved for the “umbrella” interview.
Using hyperlinks[¶](https://docassemble.org/docs/logic.html#multiple%20interviews%20links)
-------------------------------------------------------------------------------------------
There are other ways to offer users a choice of interviews. For example, you can use the [`interview_url()`](https://docassemble.org/docs/functions.html#interview_url)
function with the `i` optional keyword parameter to point users from one interview to another:
mandatory: true
question: |
What interview would you
like to use?
subquestion: |
* [Fruit]
* [Vegetables]
* [Flowers]
[Fruit]: ${ interview_url(i='docassemble.demo:data/questions/interview-about-fruit.yml') }
[Vegetables]: ${ interview_url(i='docassemble.demo:data/questions/interview-about-vegetables.yml') }
[Flowers]: ${ interview_url(i='docassemble.demo:data/questions/interview-about-flowers.yml') }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/interview-url-refer.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/interview-url-refer.yml&reset=1 "Click to try this interview")
You might also offer these hyperlinks in the menu, using the [`menu_items`](https://docassemble.org/docs/special.html#menu_items)
special variable:
mandatory: True
code: |
menu_items = [ {'url': interview_url(i='docassemble.demo:data/questions/interview-about-fruit.yml'),\
'label': 'Fruit'},\
{'url': interview_url(i='docassemble.demo:data/questions/interview-about-vegetables.yml'),\
'label': 'Vegetables'}, \
{'url': interview_url(i='docassemble.demo:data/questions/interview-about-flowers.yml'),\
'label': 'Flowers'} ]
---
mandatory: True
question: |
Select an option from the
menu in the upper right.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/menu-items-refer.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/menu-items-refer.yml&reset=1 "Click to try this interview")
You can also use the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
configuration directive in combination with [`show dispatch link`](https://docassemble.org/docs/config.html#show%20dispatch%20link)
to allow the user to access a list of interviews available on your server by selecting “Available Interviews” from the menu.
A/B testing with redirects[¶](https://docassemble.org/docs/logic.html#multiple%20interviews%20redirect)
--------------------------------------------------------------------------------------------------------
The hyperlinks described in the previous subsection can also be used with the [`command()`](https://docassemble.org/docs/functions.html#command)
function to automatically redirect the user to a particular interview, for example for the purposes of A/B testing.
The following interview seamlessly redirects the user to either the [demo interview](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/questions.yml)
or the [example interview for the `redact()` function](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/redact-docx.yml)
, depending on a computational coin flip.
mandatory: true
code: |
import random
if random.random() < 0.5:
url = interview_url(i="docassemble.demo:data/questions/questions.yml")
else:
url = interview_url(i="docassemble.demo:data/questions/examples/redact-docx.yml")
command('exit', url=url)
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/ab-test.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/ab-test.yml&reset=1 "Click to try this interview")
The use of `'exit'` in the [`command()`](https://docassemble.org/docs/functions.html#command)
function is important here because it will cause this brief interview session to be deleted from the user’s list of interview sessions, since its sole purpose is to redirect the user.
An interview like this might also log some data for purposes of collecting metrics, perhaps using [Redis](https://docassemble.org/docs/functions.html#redis)
. In the interviews being A/B tested, metrics could be logged using [Redis](https://docassemble.org/docs/functions.html#redis)
or the [Google Analytics feature](https://docassemble.org/docs/config.html#google%20analytics)
.
Using multiple endpoints in a single interview[¶](https://docassemble.org/docs/logic.html#subinterview)
--------------------------------------------------------------------------------------------------------
Another way to offer an “interview inside an interview” is to populate variables and then delete them.
objects:
- user_global: Individual
- user: Individual
- endpoint: DADict
---
mandatory: True
code: |
while True:
del endpoint[user.goal]
del user
---
question: |
What interview would you like to complete?
field: user.goal
choices:
- Fruit quiz: fruit
- Vegetable quiz: vegetable
- Legume quiz: legume
---
code: |
user.name.first = user_global.name.first
user.name.last = user_global.name.last
---
code: |
user.age_category = user_global.age_category
---
question: |
What is your name?
fields:
- First name: user_global.name.first
- Last name: user_global.name.last
---
question: |
How old are you, ${ user }?
field: user_global.age_category
choices:
- "Spring chicken"
- "The music I like isn't cool anymore"
- "Wiser than most"
---
question: |
Fruit quiz results
subquestion: |
Your favorite fruit is
${ user.favorite_fruit }.
field: endpoint['fruit']
continue button label: Try again
---
question: |
Vegetable quiz results
subquestion: |
% if user.age_category == 'Spring chicken':
You are still growing, ${ user }, so you should eat lots of vegetables.
% endif
Your favorite vegetable is
${ user.favorite_vegetable }.
field: endpoint['vegetable']
continue button label: Try again
---
question: |
Legume quiz results
subquestion: |
% if user.knows_legumes:
${ user } definitely knows what a legume is.
% else:
You don't know what a legume is.
% endif
% if user.age_category == 'Spring chicken':
You are still growing, ${ user }, so you should eat lots of legumes.
% endif
Your favorite legume is
${ user.favorite_legume }.
field: endpoint['legume']
continue button label: Try again
---
question: |
What is your favorite fruit?
fields:
Favorite fruit: user.favorite_fruit
---
question: |
What is your favorite vegetable?
fields:
Favorite vegetable: user.favorite_vegetable
---
question: |
What is your favorite legume?
fields:
Favorite legume: user.favorite_legume
---
question: |
Which of these are legumes?
fields:
- no label: user.legume_picks
datatype: checkboxes
choices:
- Peas
- Lentils
- Mango
- String beans
- Potato
- Peanut
---
code: |
if user.legume_picks['Peas'] \
and user.legume_picks['Lentils'] \
and user.legume_picks['String beans'] \
and user.legume_picks['Peanut'] \
and not user.legume_picks['Mango'] \
and not user.legume_picks['Potato']:
user.knows_legumes = True
else:
user.knows_legumes = False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/interview_in_interview.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/interview_in_interview.yml&reset=1 "Click to try this interview")
The central logic of this interview is in the following [`code`](https://docassemble.org/docs/code.html)
block:
mandatory: True
code: |
while True:
del endpoint[user.goal]
del user
This is concise but cryptic, so it may be easier to understand what the interview is doing by writing out the variables for which [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
will seek definitions, in the order in which [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
will seek them:
mandatory: True
code: |
while True:
user.goal
endpoint[user.goal]
del endpoint[user.goal]
del user
First, the interview asks for the goal (`user.goal`) – whether the user wants do an interview about fruit, vegetables, or legumes.
Next, it seeks an endpoint for that goal – a variable like `endpoint['vegetable']`. This results in the “sub-interview” being conducted. Once that endpoint is reached (e.g., when `endpoint['vegetable']` is set to `True` by the final question of the “sub-interview”), then the variables `endpoint['vegetable']` and `user` are deleted (using the [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
`del` statement). Then the logic loops back around to where it began. At this point, `user.goal` will be undefined, because the entire variable `user` had been deleted. So the user will be presented with the “fruit, vegetable, or legume” choice again, and can choose to repeat the same “sub-interview,” or start a different “sub-interview.”
Note that an interview like this is different from an interview that concludes with a [restart button](https://docassemble.org/docs/questions.html#special%20buttons)
. While a [restart button](https://docassemble.org/docs/questions.html#special%20buttons)
wipes out all of the user’s answers, this interview retains some of the information that was gathered. It does so by using two objects to track information about the user: information that is permanent is stored in the `user_global` object, and information that is temporary is stored in the `user` object.
Note that the interview developer only uses the object `user` when writing [`question`](https://docassemble.org/docs/questions.html#question)
s that refer to characteristics of the user. The following [`code`](https://docassemble.org/docs/code.html)
blocks assert that information about the `user`’s name and age should by defined by reference to attributes of the `user_global` object:
code: |
user.name.first = user_global.name.first
user.name.last = user_global.name.last
---
code: |
user.age_category = user_global.age_category
This means that whenever the interview needs the definition of `user.name.first`, it will actually seek out `user_global.name.first`. If the user has been asked for their name before, no question needs to be asked; the [`code`](https://docassemble.org/docs/code.html)
will take care of defining `user.name.first` and `user.name.last`. But other attributes, like `user.favorite_fruit`, are lost when the interview logic does `del user`. As a result, the interview will remember some answers and forget others.
Best practices for interview logic and organization[¶](https://docassemble.org/docs/logic.html#bplogic)
========================================================================================================
* Use only a single [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
[`code`](https://docassemble.org/docs/code.html)
block for each interview, and put it at the top of the file after the [initial blocks](https://docassemble.org/docs/initial.html)
.
Best practices for sharing with others[¶](https://docassemble.org/docs/logic.html#bpsharing)
=============================================================================================
* Don’t reinvent the wheel; [`include`](https://docassemble.org/docs/initial.html#include)
other people’s questions.
* Share your [`question`](https://docassemble.org/docs/questions.html#question)
s, [`code`](https://docassemble.org/docs/code.html)
, and [`template`](https://docassemble.org/docs/initial.html#template)
s with others.
* To that end, keep your [`question`](https://docassemble.org/docs/questions.html#question)
blocks in a separate [YAML](https://en.wikipedia.org/wiki/YAML)
file from your [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
interview logic, so that other people can incorporate your questions without having to edit your work. Your main interview file would consist only of:
* A [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block saying who you are and what your interview is for;
* A block to [`include`](https://docassemble.org/docs/initial.html#include)
your file of questions;
* Any [`interview help`](https://docassemble.org/docs/initial.html#interview%20help)
blocks;
* A [`default role`](https://docassemble.org/docs/initial.html#default%20role)
block, if you use [roles](https://docassemble.org/docs/roles.html)
;
* Any [`initial`](https://docassemble.org/docs/logic.html#initial)
code;
* Your [`mandatory`](https://docassemble.org/docs/logic.html#mandatory)
code or questions that set your interview in motion.
* [`include`](https://docassemble.org/docs/initial.html#include)
other people’s question files directly from their **docassemble** packages, rather than by copying other people’s files into your package. That way, when the other developers make improvements to their questions, you can gain the benefit of those improvements automatically.
* Don’t invent your own scheme for variable names; follow conventions and replicate what other people are doing.
* If other people are including your questions and code, avoid changing your variable names unnecessarily, or else you will “break” other people’s interviews. This does limit your autonomy somewhat, but the benefits for the community of interview developers more than make up for the loss of autonomy.
* * *
---
# Initial blocks
[Edit](https://github.com/jhpyle/docassemble/blob/gh-pages/_docs/initial.md "Edit this page on GitHub")
Initial blocks
==============
Contents
--------
* [1 Interview title and other metadata](https://docassemble.org/docs/initial.html#metadata)
* [1.1 Effect of multiple metadata blocks](https://docassemble.org/docs/initial.html#overlapping%20metadata)
* [2 Creating objects](https://docassemble.org/docs/initial.html#objects)
* [2.1 Importing objects from file](https://docassemble.org/docs/initial.html#objects%20from%20file)
* [3 Incorporation by reference: include](https://docassemble.org/docs/initial.html#include)
* [4 Images](https://docassemble.org/docs/initial.html#im)
* [4.1 With attribution: image sets](https://docassemble.org/docs/initial.html#image%20sets)
* [4.2 Without attribution: images](https://docassemble.org/docs/initial.html#images)
* [5 Python modules](https://docassemble.org/docs/initial.html#mods)
* [5.1 Importing the module itself: imports](https://docassemble.org/docs/initial.html#imports)
* [5.2 Importing all names in a module: modules](https://docassemble.org/docs/initial.html#modules)
* [6 Storing structured data in a variable](https://docassemble.org/docs/initial.html#data)
* [6.1 Structured data in object form](https://docassemble.org/docs/initial.html#use%20objects)
* [7 Storing structured data in a variable using code](https://docassemble.org/docs/initial.html#data%20from%20code)
* [7.1 Structured data from code in object form](https://docassemble.org/docs/initial.html#use%20objects%20from%20code)
* [8 Keeping variables fresh: reset](https://docassemble.org/docs/initial.html#reset)
* [9 Running code when a variable changes: on change](https://docassemble.org/docs/initial.html#on%20change)
* [10 Changing order of precedence](https://docassemble.org/docs/initial.html#order)
* [11 Vocabulary terms and auto terms](https://docassemble.org/docs/initial.html#terms)
* [12 The template block](https://docassemble.org/docs/initial.html#template)
* [13 The table block](https://docassemble.org/docs/initial.html#table)
* [13.1 Exporting tables to Excel and other formats](https://docassemble.org/docs/initial.html#export)
* [13.2 Converting tables to a pandas dataframe](https://docassemble.org/docs/initial.html#as_df)
* [13.3 Using tables to edit groups](https://docassemble.org/docs/initial.html#groups%20edit)
* [13.4 Sorting and filtering items in a table](https://docassemble.org/docs/initial.html#sort%20key)
* [14 Defining the sections for the navigation bar](https://docassemble.org/docs/initial.html#sections)
* [15 Assisting users with interview help](https://docassemble.org/docs/initial.html#interview%20help)
* [16 Mako functions: def](https://docassemble.org/docs/initial.html#def)
* [17 Setting the default role](https://docassemble.org/docs/initial.html#default%20role)
* [18 Setting the default language](https://docassemble.org/docs/initial.html#default%20language)
* [19 Translation files](https://docassemble.org/docs/initial.html#translations)
* [20 Default screen parts](https://docassemble.org/docs/initial.html#default%20screen%20parts)
* [21 Custom validation messages](https://docassemble.org/docs/initial.html#default%20validation%20messages)
* [22 Machine learning training data](https://docassemble.org/docs/initial.html#machine%20learning%20storage)
* [23 Optional features](https://docassemble.org/docs/initial.html#features)
* [23.1 Whether debugging features are available](https://docassemble.org/docs/initial.html#debug)
* [23.2 Whether interview is centered](https://docassemble.org/docs/initial.html#centered)
* [23.3 Widening the screen when right is used](https://docassemble.org/docs/initial.html#wide%20side%20by%20side)
* [23.4 Progress bar](https://docassemble.org/docs/initial.html#progress%20bar)
* [23.5 Navigation bar](https://docassemble.org/docs/initial.html#navigation%20bar)
* [23.6 Back button style](https://docassemble.org/docs/initial.html#question%20back%20button)
* [23.7 Help tab style](https://docassemble.org/docs/initial.html#question%20help%20button)
* [23.8 Positioning labels above fields](https://docassemble.org/docs/initial.html#labels%20above%20fields)
* [23.9 Floating labels](https://docassemble.org/docs/initial.html#floating%20labels)
* [23.10 Suppress autofill](https://docassemble.org/docs/initial.html#suppress%20autofill)
* [23.11 Hiding the standard menu items](https://docassemble.org/docs/initial.html#hide%20standard%20menu)
* [23.12 Hiding the menu and login interface entirely](https://docassemble.org/docs/initial.html#hide%20corner%20interface)
* [23.13 Javascript and CSS files](https://docassemble.org/docs/initial.html#javascript)
* [23.14 Disable analytics](https://docassemble.org/docs/initial.html#disable%20analytics)
* [23.15 Bootstrap theme](https://docassemble.org/docs/initial.html#bootstrap%20theme)
* [23.16 Inverted Bootstrap navbar](https://docassemble.org/docs/initial.html#inverse%20navbar)
* [23.17 Hiding the navbar](https://docassemble.org/docs/initial.html#hide%20navbar)
* [23.18 Width of tables in attachments](https://docassemble.org/docs/initial.html#table%20width)
* [23.19 Disabling document caching](https://docassemble.org/docs/initial.html#cache%20documents)
* [23.20 Using pdftk to fill in PDF form fields](https://docassemble.org/docs/initial.html#pdftk)
* [23.21 Producing PDF/A files](https://docassemble.org/docs/initial.html#pdfa)
* [23.22 Limiting size of uploaded images](https://docassemble.org/docs/initial.html#maximum%20image%20size)
* [23.23 Converting the format of uploaded images](https://docassemble.org/docs/initial.html#image%20upload%20type)
* [23.24 Going full screen when interview is embedded](https://docassemble.org/docs/initial.html#go%20full%20screen)
* [23.25 Infinite loop protection](https://docassemble.org/docs/initial.html#loop%20limit)
* [23.26 Customizing buttons on review pages](https://docassemble.org/docs/initial.html#review%20button%20color)
* [23.27 Enabling catchall blocks](https://docassemble.org/docs/initial.html#use%20catchall)
* [23.28 Default date limits](https://docassemble.org/docs/initial.html#default%20date%20min)
* [23.29 Whether data about the question should be sent to the browser](https://docassemble.org/docs/initial.html#send%20question%20data)
* [23.30 Popover trigger](https://docassemble.org/docs/initial.html#popover%20trigger)
* [23.31 Loading custom datatypes](https://docassemble.org/docs/initial.html#custom%20datatypes%20to%20load)
* [23.32 Jinja2 filters to apply automatically](https://docassemble.org/docs/initial.html#auto%20jinja%20filter)
This section discusses blocks that will typically appear at the beginning of the [YAML](https://en.wikipedia.org/wiki/YAML)
of your [interview](https://docassemble.org/docs/interviews.html)
.
If you are new to **docassemble**, you probably will not need to use “initial blocks” until you attempt something more advanced, so you can skip this section and proceed to the [section on questions](https://docassemble.org/docs/questions.html)
.
Interview title and other `metadata`[¶](https://docassemble.org/docs/initial.html#metadata)
============================================================================================
---
metadata:
title: |
Advice on Divorce
short title: |
Divorce
description: |
A divorce advice interview
authors:
- name: John Doe
organization: Example, Inc.
revision_date: 2015-09-28
---
A `metadata` block contains information about the interview, such as the name of the author. It must be a [YAML](https://en.wikipedia.org/wiki/YAML)
dictionary, but each the dictionary items can contain any arbitrary [YAML](https://en.wikipedia.org/wiki/YAML)
structure.
If a `title` is defined, it will be displayed in the main navigation bar in the web app.
If a `short title` is provided, it will be displayed in place of the `title` when the size of the screen is small. It is important to specify a `short title` if your `title` is more than a few words long, because otherwise the navigation bar may not look good on small screens.
If a `logo` is defined, it will be displayed in the navigation bar in the web app in place of the `title` and `short title`. The content of the `logo` should be raw [HTML](https://en.wikipedia.org/wiki/HTML)
. The [HTML](https://en.wikipedia.org/wiki/HTML)
will be placed inside of a `` element, so make sure you only use “inline” HTML elements that are compatible with being placed inside of a ``. You can [provide CSS](https://docassemble.org/docs/initial.html#css)
to the interview and reference [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
classes in your `logo`. If you include an image, you should size it to be about 20 pixels in height. If the logo is too tall, the navigation bar will expand to fit, and you will need to [adjust the CSS](https://docassemble.org/docs/config.html#bootstrap%20theme)
to specify different values for `.da-pad-for-navbar` and `.da-top-for-navbar`. If you include a `logo` that is much wider than 100 pixels, you should also specify a `short logo` that is not as wide, so your navigation bar will look good on small screens. Make sure that your raw [HTML](https://en.wikipedia.org/wiki/HTML)
does not contain any errors, or else the formatting of the page will become corrupted.
If a `short logo` is defined, it will be used in place of the `logo` on small screens. Although you may develop and test your interview on a monitor, many of your users will likely use your interviews on a smartphone, so it is important that you keep the needs of smartphone users in mind. Smartphone users want to use the interview you developed and they don’t want your logo taking up half of their screen.
If `navigation bar html` is provided, the contents will be inserted into the main navigation bar. It will be inserted into a `
` element, so for best results, the [HTML](https://en.wikipedia.org/wiki/HTML)
should consist of one or more items in a form like `
`. For more information about how these [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
classes work, see the documentation for the [Bootstrap Navbar](https://getbootstrap.com/docs/5.2/components/navbar/#supported-content)
. On small screens, the HTML is shown in the menu when the user clicks the toggler icon. You may wish to use `d-none d-md-block` classes to hide the HTML when the screen is small. Make sure your HTML does not contain any errors, or else the [HTML](https://en.wikipedia.org/wiki/HTML)
of the entire screen could be affected.
If a `tab title` is provided, it will be displayed as the title of the browser tab. Otherwise, the `title` will be used.
If a `subtitle` is provided, it will be displayed as the subtitle of the interview in the “Interviews” list available to a logged-in user at `/interviews`.
If a `title url` is provided, clicking on the title will open the given URL. Otherwise, the default behavior is that clicking the title does nothing except that when the user is on a help screen, clicking the title takes the user back to the question.
If you provide a `title url` and you do not want the URL to open in another browser window or tab, set `title url opens in other window` to `False`. The default is that the link does open another window or tab.
If a `date format` is provided, this will be used as the default date format when the [`format_date()`](https://docassemble.org/docs/functions.html#format_date)
function is called, or the [`.format_date()`](https://docassemble.org/docs/objects.html#DADateTime.format_date)
method of the [`DADateTime`](https://docassemble.org/docs/objects.html#DADateTime)
object is called (which is used when [`DADateTime`](https://docassemble.org/docs/objects.html#DADateTime)
objects are reduced to text).
If a `datetime format` is provided, this will be used as the default date/time format when the [`format_datetime()`](https://docassemble.org/docs/functions.html#format_datetime)
function is called, or the [`.format_datetime()`](https://docassemble.org/docs/objects.html#DADateTime.format_datetime)
method of the [`DADateTime`](https://docassemble.org/docs/objects.html#DADateTime)
object is called.
If a `time format` is provided, this will be used as the default time format when the [`format_time()`](https://docassemble.org/docs/functions.html#format_time)
function is called, or the [`.format_time()`](https://docassemble.org/docs/objects.html#DADateTime.format_time)
method of the [`DADateTime`](https://docassemble.org/docs/objects.html#DADateTime)
object is called.
These values can be overridden using the [`set_parts()` function](https://docassemble.org/docs/functions.html#set_parts)
.
The `metadata` block and the [`set_parts()` function](https://docassemble.org/docs/functions.html#set_parts)
can be used to modify other aspects of the main navigation bar.
If an `exit link` is provided, the behavior of the “Exit” link can be modified. (The “Exit” menu option is displayed when the [`show login` configuration directive](https://docassemble.org/docs/config.html#show%20login)
is set to `False` or the [`show login` metadata specifier](https://docassemble.org/docs/initial.html#show%20login)
in an interview is set to `False`.) The value can be either `exit`, `leave,` or `logout`. If it is `exit`, then when the user clicks the link, their interview answers will be deleted from the server, and the user will be redirected to the `exit url` (see below). If `exit link` is `leave`, the user will be redirected to the `exit url`, and their interview answers will remain intact. (It can be important to keep the interview answers on the server if [background tasks](https://docassemble.org/docs/background.html#background)
are still running.) If `exit link` is `logout`, then if the user is logged in, the user will be logged out and then redirected to the `exit url`, but if the user is not logged in, this will have the same effect as `leave`. If `exit link` is `exit_logout`, the user’s interview answers will be deleted, they will be logged out if they are logged in, and they will be redirected to the `exit url`.
If an `exit url` is provided, the user will be redirected to the given URL when they click the “Exit” link. If no `exit url` is provided, the user will be directed to the [`exitpage`](https://docassemble.org/docs/config.html#exitpage)
. The `exit url` also functions as an interview-level default value in place of the system-wide [`exitpage`](https://docassemble.org/docs/config.html#exitpage)
, which is used by the [`command()`](https://docassemble.org/docs/functions.html#command)
function and used on [special pages](https://docassemble.org/docs/questions.html#special%20buttons)
that show `buttons` or `choices` that allows users to `exit` or `leave`.
If `exit label` is provided, the given text will be used in place of the word “Exit” on the “Exit” menu option.
If you set `unlisted: True` for an interview that has an entry in the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
list in your [configuration](https://docassemble.org/docs/config.html)
, the interview will be exempted from display in the list of interviews available at `/list`. For more information about this, see the documentation for the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
configuration directive.
If you set `hidden: True`, then interview sessions for this interview will be omitted from the “My Interviews” listing of sessions. (They will still be deleted by the “Delete All” button, though.)
You can set `tags` to a list of one or more “tags” as a way of categorizing the interview.
metadata:
title: Write your will
tags:
- estates
- wills
The list of available interviews at `/list` and the list of interview sessions at `/interviews` make use of the metadata `tags` for filtering purposes. Note that the `metadata` of an interview are static, while the tags of a particular session of an interview are dynamic, and can be changed with [`session_tags()`](https://docassemble.org/docs/functions.html#session_tags)
.
If you set `sessions are unique` to `True`, then **docassemble** will resume an existing session for the user, if the user already has an existing session. This requires that the user be logged in, so the user will be redirected to the login screen if they try to access an interview for which `sessions are unique` is set to `True`. You can also set `sessions are unique` to a list of roles, in which case uniqueness will be enforced only if the user has one of the listed roles.
If you set `temporary session` to to `True`, then **docassemble** will delete any existing sessions that the user already has and start a new session.
If you set `required privileges` to a list of one or more privileges, then a user will only be able to use the interview if they have one of the given privileges. If `anonymous` is included as one of the required privileges, then users who are not logged in will be able to use the interview. However, note that `anonymous` is not actually a [privilege](https://docassemble.org/docs/users.html)
in **docassemble**’s [privilege](https://docassemble.org/docs/users.html)
management system; only logged-in users actually have [privileges](https://docassemble.org/docs/users.html)
. If no `required privileges` are listed, then the default is that the interview can be used by anybody.
metadata:
title: Administrative interview
short title: Admin
description: |
A management dashboard
sessions are unique: True
required privileges:
- admin
- developer
- advocate
If there are multiple [`metadata`](https://docassemble.org/docs/initial.html#metadata)
blocks in the [YAML](https://en.wikipedia.org/wiki/YAML)
of an interview that set `required privileges`, the `required privileges` settings of later [`metadata`](https://docassemble.org/docs/initial.html#metadata)
blocks will override the `required privileges` settings of earlier [`metadata`](https://docassemble.org/docs/initial.html#metadata)
blocks. Setting `required privileges: []` will ensure that the interview can be used, notwithstanding the `required privileges` settings of any earlier [`metadata`](https://docassemble.org/docs/initial.html#metadata)
blocks.
The `required privileges for listing` metadata specifier is like `required privileges`, except it only controls whether the interview will be shown in the list of interviews available at `/list`. The `required privileges` metadata specifier also controls whether the interview will be listed. For more information about the `/list` page, see the documentation for the [`dispatch`](https://docassemble.org/docs/config.html#dispatch)
configuration directive.
The `required privileges for initiating` metadata specifier controls whether the user is allowed to initiate a new session. If you set `required privileges for initiating: admin`, then only users with the `admin` privilege will be able to start a new session by visiting the URL for the interview. You may want to use this if you use the [API](https://docassemble.org/docs/api.html)
to initiate sessions and you want the [API](https://docassemble.org/docs/api.html)
to be the exclusive means of initiating a new session. If you set `required privileges for initiating: None`, then not even the administrator will be able to initiate a session of the interview (except by using the [API](https://docassemble.org/docs/api.html)
).
If you set `require login` to `True`, then a non-logged-in user will be denied access to the interview. However, if `anonymous` is one of the `required privileges`, a non-logged-in user will be able to access the interview regardless of this setting.
You can set an `error action` if you want your interview to do something substantive in the event that your interview encounters an error that it would otherwise show to the user.
A simple application of `error action` would be to replace the error screen with a [`question`](https://docassemble.org/docs/questions.html#question)
:
metadata:
error action: on_error
---
event: on_error
question: |
Sorry, we have encountered an error.
buttons:
- Exit: exit
url: https://docassemble.org
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/error-action.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/error-action.yml&reset=1 "Click to try this interview")
When the interview encounters an error, the interview will run the [action](https://docassemble.org/docs/functions.html#actions)
given by `error action`. In this case, `error action` is `on_error`, and calling this [action](https://docassemble.org/docs/functions.html#actions)
shows a [`question`](https://docassemble.org/docs/questions.html#question)
to the user. If you would like to use the error message in the [action](https://docassemble.org/docs/functions.html#actions)
, you can access it by calling `action_argument('error_message')`. The “How question came to be asked” explanation can be obtained by calling `action_argument('error_history')`. The result will be `None` if the server is not a development server, or if the history is unavailable for some reason. The error trace can be obtained by calling `action_argument('error_trace')`.
An [action](https://docassemble.org/docs/functions.html#actions)
can also run code that changes the interview logic. For example, an `error action` could skip through the remainder of the questions and present a final screen:
metadata:
error action: on_error
---
event: on_error
code: |
healthy = False
---
mandatory: True
code: |
if not healthy:
fail_safe
favorite_fruit
favorite_vegetable
favorite_number
final_screen
---
event: fail_safe
code: |
if not defined('favorite_fruit'):
favorite_fruit = '_________'
if not defined('favorite_vegetable'):
favorite_vegetable = '_________'
if not defined('favorite_number'):
favorite_number = '____'
final_screen
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/error-action-2.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/error-action-2.yml&reset=1 "Click to try this interview")
If the attempt to run the error action also results in an error, the latter error is shown on the screen in the usual fashion.
See [`error help`](https://docassemble.org/docs/initial.html#error%20help)
and [`verbose error messages`](https://docassemble.org/docs/config.html#verbose%20error%20messages)
for other ways to customize error messages.
The [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block also accepts specifiers for default content to be inserted into various parts of the screen.
metadata:
title: Title
short title: Short title
subtitle: |
This is the subtitle part.
pre: |
This is the pre part.
submit: |
This is the submit part.
post: |
This is the post part.
under: |
This is the under part.
right: |
This is the right part.
exit link: leave
exit label: |
Exit label
help label: |
Help label
help button color: warning
continue button label: |
Continue button label
continue button color: success
resume button label: |
Resume button label
resume button color: info
back button label: |
Back button label
back button color: danger
footer: |
This is the footer part.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/metadata-screen-parts.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/metadata-screen-parts.yml&reset=1 "Click to try this interview")
You can provide different values for different languages by setting each directive to a dictionary in which the keys are languages and the values are content.
metadata:
post:
"*": |
This interview was sponsored in part by a grant from the Example Foundation.
"es": |
Esta entrevista fue patrocinada en parte por una beca de la Fundación Ejemplo.
In this example, the text indicated by `"*"` will be used as the `post` screen part with `question` blocks that have no `language`. The text indicated by `"es"` will will be used as the `post` screen part with `question` blocks that are modified with `language: es`.
For information about other ways to set defaults for different parts of the screens during interviews, see the [screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
section.
The [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block also accepts the specifier `error help`. This is [Markdown](https://daringfireball.net/projects/markdown/)
\-formatted text that will be included on any error screen that appears to the user during the interview. You can also provide this text on a server-wide basis using the [`error help`](https://docassemble.org/docs/initial.html#error%20help)
directive in the [Configuration](https://docassemble.org/docs/config.html)
.
metadata:
error help: |
We are sorry.
An error has occurred.
Please contact
[the administrator](mailto:admin@example.com).
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/error-help.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/error-help.yml&reset=1 "Click to try this interview")
To support multiple languages, you can set `error help` to a dictionary where the keys are language codes and the values are the error text to be shown:
metadata:
error help:
en: |
We are sorry.
An error has occurred.
Please contact
[the administrator](mailto:admin@example.com).
es: |
Lo sentimos.
Se ha producido un error.
Por favor, póngase en contacto con
[el administrador](mailto:admin@example.com).
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/error-help-language.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/error-help-language.yml&reset=1 "Click to try this interview")
This will not always be reliable, because an error might happen before the user’s language is known.
The [`metadata`](https://docassemble.org/docs/initial.html#metadata)
block also accepts the specifier `show login`, which can be `true` or `false`. This controls whether the user sees a “Sign in or sign up to save answers” link in the upper right-hand corner during the interview. If `show login` is not specified in the [`metadata`](https://docassemble.org/docs/initial.html#metadata)
, the [Configuration](https://docassemble.org/docs/config.html)
directive [`show login`](https://docassemble.org/docs/config.html#show%20login)
determines whether this link is available.
metadata:
show login: False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/show-login.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/show-login.yml&reset=1 "Click to try this interview")
By default, all of the functions and classes of [`docassemble.base.util`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/util.py)
are imported into the namespace of a **docassemble** interview. If you want to load names manually using a [`modules`](https://docassemble.org/docs/initial.html#modules)
block, you can set `suppress loading util` to `True`:
metadata:
suppress loading util: True
If `suppress loading util` is `True`, the only name that will be imported into your interview is [`process_action`](https://docassemble.org/docs/functions.html#process_action)
.
You can control the [meta tags](https://en.wikipedia.org/wiki/Meta_element)
returned by an interview by setting `social`.
metadata:
title: Social meta tags
short: Social
social:
description: |
A demonstration of meta tags.
image: court.png
og:
image: docassemble.demo:data/static/crown.png
description: A one-page guided interview demonstrating meta tags.
twitter:
title: Social meta tag demo
site: "@docassemble"
description: |
An interview that demonstrates meta tags.
image: https://docassemble.org/img/docassemble-logo-sq-125.jpg
"image:alt": Docassemble logo
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/social.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/social.yml&reset=1 "Click to try this interview")
This results in the following [HTML](https://en.wikipedia.org/wiki/HTML)
inside of the `` tag:
In this example, the **docassemble** server is `https://demo.docassemble.org`, the version of the `docassemble.demo` package is 1.1.5, the [`brandname`](https://docassemble.org/docs/config.html#brandname)
of the server is `docassemble`, and the [`locale`](https://docassemble.org/docs/config.html#locale)
of the server is `en_US.utf8`.
The `image` references are special because if you set them to a reference to a static file, they will be replaced with a full URL to that file. Alternatively, you can provide a full URL. The example contains references to a file within the same package (`court.png`), a file in a different package (`docassemble.demo:data/static/crown.png`), and an external URL (`https://docassemble.org/img/docassemble-logo-sq-125.jpg`).
Note that the `itemprop="name"`, `twitter: title`, and `og:title` are all set to the `title` of the page. This can be overridden by setting the top-level `name`, `title` under `twitter`, and `title` under `og`.
By default, `twitter:card` is set to `summary`, `og:url` is set to the URL for the interview, `og:site_name` is set to the value of [`brandname`](https://docassemble.org/docs/config.html#brandname)
, `og:locale` is determined from the [`locale`](https://docassemble.org/docs/config.html#locale)
, and `og:type` is set to `website`. These defaults can be specifically overridden.
Server-wide default values for [meta tags](https://en.wikipedia.org/wiki/Meta_element)
can be set using the [`social`](https://docassemble.org/docs/config.html#social)
Configuration directive.
Note that by default, the **docassemble** server disallows web crawling by returning a restrictive `/robots.txt` file. That means that as a practical matter, sites will not be able to consume your [meta tags](https://en.wikipedia.org/wiki/Meta_element)
. The `/robots.txt` file can be customized using the [`allow robots`](https://docassemble.org/docs/config.html#allow%20robots)
directive so that your [meta tags](https://en.wikipedia.org/wiki/Meta_element)
are accessible.
If you look at example interviews that are used in the documentation, you will see `example start` and `example end` in the `metadata`. These indicate the YAML blocks that should be excerpted and included in the documentation. There is a script in the [GitHub repository](https://github.com/jhpyle/docassemble)
called `make-screenshots.sh`, which calls `get_yaml_from_example.py`, which scans the [YAML](https://en.wikipedia.org/wiki/YAML)
files in a directory and outputs [YAML](https://en.wikipedia.org/wiki/YAML)
containing the excerpts, which is then processed by the [Jekyll](https://jekyllrb.com/)
documentation site that is hosted on [GitHub Pages](https://pages.github.com/)
.
If you are using the [translation files](https://docassemble.org/docs/initial.html#translations)
system for translating an interview into multiple languages, you should specify a `default language` in the `metadata`. If you wrote the `question` blocks in English and your [translation files](https://docassemble.org/docs/initial.html#translations)
translate questions from English into other languages, set `default language: en` in the `metadata`. The [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block will override the `default language` in the `metadata`, and the [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
will override the [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block. If you don’t specify a `default language` in the `metadata`, **docassemble** will assume the default language is the language indicated by the [`language`](https://docassemble.org/docs/config.html#language)
directive in the [Configuration](https://docassemble.org/docs/config.html)
. Setting a `default language` in the `metadata` ensures that your interview can be installed on a server that uses a different system-wide default language.
It is important to put your `metadata` directive at the top of your interview YAML (i.e. before `question` blocks and `include` directives). The `default language` in the `metadata` is only effective for blocks that follow the `metadata` directive. This means that another `default language` directive can change the default language of your interview. For example, if your interview starts out with a `metadata` block that sets the `default language` to `de`, but then you have an `include` block that brings in a YAML file containing a `metadata` block that sets the `default language` to `es`, then all blocks processed afterward will be considered to be written in Spanish (assuming no [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block or `language` modifier is used). Although this may be what you want, in general, the best way to indicate the default language of a YAML file is the [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block, rather than `metadata`; the purpose of the `metadata` is to specify an interview-wide default.
If you have more than one e-mail configuration under [`mail`](https://docassemble.org/docs/config.html#mail%20multiple)
, you can specify an `email config` to use in the interview.
metadata:
email config: abc
This will affect the functioning of the [`attachment`](https://docassemble.org/docs/documents.html#attachment)
block’s e-mail feature, as well as the [`send_email()`](https://docassemble.org/docs/functions.html#send_email)
function.
Effect of multiple `metadata` blocks[¶](https://docassemble.org/docs/initial.html#overlapping%20metadata)
----------------------------------------------------------------------------------------------------------
An interview can contain multiple metadata blocks. Values in later blocks override earlier blocks. The values over later `metadata` blocks are effectively superimposed on top of earlier `metadata` blocks.
If you write [YAML](https://en.wikipedia.org/wiki/YAML)
files to be [`include`](https://docassemble.org/docs/initial.html#include)
d into other interviews, it is a best practice not to include [`metadata`](https://docassemble.org/docs/initial.html#metadata)
in [YAML](https://en.wikipedia.org/wiki/YAML)
files that will be included into other interviews.
Creating `objects`[¶](https://docassemble.org/docs/initial.html#objects)
=========================================================================
---
objects:
- spouse: Individual
- user.case: Case
---
An `objects` block creates objects that may be referenced in your interview. See [objects](https://docassemble.org/docs/objects.html)
for more information about objects in **docassemble**.
If your interview references the variable `spouse`, **docassemble** will find the above `objects` block and process it. It will define `spouse` as an instance of the object class `Individual` and define `user.case` as an instance of the object class `Case`.
The use of objects in **docassemble** interviews is highly encouraged. However, the objects you use as variables need to inherit from the class `DAObject`. Otherwise, **docassemble** might not be able to find the appopriate [`code` blocks](https://docassemble.org/docs/code.html)
or questions necessary to define them. This is because of the way **docassemble** keeps track of the names of variables.
A code block like this would effectively do the same thing as the `objects` block above:
---
code: |
spouse = Individual('spouse')
user.initializeAttribute('case', Case)
---
This code is more complicated than normal Python code for object initialization because the full name of the variable needs to be supplied to the function that creates and initializes the object. The base class [`DAObject`](https://docassemble.org/docs/objects.html#DAObject)
keeps track of variable names.
In some situations, running `spouse = Individual()` will correctly detect the variable name `spouse`, but in other situations, the name cannot be detected. Running `spouse = Individual('spouse')` will always set the name correctly.
Whenever possible, you should use `objects` blocks rather than code to initialize your objects because `objects` blocks are clean and readable.
You can also use `objects` blocks to initialize attributes of the objects you create. For information on how to do this, see the documentation for the [`using()`](https://docassemble.org/docs/objects.html#DAObject.using)
method.
Importing `objects from file`[¶](https://docassemble.org/docs/initial.html#objects%20from%20file)
--------------------------------------------------------------------------------------------------
---
objects from file:
- claims: claim_list.yml
---
An `objects from file` block imports objects or other data elements that you define in a separate [YAML](https://en.wikipedia.org/wiki/YAML)
or [JSON](https://en.wikipedia.org/wiki/JSON)
data file located in the [sources folder](https://docassemble.org/docs/playground.html#sources)
of the current package. If the interview file containing the `objects from file` block is `data/questions/manage_claims.yml`, **docassemble** will expect the data file to be located at `data/sources/claim_list.yml`.
For more information about how this works, and about how to format the data file, see the documentation for the [`objects_from_file()` function](https://docassemble.org/docs/functions.html#objects_from_file)
. The example above is equivalent to running `claims = objects_from_file('claim_list.yml', name='claims')`.
If you set `use objects` to `True`, then the `use_objects` keyword parameter of the [`objects_from_file()` function](https://docassemble.org/docs/functions.html#objects_from_file)
will be used.
---
use objects: True
objects from file:
- claims: claim_list.yml
---
This is equivalent to running `claims = objects_from_file('claim_list.yml', name='claims', use_objects=True)`.
Incorporation by reference: `include`[¶](https://docassemble.org/docs/initial.html#include)
============================================================================================
---
include:
- basic-questions.yml
- docassemble.helloworld:questions.yml
---
The `include` block incorporates the questions in another [YAML](https://en.wikipedia.org/wiki/YAML)
file, almost as if the contents of the other [YAML](https://en.wikipedia.org/wiki/YAML)
file appeared in place of the `include` block. When the `include`d file is parsed, files referenced within it will be assumed to be located in the `include`d file’s package.
When a filename is provided without a package name, **docassemble** will look first in the `data/questions` directory of the current package (i.e., the package within which the [YAML](https://en.wikipedia.org/wiki/YAML)
file being read is located), and then in the `data/questions` directory of [`docassemble.base`](https://docassemble.org/docs/installation.html#docassemble.base)
.
You can include question files from other packages by explicitly referring to their package names. E.g., `docassemble.helloworld:questions.yml` refers to the file `questions.yml` in the `docassemble/helloworld/data/questions` directory of that package.
The `include` block is a special type of block that is only evaluated when the YAML is initially compiled. Modifiers like `if` and `mandatory` have no effect on the `include` block. It is not possible to conditionally include a YAML file because `include` is a “compile-time” block, not a “runtime” block. If you want the blocks inside of the included YAML file to be conditionally applicable, you need to use `if` or `mandatory` modifiers on those blocks.
See the section on [combining multiple interviews into one](https://docassemble.org/docs/logic.html#multiple%20interviews)
for strategies about how to use `include` effectively.
Images[¶](https://docassemble.org/docs/initial.html#im)
========================================================
With attribution: `image sets`[¶](https://docassemble.org/docs/initial.html#image%20sets)
------------------------------------------------------------------------------------------
---
image sets:
freepik:
attribution: |
Icon made by [Freepik](https://www.flaticon.com/authors/freepik)
images:
baby: crawling.svg
people: users6.svg
injury: accident3.svg
---
An `image sets` block defines the names of icons that you can use to decorate your questions.
The file names refer to files located in the `data/static` directory of the package in which the [YAML](https://en.wikipedia.org/wiki/YAML)
file is located.
Since most free icons available on the internet require attribution, the `image sets` block allows you to specify what attribution text to use for particular icons. The web app shows the appropriate attribution text at the bottom of any page that uses one of the icons. The example above is for a collection of icons obtained from the web site Freepik, which offers free icons under an attribution-only license.
The `image sets` block must be in the form of a [YAML](https://en.wikipedia.org/wiki/YAML)
dictionary, where the names are the names of collections of icons. The collection itself is also a dictionary containing terms `images` and (optionally) an `attribution`. The `images` collection is a dictionary that assigns names to icon files, so that you can refer to icons by a name of your choosing rather than by the name of the image file.
For information on how to use the icons you have defined in an `image sets` block, see `decoration` in the [question modifiers](https://docassemble.org/docs/modifiers.html)
section, `buttons` in the [setting variables](https://docassemble.org/docs/fields.html)
section, and “Inserting inline icons” in the [markup](https://docassemble.org/docs/markup.html)
section.
Without attribution: `images`[¶](https://docassemble.org/docs/initial.html#images)
-----------------------------------------------------------------------------------
---
images:
bills: money146.svg
children: children2.svg
---
An `images` block is just like an `image sets` block, except that it does not set any attribution information. It is simpler because you do not need to give a name to a “set” of images.
The above `images` block is essentially equivalent to writing:
---
image sets:
unspecified:
images:
bills: money146.svg
children: children2.svg
---
Python modules[¶](https://docassemble.org/docs/initial.html#mods)
==================================================================
Importing the module itself: `imports`[¶](https://docassemble.org/docs/initial.html#imports)
---------------------------------------------------------------------------------------------
---
imports:
- datetime
- us
---
`imports` loads a Python module name into the namespace in which your code and question templates are evaluated. The example above is equivalent to running the following Python code:
import datetime
import us
Note that using third-party modules in [`code` blocks](https://docassemble.org/docs/code.html)
could potentially lead to errors relating to the Python [`pickle`](https://docs.python.org/3/library/pickle.html)
module. Variables that are defined in your interview [YAML](https://en.wikipedia.org/wiki/YAML)
are part of the “interview answers” that persist in the database. Most objects that are created by third-party Python modules are meant to live in the memory of a Python process and are not amenable to being “serialized” by [`pickle`](https://docs.python.org/3/library/pickle.html)
or any other mechanism.
The “interview answers” are a Python `dict`. This `dict` is the context in which Python in your [YAML](https://en.wikipedia.org/wiki/YAML)
is executed or evaluated. This `dict` is also stored to the SQL database using [`pickle`](https://docs.python.org/3/library/pickle.html)
after each screen in the interview process.
Before pickling this dictionary, **docassemble** removes any top-level names that refer to modules, functions, and classes, because these variables cannot be pickled. Whenever the screen loads, **docassemble** restores the dictionary from SQL and then imports names from `docassemble.base.util`, loads the names of any modules listed in `imports` blocks, and imports names from the modules listed in `modules` blocks.
Importing all names in a module: `modules`[¶](https://docassemble.org/docs/initial.html#modules)
-------------------------------------------------------------------------------------------------
---
modules:
- datetime
---
Like `imports`, `modules` loads Python modules into the namespace in which your code and question templates are evaluated, except that it imports all of the names that the module exports. The example above is equivalent to running the following Python code:
from datetime import *
If you have a module file in the same package as the [YAML](https://en.wikipedia.org/wiki/YAML)
file, you can import the names from the module file using Python’s syntax for a relative module reference:
---
modules:
- .utils
---
Note that if you use `modules` to import names from a module of your own, you should make sure to define [`__all__`](https://docs.python.org/3/tutorial/modules.html#importing-from-a-package)
in the module to limit which names are imported.
While a `modules` block can import names of functions, classes, and modules, you should not use `modules` to import other types of variables. If a name brought in by `modules` does not refer to a function, class, or module, you will get an error if the name refers to an object that is not pickleable. Even if the object is pickleable, it is not a good idea to bring it into the interview answers, because that would be like doing:
foo = 3
from somemodule import foo
If you have a module that contains constants, it would be better to bring in the name of the module and access constants from the module name:
---
imports:
- somemodule
---
code: |
bar = somemodule.foo * 42
Storing structured `data` in a variable[¶](https://docassemble.org/docs/initial.html#data)
===========================================================================================
The `data` block allows you to specify a data structure in [YAML](https://en.wikipedia.org/wiki/YAML)
in a block and have it available as a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
data structure.
For example, in this interview we create a [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
list and then re-use it in two questions to offer a multiple-choice list.
variable name: fruits
data:
- Apple
- Orange
- Peach
- Pear
---
question: |
What is your favorite fruit?
field: user_favorite_fruit
dropdown:
code: fruits
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/data-simple.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/data-simple.yml&reset=1 "Click to try this interview")
In [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
, the variable `fruits` is this:
['Apple', 'Orange', 'Peach', 'Pear']
You can also use the `data` block to create more complex data structures. You can also use [Mako](https://www.makotemplates.org/)
in the data structure.
variable name: fruits
data:
Apple:
description: |
The apple is a tasty red fruit.
Everyone on ${ planet } loves
to eat apples.
seeds: 5
Orange:
description: |
The orange is, surprisingly,
orange-colored. Most people
on ${ planet } dislike
eating oranges.
seeds: 10
Peach:
description: |
The peach is a fragile fruit.
There are 165,323 peach
orchards on ${ planet }.
seeds: 1
Pear:
description: |
The pear is variously yellow,
green, or brown.
The planet ${ planet } is
shaped like a pear.
seeds: 0
---
question: |
On what planet were you born?
fields:
Planet: planet
---
question: |
What is your favorite fruit?
field: user_favorite_fruit
choices:
code: fruits.keys()
---
mandatory: True
question: |
Summary of ${ user_favorite_fruit }
subquestion: |
${ fruits[user_favorite_fruit]['description'] }
The ${ user_favorite_fruit } has
${ nice_number(fruits[user_favorite_fruit]['seeds']) }
seeds.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/data.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/data.yml&reset=1 "Click to try this interview")
`data` blocks do not work the same way as [`template`](https://docassemble.org/docs/initial.html#template)
blocks. The [Mako](https://www.makotemplates.org/)
templating in a `data` block is evaluated at the time the variable indicted by `variable name` is defined. The text stored in the data structure is the result of processing the [Mako](https://www.makotemplates.org/)
templating. The [Mako](https://www.makotemplates.org/)
templating is not re-evaluated automatically each time a [`question`](https://docassemble.org/docs/questions.html#question)
is shown.
You can also import data from [YAML](https://en.wikipedia.org/wiki/YAML)
files using the [`objects_from_file()` function](https://docassemble.org/docs/functions.html#objects_from_file)
.
Structured data in object form[¶](https://docassemble.org/docs/initial.html#use%20objects)
-------------------------------------------------------------------------------------------
If you set `use objects: True` in a [`data`](https://docassemble.org/docs/initial.html#data)
block, then lists in your [YAML](https://en.wikipedia.org/wiki/YAML)
will become [`DAList`](https://docassemble.org/docs/objects.html#DAList)
s in the resulting data structure, and dictionaries in your [YAML](https://en.wikipedia.org/wiki/YAML)
will become [`DADict`](https://docassemble.org/docs/objects.html#DADict)
s. The `.gathered` attribute of these objects will be set to `True`.
In addition, when `use objects: True` is enabled, any dictionaries in the data structure will be transformed into a [`DAContext`](https://docassemble.org/docs/objects.html#DAContext)
object if the keys of the dictionary are a subset of `question`, `document`, `docx`, `pdf`, and `pandoc` that has at least two elements.
This is a useful shorthand for creating [`DAContext`](https://docassemble.org/docs/objects.html#DAContext)
objects. For example:
variable name: fruits
use objects: True
data:
- question: Apple
document: red fruit
- question: Orange
document: fruit that rhymes with nothing
- question: Peach
document: juicy fruit
docx: peachy peach
pandoc: very juicy fruit
---
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
datatype: object
choices: fruits
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/context.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/context.yml&reset=1 "Click to try this interview")
If you set `use objects: objects`, then the data structure under `data` will be evaluated as though it were a file imported by the [`objects_from_file()` function](https://docassemble.org/docs/functions.html#objects_from_file)
.
variable name: people
use objects: objects
data:
object: Individual
module: docassemble.base.util
items:
- name:
object: IndividualName
item:
first: Fred
last: Smith
email: fred@example.com
allergies:
- peanuts
- subway tokens
skills:
- guessing pennies
- name:
object: IndividualName
item:
first: Larry
last: Jones
email: larry@example.com
allergies: []
skills:
- stapling
- making ${ 'coffee' }
---
mandatory: True
question: |
Summary
subquestion: |
% for item in people:
${ item } is at ${ item.email } and is good at ${ item.skills }
but has trouble with ${ item.allergies }.
% endfor
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/data-objects.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/data-objects.yml&reset=1 "Click to try this interview")
By default, if objects created with `use objects: True` or `use objects: objects` contain [`DAList`](https://docassemble.org/docs/objects.html#DAList)
or [`DADict`](https://docassemble.org/docs/objects.html#DADict)
objects, these objects will have their `.gathered` attributes set to `True`. If you want to leave `.gathered` undefined, set `gathered: False`. In the following interview, `gathered: False` is set so that the user can be asked to fill in missing items.
variable name: people
use objects: objects
gathered: False
data:
object: Individual
module: docassemble.base.util
items:
- name:
object: IndividualName
item:
first: Fred
last: Smith
email: fred@example.com
allergies:
- peanuts
- subway tokens
skills:
- guessing pennies
- name:
object: IndividualName
item:
first: Larry
last: Jones
email: larry@example.com
allergies: []
skills:
- stapling
- making ${ 'coffee' }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/data-objects-gathered.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/data-objects-gathered.yml&reset=1 "Click to try this interview")
Storing structured `data` in a variable using code[¶](https://docassemble.org/docs/initial.html#data%20from%20code)
====================================================================================================================
The `data from code` block works just like the [`data`](https://docassemble.org/docs/initial.html#data)
block, except that [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code is used instead of text or [Mako](https://www.makotemplates.org/)
markup.
variable name: fruits
data from code:
Apple:
description: |
', '.join(['red', 'shiny', 'for teachers'])
seeds: 10/2
Orange:
description: |
capitalize('round') + " and orange"
seeds: seeds_in_orange
Peach:
description: peach_description
seeds: 10**6
Pear:
description: |
"Like an apple, but not like an apple."
seeds: 0
---
question: |
How many seeds in an orange?
fields:
- no label: seeds_in_orange
datatype: range
min: 0
max: 100
---
question: |
How would you describe a peach?
fields:
- no label: peach_description
---
question: |
What is your favorite fruit?
field: user_favorite_fruit
choices:
code: fruits.keys()
---
mandatory: True
question: |
Summary of ${ user_favorite_fruit }
subquestion: |
${ fruits[user_favorite_fruit]['description'] }
The ${ user_favorite_fruit } has
${ nice_number(fruits[user_favorite_fruit]['seeds']) }
seeds.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/data-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/data-code.yml&reset=1 "Click to try this interview")
Structured data from code in object form[¶](https://docassemble.org/docs/initial.html#use%20objects%20from%20code)
-------------------------------------------------------------------------------------------------------------------
The [`use objects`](https://docassemble.org/docs/initial.html#use%20objects)
modifier can also be used with `data from code`.
variable name: fruits
use objects: True
data from code:
- question: |
"Apple"
document: |
"red fruit"
- question: |
"Orange"
document: |
"fruit that rhymes " + "with nothing"
- question: |
"Peach"
document: |
"juicy fruit"
docx: |
"peachy peach"
pandoc: |
"very juicy " + "fruit"
---
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
datatype: object
choices: fruits
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/questions/examples/context-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.demo:data/questions/examples/context-code.yml&reset=1 "Click to try this interview")
The `data from code` block also supports `use objects: objects`.
variable name: the_object
use objects: objects
data from code:
object: Thing
module: docassemble.base.util
item:
name:
object: Name
module: docassemble.base.util
item:
text: |
the_name
size: |
4 * unit_size
---
mandatory: True
question: |
The object is ${ the_object } and its size is
${ the_object.size }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/data-code-objects.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/data-code-objects.yml&reset=1 "Click to try this interview")
The `gathered: False` modifier is also supported when `use objects` is set to `True` or set to `objects`.
Keeping variables fresh: `reset`[¶](https://docassemble.org/docs/initial.html#reset)
=====================================================================================
The `reset` block will cause variables to be undefined every time a screen loads.
This can be helpful in a situation where a variable is set by a [`code` block](https://docassemble.org/docs/code.html)
and the value of the variable ought to be considered afresh based on the user’s latest input.
---
reset:
- client_is_guilty
- opposing_party_is_guilty
---
Effectively, this causes variables to act like functions.
Another way to use this feature is to set the [`reconsider` modifier](https://docassemble.org/docs/logic.html#reconsider)
on a [`code`](https://docassemble.org/docs/code.html#code)
block. This will have the same effect as `reset`, but it will apply automatically to all of the variables that are capable of being assigned by the [`code`](https://docassemble.org/docs/code.html#code)
block.
The `reset` block and the [`reconsider` modifier](https://docassemble.org/docs/logic.html#reconsider)
are computationally inefficient because they cause extra code to be run every time a new screen loads. For a more computationally efficient alternative, see the [`reconsider()`](https://docassemble.org/docs/functions.html#reconsider)
function
Running code when a variable changes: `on change`[¶](https://docassemble.org/docs/initial.html#on%20change)
============================================================================================================
If you allow users to go back and change their answers, you may find situations where using [`depends on`](https://docassemble.org/docs/logic.html#depends%20on)
is not sufficient to invalidate variables that depend on a variable that has been altered.
In this situation, you can write a block of [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code in an `on change` block that will be executed when a variable is changed.
on change:
married: |
for item in income.complete_elements():
item.invalidate_attr('amount')
income.reset_gathered(mark_incomplete=True)
---
question: |
Are you married?
yesno: married
---
question: |
% if married:
What is the ${ ordinal(i) } income item
that you and your spouse earn?
% else:
What is your ${ ordinal(i) } income source?
% endif
fields:
- Name: income[i].name.text
- Amount per year: income[i].amount
datatype: currency
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/on-change.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/on-change.yml&reset=1 "Click to try this interview")
If the user finishes this interview and then changes his mind about whether he is married (indicated by the variable `married`), this will invalidate the answers to the questions about income. If he had said at the outset that he was married, the questions would have asked about the income of both himself and his spouse.
Tagging the `income[i].amount` question with `depends on: married` would not work because in the context of a change to `married`, the variable `i` is not defined.
The `on change` block states explicitly what should be done if the variable `married` changes: the `.amount` attributes should be invalidated and the `income` list should be reopened for gathering. Calling `income.reset_gathered()` with `mark_incomplete=True` will undefine the `.complete` attributes on each of the income items. This is important because otherwise the existing items would still be considered “complete,” and thus the `.amount` attributes would not be re-defined by the interview logic.
The `on change` specifier needs to point to a [YAML](https://en.wikipedia.org/wiki/YAML)
dictionary in which the keys are variable names and the values are [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code that will be run when the given variable changes value.
The `on change` code is run not only when the variable is changed, but when the variable is initialized. This is in contrast to [`depends on`](https://docassemble.org/docs/logic.html#depends%20on)
, which does not invalidate dependent variables when a variable is initialized, but only when it is changed.
It is important that you write `on change` code so that it will always run to completion without encountering any undefined variables. This code runs during a different part of the screen loading process than other code. `on change` code runs before [`modules`](https://docassemble.org/docs/initial.html#modules)
and [`imports`](https://docassemble.org/docs/initial.html#imports)
blocks have loaded (although the standard **docassemble** functions from `docassemble.base.util`, such as [`undefine()`](https://docassemble.org/docs/functions.html#undefine)
and [`invalidate()`](https://docassemble.org/docs/functions.html#invalidate)
, are available). If you need to refer to names from custom modules, bring them into the namespace manually with a line like `from docassemble.missouri import MyObject`.
You can have more than one `on change` block in your interview. If more than one block refers to the same variable, all of the code blocks will be run. The code blocks will be run in the order in which the blocks appear in the [YAML](https://en.wikipedia.org/wiki/YAML)
file.
Changing order of precedence[¶](https://docassemble.org/docs/initial.html#order)
=================================================================================
As explained in [how **docassemble** finds questions for variables](https://docassemble.org/docs/logic.html#variablesearching)
, if there is more than one [`question`](https://docassemble.org/docs/questions.html#question)
or [`code`](https://docassemble.org/docs/code.html#code)
block that offers to define a particular variable, blocks that are later in the [YAML](https://en.wikipedia.org/wiki/YAML)
file will be tried first.
If you would like to specify the order of precedence of blocks in a more explicit way, so that you can order the blocks in the [YAML](https://en.wikipedia.org/wiki/YAML)
file in whatever way you want, you can tag two or more blocks with [`id`](https://docassemble.org/docs/modifiers.html#precedence)
s and insert an `order` block indicating the order of precedence of the blocks.
For example, suppose you have an interview with two blocks that could define the variable `favorite_fruit`. Normally, **docassemble** will try the the second block first because it appears later in the [YAML](https://en.wikipedia.org/wiki/YAML)
file; the second block will “override” the first.
question: |
What the heck is your favorite fruit?
fields:
Fruit: favorite_fruit
---
question: |
What is your favorite fruit?
fields:
Fruit: favorite_fruit
---
mandatory: True
question: |
Your favorite fruit is
${ favorite_fruit }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/supersede-regular.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/supersede-regular.yml&reset=1 "Click to try this interview")
However, if you actually want the first block to be tried first, you can manually specify the order in which the blocks will be tried:
order:
- informal favorite fruit question
- regular favorite fruit question
---
id: informal favorite fruit question
question: |
What the heck is your favorite fruit?
fields:
Fruit: favorite_fruit
---
id: regular favorite fruit question
question: |
What is your favorite fruit?
fields:
Fruit: favorite_fruit
---
mandatory: True
question: |
Your favorite fruit is
${ favorite_fruit }.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/supersede-order.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/supersede-order.yml&reset=1 "Click to try this interview")
Another way to override the order in which blocks will be tried is by using the [`id` and `supersedes`](https://docassemble.org/docs/modifiers.html#precedence)
question modifiers.
Vocabulary `terms` and `auto terms`[¶](https://docassemble.org/docs/initial.html#terms)
========================================================================================
Sometimes you will use vocabulary terms that the user may or may not know. Instead of interrupting the flow of your questions to define every term, you can define certain vocabulary words, and **docassemble** will turn them into hyperlinks wherever they appear in curly brackets. When the user clicks on the hyperlink, a popup appears with the word’s definition.
terms:
creeper: |
A tall green creature that explodes if
you get too close.
zombie pigman: |
A harmless creature who carries a gold
sword.
---
question: Have you ever met a {creeper}?
subquestion: |
If you have met a {zombie pigman}, you
have almost certainly met a creeper.
yesno: met_a_creeper
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/terms.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/terms.yml&reset=1 "Click to try this interview")
If you want the terms to be highlighted every time they are used, whether in curly brackets or not, use `auto terms`.
auto terms:
creeper: |
A tall green creature that explodes if
you get too close.
zombie pigman: |
A harmless creature who carries a gold
sword.
---
question: Have you ever met a creeper?
subquestion: |
If you have met a zombie pigman, you
have almost certainly met a creeper.
yesno: met_a_creeper
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/auto-terms.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/auto-terms.yml&reset=1 "Click to try this interview")
Using `auto terms` can lead to ambiguities, so it is generally better to use `terms` if you can. If you have two terms, `green apple` and `apple`, then `auto terms` will try to make a term within a term, which will lead to unpredictable behavior.
If you want to refer to a term but you want the text of the hyperlink to be different from the name of the term, you can use the pipe character `|` and write the alternate text after the `|`.
question: Have you ever met a {creeper}?
subquestion: |
If you have met a
{zombie pigman|zombified pigman}, you
have almost certainly met a creeper.
yesno: met_a_creeper
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/terms-alternate.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/terms-alternate.yml&reset=1 "Click to try this interview")
Alternatively, in your definition of the terms, you can specify that multiple phrases should be associated with a single definition. You write your terms as a list of dictionaries, and if a dictionary has two keys, `phrases` and `definition`, where `phrases` refers to a list of terms and `definition` refers to a definition, that definition will be used for each of the phrases:
terms:
- phrases:
- charged creeper
- creeper
definition: |
A tall green creature that explodes if
you get too close.
- zombie pigman: |
A harmless creature who carries a gold
sword.
You can also use [`terms`](https://docassemble.org/docs/modifiers.html#terms)
and [`auto terms`](https://docassemble.org/docs/modifiers.html#auto%20terms)
as [question modifiers](https://docassemble.org/docs/modifiers.html)
, in which case the terms will apply only to the question, not to the interview as a whole. When you use `terms` and `auto terms` as initial blocks, you cannot use [Mako](https://www.makotemplates.org/)
templating in the definitions, but when you use them as [question modifiers](https://docassemble.org/docs/modifiers.html)
, you can use [Mako](https://www.makotemplates.org/)
templating.
If you want to define terms using [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code, you can use the [`update_terms()`](https://docassemble.org/docs/functions.html#update_terms)
function.
The `template` block[¶](https://docassemble.org/docs/initial.html#template)
============================================================================
The word “template” has a number of different meanings. If you are interested in how to insert variables into the text of your questions or documents using the [Mako](https://www.makotemplates.org/)
templating syntax, see [markup](https://docassemble.org/docs/markup.html)
. If you are interested in document assembly based on forms or document templates, see the [Documents](https://docassemble.org/docs/documents.html)
section.
A `template` block allows you to assign text to a variable and then re-use the text by referring to a variable.
template: disclaimer
content: |
The opinions expressed herein do not
*necessarily* reflect the views
of ${ company }.
---
field: intro_screen
question: Welcome to the interview!
subquestion: |
Greetings. We hope you learn something
from this guided interview.
${ disclaimer }
To get started, press **Continue**.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/template.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/template.yml&reset=1 "Click to try this interview")
The `content` of a `template` may contain [Mako](https://www.makotemplates.org/)
and [Markdown](https://daringfireball.net/projects/markdown/)
.
The name after `template:` is a [variable name](https://docassemble.org/docs/fields.html#variable%20names)
that you can refer to elsewhere. The variable gets defined as a special type of object known as a [`DALazyTemplate`](https://docassemble.org/docs/objects.html#DALazyTemplate)
.
The `template` block, like [`question`](https://docassemble.org/docs/questions.html#question)
and [`code`](https://docassemble.org/docs/code.html#code)
blocks, offers to define a variable. So when **docassemble** needs to know the definition of `disclaimer` and finds that `disclaimer` is not defined, it will look for a [`question`](https://docassemble.org/docs/questions.html#question)
, [`code`](https://docassemble.org/docs/code.html#code)
, or `template` block that offers to define `disclaimer`. If it finds the `template` block above, it will define the `disclaimer` variable.
Optionally, a `template` can have a `subject`:
template: disclaimer
subject: |
Please be advised
content: |
The opinions expressed herein do not
*necessarily* reflect the views
of ${ company }.
---
field: intro_screen
question: Welcome to the interview!
subquestion: |
Greetings. We hope you learn something
from this guided interview.
To get started, press **Continue**.
under: |
### ${ disclaimer.subject }
${ disclaimer.content }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/template-subject.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/template-subject.yml&reset=1 "Click to try this interview")
You can refer to the two parts of the template by writing, e.g., `disclaimer.subject` and `disclaimer.content`.
Note that writing `${ disclaimer }` has the same effect as writing `${ disclaimer.content }`. You can also write `${ disclaimer.show() }` (for interchangability with images).
To convert the subject and the content to HTML, you can write `disclaimer.subject_as_html()` and `disclaimer.content_as_html()`. These methods take the optional keyword argument `trim`. If `True`, the resulting HTML will not be in a `
` element. (The default is `False`.)
[`template`](https://docassemble.org/docs/initial.html#template)
objects are also useful for defining the content of e-mails. See [`send_email()`](https://docassemble.org/docs/functions.html#send_email)
for more information on using templates with e-mails.
You might prefer to write text in [Markdown](https://daringfireball.net/projects/markdown/)
files, rather than in [Markdown](https://daringfireball.net/projects/markdown/)
embedded within [YAML](https://en.wikipedia.org/wiki/YAML)
. To facilitate this, **docassemble** allows you to create a `template` that references a separate [Markdown](https://daringfireball.net/projects/markdown/)
file.
template: disclaimer
content file: disclaimer.md
---
field: intro_screen
question: Welcome to the interview!
subquestion: |
Greetings. We hope you learn something
from this guided interview.
${ disclaimer }
To get started, press **Continue**.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/template-file.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/template-file.yml&reset=1 "Click to try this interview")
The file [`disclaimer.md`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/templates/disclaimer.md)
is a simple [Markdown](https://daringfireball.net/projects/markdown/)
file containing the disclaimer from the previous example.
The `content file` is assumed to refer to a file in the “templates” folder of the same package as the interview source, unless a specific package name is indicated. (e.g., `content file:` [`docassemble.demo:data/templates/hello_template.md`](https://github.com/jhpyle/docassemble/blob/master/docassemble_demo/docassemble/demo/data/templates/hello_template.md)
)
In the example above, the sample interview is in the file [`docassemble.base:data/questions/examples/template-file.yml`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/template-file.yml)
, while the [Markdown](https://daringfireball.net/projects/markdown/)
file is located at [`docassemble.base:data/templates/disclaimer.md`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/templates/disclaimer.md)
.
If the `content file` specifier refers to a dictionary in which the only key is `code`, the `code` will be evaluated as [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code, and the result will be used as the file.
code: |
template_file_to_use = 'disclaimer.md'
---
template: disclaimer
content file:
code: template_file_to_use
---
field: intro_screen
question: Welcome to the interview!
subquestion: |
Greetings. We hope you learn something
from this guided interview.
${ disclaimer }
To get started, press **Continue**.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/template-file-code.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/template-file-code.yml&reset=1 "Click to try this interview")
In this example, the `code` evaluated to the name of a file in the templates folder. The `code` may also evaluate to a URL, [`DAFile`](https://docassemble.org/docs/objects.html#DAFile)
, [`DAFileList`](https://docassemble.org/docs/objects.html#DAFileList)
, [`DAFileCollection`](https://docassemble.org/docs/objects.html#DAFileCollection)
, or [`DAStaticFile`](https://docassemble.org/docs/objects.html#DAStaticFile)
.
For more information about this use of `template`s, see the section on [Inserting multi-line or formatted text into a single field in a DOCX file](https://docassemble.org/docs/documents.html#markdown%20to%20docx)
.
The `table` block[¶](https://docassemble.org/docs/initial.html#table)
======================================================================
The `table` works in much the same way as a `template`, except its content is a table that will be formatted appropriately whether it is included in a [question](https://docassemble.org/docs/questions.html)
or in a [document](https://docassemble.org/docs/documents.html)
.
This block should be used when each row of your table represents an item in a [group](https://docassemble.org/docs/groups.html)
; that is, you do not know how many rows the table will contain, because the information is in a [list](https://docs.python.org/3.12/tutorial/datastructures.html)
, [dictionary](https://docs.python.org/3/library/stdtypes.html#dict)
, or [set](https://docs.python.org/3/library/stdtypes.html#set)
. If you just want to format some text in a table format, see the documentation about [tables](https://docassemble.org/docs/initial.html#table)
in the [markup](https://docassemble.org/docs/markup.html)
section.
In the following example, the variable `fruit` is a [`DAList`](https://docassemble.org/docs/objects.html#DAList)
of objects of type [`Thing`](https://docassemble.org/docs/objects.html#Thing)
, each of which represents a fruit. Each row in the resulting table will describe one of the fruits.
objects:
- fruit: DAList
---
mandatory: true
code: |
fruit.object_type = Thing
---
mandatory: True
question: |
Information about fruit
subquestion: |
Here is a fruity summary.
${ fruit_table }
---
table: fruit_table
rows: fruit
columns:
- Fruit Name: row_item.name
- Seeds: row_item.seeds
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table.yml&reset=1 "Click to try this interview")
The `table: fruit_table` line indicates the name of the variable that will hold the template for table. The [`question`](https://docassemble.org/docs/questions.html#question)
block includes the table simply by referring to the variable `fruit_table`.
The `rows: fruit` line indicates the variable containing the [group](https://docassemble.org/docs/groups.html)
of items that represent rows in the table. The `fruit` variable is a [`DAList`](https://docassemble.org/docs/objects.html#DAList)
that gets populated during the interview.
Next, `columns` describes the header of each column and what should be printed in each cell under that header. Like a [`fields`](https://docassemble.org/docs/fields.html#fields)
list within a [`question`](https://docassemble.org/docs/questions.html#question)
, `columns` must contain a [YAML](https://en.wikipedia.org/wiki/YAML)
list where each item is a key/value pair (a one-item dictionary) where the key is the header of the column and the value is a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
representing the contents of the cell for that column, for a given row.
In the example above, the header of the first column is “Fruit Name” and the [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
that produces the name of the fruit is `row_item.name`.
There are two special variables available to these [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
s:
* `row_item`: this is the item in the [group](https://docassemble.org/docs/groups.html)
corresponding to the current row.
* `row_index`: this is `0` for the first row, `1` for the second row, `2` for the third row, etc.
You can pretend that the [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
s are evaluated in a context like this:
row_index = 0
for row_item in fruit:
# evaluation takes place here
row_index = row_index + 1
In this example, the first column will show name of the fruit (`row_item.name`) and the second column will show the number of seeds (`row_item.seeds`).
The header of each column is plain text (not a [Python expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
). The header can include [Mako](https://www.makotemplates.org/)
and [Markdown](https://daringfireball.net/projects/markdown/)
.
If you have a complicated header, you can use the special keys `header` and `cell` to describe the header and the cell separately. (This is similar to using [`label` and `field`](https://docassemble.org/docs/fields.html#label)
within a [`fields`](https://docassemble.org/docs/fields.html#fields)
list.)
code: |
thing = "Fruit"
---
table: fruit_table
rows: fruit
columns:
- header: |
${ thing } Name
cell: |
row_item.name
- header: |
Number of Seeds
cell: |
row_item.seeds
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-alt.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-alt.yml&reset=1 "Click to try this interview")
You can use [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
to create cells with content that is computed from the items of a [group](https://docassemble.org/docs/groups.html)
.
table: fruit_table
rows: fruit
columns:
- Fruit Name: |
noun_plural(row_item.name)
- Number of Seeds: |
row_item.seeds * 2
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-python.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-python.yml&reset=1 "Click to try this interview")
The above example prints the name of the fruit as a plural noun, and inflates the number of seeds.
Remember that the [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
code here is an [expression](https://stackoverflow.com/questions/4782590/what-is-an-expression-in-python)
, not a block of code. If you want to use if/then/else logic in a cell, you will need to use [Python](https://en.wikipedia.org/wiki/Python_%28programming_language%29)
’s one-line form of if/then/else:
table: fruit_table
rows: fruit
columns:
- Fruit Name: |
noun_plural(row_item.name)
- Number of Seeds: |
"too many" if row_item.seeds > 20 else row_item.seeds
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-if-then.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-if-then.yml&reset=1 "Click to try this interview")
When `fruit_table` is inserted into the [`question`](https://docassemble.org/docs/questions.html#question)
, the result will be a [Markdown](https://daringfireball.net/projects/markdown/)
\-formatted table.
This:
question: |
Information about fruit
subquestion: |
Here is a fruity summary.
${ fruit_table }
will have the effect of this:
question: |
Information about fruit
subquestion: |
Here is a fruity summary.
Fruit Name |Number of Seeds
-----------|---------------
Apples |4
Oranges |3
Pears |6
For more information about [Markdown](https://daringfireball.net/projects/markdown/)
\-formatted tables, see the documentation about [tables](https://docassemble.org/docs/initial.html#table)
in the [markup](https://docassemble.org/docs/markup.html)
section.
Instead of using a `table` block, you could construct your own [Markdown](https://daringfireball.net/projects/markdown/)
tables manually using a [Mako](https://www.makotemplates.org/)
“for” loop. For example:
mandatory: True
question: |
Information about fruit
subquestion: |
Here is a fruity summary.
Fruit Name |Number of Seeds
---------------|----------------
% for item in fruit:
${ item.name } | ${ item.seeds }
% endfor
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-mako.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-mako.yml&reset=1 "Click to try this interview")
The advantages of using the `table` block are:
* The `table` block describes the content of a table in a conceptual rather than visual way. In [Markdown](https://daringfireball.net/projects/markdown/)
, simple tables look simple, but complicated tables can look messy. The `table` block allows you to map out your ideas in outline form rather than squeezing everything into a single line that has a lot of punctuation marks.
* The `table` block will attempt to set the relative table widths in a sensible way based on the actual contents of the table. If you create your own tables in [Markdown](https://daringfireball.net/projects/markdown/)
, and the text in any cell wraps, the relative table widths of the columns will be decided based on the relative widths of the cells in the divider row (`----|---------`). You might not know in advance what the relative sizes of the text will be in each column.
The `table` block acts like a `template` block in that the variable it sets will be a **docassemble** object. The `.content` attribute will be set to the text of the table in [Markdown](https://daringfireball.net/projects/markdown/)
format.
If the variable indicated by `rows` is empty, the table will display with only the headers. To suppress this, you can add `show if empty: False` to the `table` block. The resulting `.content` will be the empty string, `""`.
table: fruit_table
rows: fruit
columns:
- Fruit Name: row_item.name
- Number of Seeds: row_item.seeds
show if empty: False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-empty.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-empty.yml&reset=1 "Click to try this interview")
If you would like a message to display in place of the table in the event that there are no `rows` to display, you can set `show if empty` to this message. [Mako](https://www.makotemplates.org/)
and [Markdown](https://daringfireball.net/projects/markdown/)
can be used. The message will become the `.content` of the resulting object.
table: fruit_table
rows: fruit
columns:
- Fruit Name: row_item.name
- Number of Seeds: row_item.seeds
show if empty: |
I'm very sorry, but there are no fruits
to display today.
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-empty-message.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-empty-message.yml&reset=1 "Click to try this interview")
If you include a table in the content of an [`attachment`](https://docassemble.org/docs/documents.html#attachment)
, you might find that the table is too wide, or not wide enough. [Pandoc](https://pandoc.org/)
breaks lines, determines the relative width of columns, and determines the final width of a table based on the characters in the divider row (`----|---------`).
By default, **docassemble** will construct a divider row that is no longer than 65 characters. This should work for standard applications (12 point font, letter size paper).
You can change the number of characters from 65 to something else by setting value of [`table width`](https://docassemble.org/docs/initial.html#table%20width)
in a [`features`](https://docassemble.org/docs/initial.html#features)
block.
features:
table width: 35
---
table: fruit_table
rows: fruit
columns:
- Fruit Name: row_item.name
- Seeds: row_item.seeds
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-width.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-width.yml&reset=1 "Click to try this interview")
You can also use `table` blocks with [`DADict`](https://docassemble.org/docs/objects.html#DADict)
objects:
scan for variables: False
mandatory: True
code: |
income['employment'].receives = True
income['employment'].amount = 237
income['benefits'].receives = False
income['interest'].receives = True
income['interest'].amount = 23
---
table: income.table
rows: income
columns:
- Type: |
row_index
- Receives: |
'Yes' if row_item.receives else 'No'
- Amount: |
currency(row_item.amount) if row_item.receives else ''
---
mandatory: True
question: |
Summary of income
subquestion: |
${ income.table }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-dict.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-dict.yml&reset=1 "Click to try this interview")
When `rows` refers to a [`DADict`](https://docassemble.org/docs/objects.html#DADict)
, then in the `columns`, `row_index` represents the “key” and `row_item` represents the “value” of each item in the dictionary.
By default, the display of a table will require that the table is gathered. If you want to display a table with the items in a group that have been completely gathered so far, you can set `require gathered: False` in the table definition.
table: fruit.table
rows: fruit
columns:
- Fruit Name: row_item
- Number of Seeds: row_item.seeds
- Last eaten: row_item.last_eaten
require gathered: False
edit:
- name.text
- last_eaten
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-require-gathered.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-require-gathered.yml&reset=1 "Click to try this interview")
When displaying a table using `require gathered: False`, an item will not be shown unless it has been completely gathered. If you want to show unfinished items, set `show incomplete: True`.
table: fruit.table
rows: fruit
columns:
- Fruit Name: row_item
- Number of Seeds: row_item.seeds
- Last eaten: row_item.last_eaten
show incomplete: True
show if empty: You do not have any fruit.
edit:
- name.text
- last_eaten
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-show-incomplete.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-show-incomplete.yml&reset=1 "Click to try this interview")
By default, a column for which information is not known will be displayed as “n/a.” You can customize this by setting `not available label`. In this example, the label is set to a blank value:
table: fruit.table
rows: fruit
columns:
- Fruit Name: row_item
- Number of Seeds: row_item.seeds
- Last eaten: row_item.last_eaten
show incomplete: True
show if empty: You do not have any fruit.
not available label: ""
edit:
- name.text
- last_eaten
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-not-available-label.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-not-available-label.yml&reset=1 "Click to try this interview")
Exporting tables to Excel and other formats[¶](https://docassemble.org/docs/initial.html#export)
-------------------------------------------------------------------------------------------------
You can call the `export()` method on a `table` to get a [`DAFile`](https://docassemble.org/docs/objects.html#DAFile)
representation of the table.
For example, this interview provides a Microsoft Excel .xlsx file representation of a table:
objects:
- fruit: DAList
---
mandatory: true
code: |
fruit.object_type = Thing
---
mandatory: True
question: |
Information about fruit
subquestion: |
Here is a fruity summary.
${ fruit_table }
You can also [download this information].
[download this information]: ${ fruit_table.export('fruit.xlsx', title='fruit').url_for() }
---
table: fruit_table
rows: fruit
columns:
- Fruit Name: row_item.name
- Seeds: row_item.seeds
- Last eaten: row_item.last_eaten
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-export.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-export.yml&reset=1 "Click to try this interview")
This function uses the [`pandas`](https://pandas.pydata.org/)
module to export to various formats.
The `export()` method takes a filename, which is parsed to determine the file format you want to use. This can also be provided as the `filename` keyword parameter. If you omit the filename, you can indicate the file format using the `file_format` keyword parameter. The default file format is `'xlsx'`. The valid file formats include `csv`, `xlsx`, and `json`.
The `title` keyword parameter indicates the name of the data set. This is used as the name of the Microsoft Excel sheet.
When the `xlsx` format is used, you can set the `freeze_panes` keyword parameter to `False` to turn off the Microsoft Excel “freeze panes” feature.
Here are some examples of usage:
* `fruit_table.export('fruit.xlsx')`: returns a Microsoft Excel file called `fruit.xlsx`.
* `fruit_table.export('fruit.xlsx', title='Fruits')`: returns a Microsoft Excel file called `fruit.xlsx` where the sheet is named “Fruits”.
* `fruit_table.export('fruit.xlsx', title='Fruits', freeze_panes=False)`: returns a Microsoft Excel file called `fruit.xlsx` where the sheet is named “Fruits” and the “freeze panes” feature is turned off.
* `fruit_table.export('fruit.csv')`: returns a comma-separated values file called `fruit.csv`.
* `fruit_table.export(file_format='csv')`: returns a comma-separated values file called `file.csv`.
* `fruit_table.export()`: returns a Microsoft Excel file called `file.xlsx`.
The optional keyword parameter `output_to` can be used to specify a [`DAFile`](https://docassemble.org/docs/objects.html#DAFile)
to which the output of `.export()` should be written. If omitted, [`DAFile`](https://docassemble.org/docs/objects.html#DAFile)
with a random instance name will be returned.
Converting tables to a pandas dataframe[¶](https://docassemble.org/docs/initial.html#as_df)
--------------------------------------------------------------------------------------------
If you want to work with your table as a [`pandas`](https://pandas.pydata.org/)
dataframe, you can call `fruit_table.as_df()` to obtain the information for the table as a [`pandas`](https://pandas.pydata.org/)
dataframe object. However, note that objects from the [`pandas`](https://pandas.pydata.org/)
package cannot necessarily be “pickled” by Python, so it is best if you call this method from functions in Python modules, or in such a way that the results do not get saved to variables in the interview.
Using tables to edit groups[¶](https://docassemble.org/docs/initial.html#groups%20edit)
----------------------------------------------------------------------------------------
You can use a `table` to provide the user with an interface for editing an already-gathered [`DAList`](https://docassemble.org/docs/objects.html#DAList)
or [`DADict`](https://docassemble.org/docs/objects.html#DADict)
.
mandatory: True
question: |
All done
subquestion: |
The people are ${ person }.
Your favorite is ${ favorite }.
${ person.table }
${ person.add_action() }
---
table: person.table
rows: person
columns:
- Name: |
row_item.name.full()
- Fruit: |
row_item.favorite_fruit
edit:
- name.first
- favorite_fruit
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/edit-list.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/edit-list.yml&reset=1 "Click to try this interview")
For more information about this feature, see the section on [editing an already-gathered list](https://docassemble.org/docs/groups.html#editing)
in the section on [groups](https://docassemble.org/docs/groups.html)
.
Sorting and filtering items in a table[¶](https://docassemble.org/docs/initial.html#sort%20key)
------------------------------------------------------------------------------------------------
If you have a `DAList` or a `DADict` and you want to display a table of some of the items, or display the items sorted in a different way, you can specify `sort key` and `filter` options.
In a `table` definition where `rows` refers to a `DAList`, `sort key` refers to a function that takes a single parameter as input, namely an item in the list, and returns a value that should be used for sorting the list. When Python sorts the list, it will call this function on each item in the list. The `sort key` is passed to the `key` parameter of the Python [`sorted`](https://docs.python.org/3/library/functions.html#sorted)
function.
Any function name can be used. For example, writing `sort key: str` will use the `str()` function to convert each item in the list to text, and then alphabetize the rows based on that textual representation of each item.
In most cases, though, you will want to write a [`lambda`](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions)
function. For example:
table: fruit.table
rows: fruit
columns:
- Fruit Name: row_item
- Number of Seeds: row_item.seeds
- Last Eaten: row_item.last_eaten
sort key: |
lambda y: y.last_eaten
sort reverse: True
filter: |
row_item.seeds > 0
show if empty: |
You have never eaten any fruit.
edit:
- name.text
- last_eaten
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-sort.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-sort.yml&reset=1 "Click to try this interview")
This example uses
sort key: |
lambda y: y.last_eaten
The `last_eaten` attribute is defined by a `datatype: date` field, so the rows will be presented to the user in date order.
A [`lambda`](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions)
function is just like a Python function, except it doesn’t have a name. Equivalently, you could load a `.py` module file that defines the following function:
def get_last_eaten(y):
return y.last_eaten
and then you could write `sort key: get_last_eaten`. However, it is easier to just write `lambda y: y.last_eaten`.
Note that the use of the variable name `y` is arbitrary. You could use any variable name you want (as long as it isn’t a reserved Python keyword).
In a `table` definition, `sort reverse` indicates whether the rows will be sorted in reverse order. In the above example, `sort reverse: True` is used, which means the table will present the items in reverse chronological order. `sort reverse` can refer to `True`, `False`, or any Python expression. If `sort reverse` is omitted or it evaluates to a false value, the rows are sorted in forward order.
If you want the `table` to present a filtered list of the items, you can set `filter` to a Python expression that should be evaluated to determine if the item should be included or not. In the above example, the expression is `row_item.seeds > 0`. That means that the `table` will omit any items in the `fruit` list where the `seeds` attribute is zero. Like an item in the `columns`, the `filter` expression uses the special variables `row_item` and `row_index`. The expression is evaluated for each item in the list, and if the expression evaluates to a true value, the item is included in the table.
If your `table` is editable, it is important to perform sorting and filtering using `sort key`, `sort reverse`, and `filter`, rather than by passing an expression to `rows` that returns an altered version of the underlying list. If you give `rows` an altered version of a `DAList` object, some of the editing features might not work correctly.
Note that these sorting and filtering features do not alter the underlying `DAList` in any way; they only affect the way the `table` representation is displayed.
### Sorting and filtering dictionaries[¶](https://docassemble.org/docs/initial.html#sort%20filter%20dict)
The `sort key`, `sort reverse`, and `filter` features also work if the `rows` refers to a `DADict` object. The main difference is that the `sort key` function is given a Python `tuple` with two items, the dictionary key and the dictionary value. If you want to sort on the key of a dictionary item, access the first item; if you want to sort on the value of a dictionary item, access the second item.
table: income.table
rows: income
columns:
- Type: |
row_index
- Receives: |
'Yes' if row_item.receives else 'No'
- Amount: |
currency(row_item.amount) if row_item.receives else ''
sort key: |
lambda y: y[1].amount if y[1].receives else 0.0
filter: |
row_item.receives is False or row_item.amount < 100
edit:
- receives
delete buttons: False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/table-sort-dict.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/table-sort-dict.yml&reset=1 "Click to try this interview")
In this example, the `sort key` expression is:
lambda y: y[1].amount if y[1].receives else 0.0
Here, `y[1]` refers to the value of the dictionary item (a [`DAObject`](https://docassemble.org/docs/objects.html#DAObject)
). The income items that are not received are listed as though their `amount` was zero, and then the other items are listed in `amount` order.
Defining the sections for the navigation bar[¶](https://docassemble.org/docs/initial.html#sections)
====================================================================================================
You can use the [`navigation`](https://docassemble.org/docs/initial.html#navigation%20bar)
bar feature or the [`nav.show_sections()`](https://docassemble.org/docs/functions.html#DANav.show_sections)
function to show your users the “sections” of the interview and what the current section of the interview is.
Here is a complete example.
sections:
- Introduction
- About you:
- Contact info
- Demographics
- Preferences
- Conclusion
---
features:
navigation: True
progress bar: True
---
mandatory: True
code: |
menu_items = [ action_menu_item('Roadmap', 'road_map') ]
---
initial: True
code: |
if returning_user(minutes=0.5):
welcome_back
---
mandatory: True
question: |
Welcome to the interview
subquestion: |
If you are not on a
smartphone-sized device,
you should see a navigation
bar to the left.
field: sees_nav_bar
---
mandatory: True
question: |
I am going to ask you some
questions about yourself.
field: intro_to_about_you
section: About you
---
mandatory: True
question: |
What is your name?
fields:
- First Name: first_name
- Last Name: last_name
section: Contact info
---
mandatory: True
question: |
What is your e-mail address?
fields:
- E-mail: email_address
datatype: email
---
mandatory: True
question: |
What is your gender?
field: gender
choices:
- Male
- Female
- Something else
section: Demographics
---
mandatory: True
question: |
What kind of belly button
do you have?
subquestion: |
To see what a user would
see after returning to
the interview after a period
of absence, try waiting
thirty seconds, then
[click into the\
interview](${ interview_url(local=True) }).
In addition, there is a similar
screen available on the Menu in the
upper-right, under "Roadmap."
field: belly_button
choices:
- Innie
- Outie
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Favorite fruit: favorite_fruit
section: Preferences
---
mandatory: True
question: |
What is your favorite vegetable?
fields:
- Favorite vegetable: favorite_vegetable
---
progress: 100
mandatory: True
question: Thank you.
subquestion: |
${ first_name },
Your answers mean a lot to me.
I am going to go eat some
${ favorite_vegetable }
now.
section: Conclusion
---
event: welcome_back
question: |
Welcome back!
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to pick up
where you left off.
buttons:
Continue: continue
---
event: road_map
question: |
Roadmap
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to resume the
interview.
buttons:
Continue: continue
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections.yml&reset=1 "Click to try this interview")
Subsections are supported, but only one level of nesting is allowed.
If your interview uses [multiple languages](https://docassemble.org/docs/language.html)
, you can specify more than one `sections` block and modify each one with a `language` modifier:
---
language: en
sections:
- Introduction
- Fruit
- Vegetables
- Conclusion
---
language: es
sections:
- Introducción
- Fruta
- Vegetales
- Conclusión
---
If no language is specified, the fallback language `*` is used.
Section headings are not processed as [Mako](https://www.makotemplates.org/)
. As a result, the [`translations`](https://docassemble.org/docs/initial.html#translations)
system cannot be used to translate section headings. Thus the translation method described above, using the `language` modifier, must be used to support section headings in multiple languages. (Alternatively, you could use [`code` blocks](https://docassemble.org/docs/code.html)
and the [`nav.set_sections()`](https://docassemble.org/docs/functions.html#DANav.set_sections)
to define the sections using [`template`](https://docassemble.org/docs/initial.html#template)
blocks reduced to text.)
In the example above, the [`section`](https://docassemble.org/docs/modifiers.html#section)
modifier referred to sections using the same text that is displayed to the user. However, in some circumstances, you might want to use a shorthand to refer to a section, and update the actual section names displayed to the user without having to make changes in numerous places in your interview. You can do this by using key/value pairs in your `sections` block, and using the special key `subsections` to indicate subsections:
sections:
- intro: Introduction
- about: About you
subsections:
- contact: Contact info
- demographic: Demographics
- prefs: Preferences
- conclusion: Conclusion
---
features:
navigation: True
---
mandatory: True
question: |
Welcome to the interview
subquestion: |
If you are not on a
smartphone-sized device,
you should see a navigation
bar to the left.
field: sees_nav_bar
---
mandatory: True
question: |
I am going to ask you some
questions about yourself.
field: intro_to_about_you
section: about
---
mandatory: True
question: |
What is your name?
fields:
- First Name: first_name
- Last Name: last_name
section: contact
---
mandatory: True
question: |
What is your e-mail address?
fields:
- E-mail: email_address
datatype: email
---
mandatory: True
question: |
What is your gender?
field: gender
choices:
- Male
- Female
- Something else
section: demographic
---
mandatory: True
question: |
What kind of belly button
do you have?
field: belly_button
choices:
- Innie
- Outie
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Favorite fruit: favorite_fruit
section: prefs
---
mandatory: True
question: |
What is your favorite vegetable?
fields:
- Favorite vegetable: favorite_vegetable
---
mandatory: True
question: Thank you.
subquestion: |
${ first_name },
Your answers mean a lot to me.
I am going to go eat some
${ favorite_vegetable }
now.
section: conclusion
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-keywords.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-keywords.yml&reset=1 "Click to try this interview")
The keywords for section names need to be valid [Python names](https://docassemble.org/docs/fields.html#variable%20names)
. When choosing keywords, make sure not to use the names of variables that already exist in your interview.
This is because the keywords can be used to make the left-hand navigation bar clickable. If a keyword for a section is a variable that exists in the interview, clicking on the section will cause an [action](https://docassemble.org/docs/functions.html#actions)
to be launched that seeks a definition of that variable.
The recommended way to use this feature is to set up [`review`](https://docassemble.org/docs/fields.html#review)
blocks that have [`event`](https://docassemble.org/docs/fields.html#event)
set to the keyword of each section that you want to be clickable.
sections:
- intro: Introduction
- about: About you
subsections:
- contact: Contact info
- demographic: Demographics
- prefs: Preferences
- conclusion: Conclusion
---
event: contact
section: contact
question: |
Review contact information
review:
- Edit name: first_name
button: |
Name: ${ first_name } ${ last_name }
- Edit e-mail: email_address
button: |
E-mail: ${ email_address }
---
event: demographic
section: demographic
question: |
Review demographic information
review:
- Edit gender: gender
button: |
Gender: ${ gender }
- Edit belly button: belly_button
button: |
Belly button: ${ belly_button }
---
event: prefs
section: prefs
question: |
Preferences
review:
- Edit fruit: favorite_fruit
button: |
Favorite fruit: ${ favorite_fruit }
- Edit vegetable: favorite_vegetable
button: |
Favorite vegetable: ${ favorite_vegetable }
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-keywords-review.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-keywords-review.yml&reset=1 "Click to try this interview")
Note that if you use [`review`](https://docassemble.org/docs/fields.html#review)
blocks in an interview with sections, every question should have a `section` defined. Otherwise, when your users jump around the interview, their section may not be appropriate for the question they are currently answering. Alternatively, you could use [`code` blocks](https://docassemble.org/docs/code.html)
and the [`nav.set_section()`](https://docassemble.org/docs/functions.html#DANav.set_section)
function to make sure that the section is set appropriately.
By default, users are only able to click on sections that they have visited. If you want users to be able to click on any section at any time, set `progressive` to `False`:
sections:
- intro: Introduction
- about: About you
subsections:
- contact: Contact info
- demographic: Demographics
- prefs: Preferences
- conclusion: Conclusion
progressive: False
---
event: intro
code: |
force_ask('sees_nav_bar')
---
event: about
code: |
force_ask('intro_to_about_you')
---
event: contact
code: |
force_ask('first_name', 'email_address')
---
event: demographic
code: |
force_ask('gender', 'belly_button')
---
event: prefs
code: |
force_ask('favorite_fruit', 'favorite_vegetable')
---
event: conclusion
code: |
force_ask('final_screen')
---
features:
navigation: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-non-progressive.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-non-progressive.yml&reset=1 "Click to try this interview")
By default, subsections are not shown until the user has entered one of the subsections. If you want subsections to be opened by default, set `auto open` to `True`.
sections:
- Introduction
- About you:
- Contact info
- Demographics
- Preferences
- Conclusion
auto open: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-auto-open.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-auto-open.yml&reset=1 "Click to try this interview")
Note that **docassemble**’s logic system is very different from that of SurveyMonkey or TypeForm, where defining a sequence of questions and dividing those questions into sections is straightforward. In **docassemble**, the question that is asked at any given time is based on the interview logic. The interview logic is evaluated and if a necessary piece of information is undefined, **docassemble** will ask a question (or run a `code` block) to get a definition of that piece of information. This is convenient when working toward an end goal, because the interview developer can focus on specifying correct rules, without worrying about the complexities of designing an interview process. The downside is that the sequence of questions might be different from one interview to another, and the sequence might not be easy to fit into well-defined sections. Once variables are defined, the sequence of questions that led to those variables being defined is lost and cannot be repeated. Nor should the original interview process be repeated, necessarily, because if the user makes changes that impact the logic, the sequence should be different.
Given the way that **docassemble**’s interview logic works, if you want to let the user revisit a section they have already completed, you need to specify a process for that. Clicking on a section heading and going to a special [`review`](https://docassemble.org/docs/fields.html#review)
screen is one of the ways you can specify a process.
The idea of a [`review`](https://docassemble.org/docs/fields.html#review)
screen for a section is that you can give the user an overview of the information they already entered. If users can see their information from a summary perspective, it helps them notice what might be incorrect or inconsistent. The `review` screen can be divided into subsections with headings indicated in Markdown. You can embed Mako logic in the text and explain to the user the significance of what they have entered.
A [`review`](https://docassemble.org/docs/fields.html#review)
screen should ideally not have one button per variable, but rather have one button for a sequence of one or more screens that the user might want to revisit, after which they are returned to the `review` screen and they can see what has changed. If the user has entered information into [groups](https://docassemble.org/docs/groups.html)
, you can show the user a `table` of the information they entered and let them add, edit, or delete items.
Assisting users with `interview help`[¶](https://docassemble.org/docs/initial.html#interview%20help)
=====================================================================================================
---
interview help:
heading: How to use this web site
content: |
Answer each question. At the end, you will get a prize.
---
An `interview help` block adds text to the “Help” page of every question in the interview. If the question has `help` text of its own, the `interview help` will appear after the question-specific help.
You can also add audio to your interview help:
---
interview help:
heading: How to use this web site
audio: answer_each_question.mp3
content: |
Answer each question. At the end, you will get a prize.
---
You can also add video to help text using the `video` specifier.
See the [question modifiers](https://docassemble.org/docs/modifiers.html)
section for an explanation of how audio and video file references work.
You can also provide a `label` as part of the `interview help`. This label will be used instead of the word “Help” in the main navigation bar as a label for the “Help” tab.
---
interview help:
label: More info
heading: More information about this web site
content: |
If you are not sure what the right answer is, provide
your best guess.
You are answering these questions under the pains and
penalties of perjury. Your answers will be
shared with the special prosecutor.
---
Note that if you provide question-specific [`help`](https://docassemble.org/docs/modifiers.html#help)
, and you include a `label` as part of that help, that label will override the default label provided in the `interview help` (except if [`question help button`](https://docassemble.org/docs/initial.html#question%20help%20button)
is enabled).
Mako functions: `def`[¶](https://docassemble.org/docs/initial.html#def)
========================================================================
def: adorability
mako: |
<%def name="describe_as_adorable(person)"> \
${ person } is adorable. \
%def>
A `def` block allows you to define [Mako](https://www.makotemplates.org/)
“[def](https://docs.makotemplates.org/en/latest/defs.html)
” functions that you can re-use later in your question or document templates. You can use the above function by doing:
---
question: |
${ describe_as_adorable(spouse) } Am I right?
yesno: user_agrees_spouse_is_adorable
usedefs:
- adorability
---
Due to the way **docassemble** parses interviews, the `def` block needs to be defined before it is used.
Note the `\` marks at the end of the lines in the `mako` definition. Without these marks, there would be an extra newline inserted. You may or may not want this extra newline.
Setting the `default role`[¶](https://docassemble.org/docs/initial.html#default%20role)
========================================================================================
default role: client
code: |
if user_logged_in() and user_has_privilege('advocate'):
user = advocate
role = 'advocate'
else:
user = client
role = 'client'
set_info(user=user, role=role)
---
If your interview uses the [roles](https://docassemble.org/docs/roles.html)
feature for multi-user interviews, the `default role` specifier will define what role or roles will be required for any question that does not contain an explicit `role` specifier.
When you use the [roles](https://docassemble.org/docs/roles.html)
feature, you need to have some way of telling your interview logic what the role of the interviewee is.
If you include `code` within the same block as your `default role` specifier, that code will be executed every time the interview logic is processed, as if it was marked as `initial`. For this reason, any `default role` specifier that contains code should be placed earlier in the interview file than any `mandatory` questions or [`code` blocks](https://docassemble.org/docs/code.html)
.
In the example above, the interview has two roles: “client” and “advocate”. The special variable `role` is set in the `code` block, which is executed every time the interview logic is processed.
In addition, the [`set_info()`](https://docassemble.org/docs/functions.html#set_info)
function is called. This lets the linguistic functions know who the `user` is, so that questions can ask “What is your date of birth?” or “What is John Smith’s date of birth” depending on whether the current user is John Smith or not.
Setting the `default language`[¶](https://docassemble.org/docs/initial.html#default%20language)
================================================================================================
---
default language: es
---
This sets the language to use for all of the remaining questions in the file for which the [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
is not specified. The purpose of this is to save typing; otherwise you would have to set the [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
for each question. Note that this does not extend to questions in [`include`](https://docassemble.org/docs/initial.html#include)
d files.
If your interview only uses one language, it is not necessary to (and probably not a good idea to) set a `default language`.
See [language support](https://docassemble.org/docs/language.html)
for more information about how to create [multi-lingual interviews](https://docassemble.org/docs/language.html)
. See [question modifiers](https://docassemble.org/docs/modifiers.html)
for information about the `language` setting of a question.
Translation files[¶](https://docassemble.org/docs/initial.html#translations)
=============================================================================
One way that **docassemble** supports [multi-lingual interviews](https://docassemble.org/docs/language.html)
is through the [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
on a [`question`](https://docassemble.org/docs/questions.html#question)
and the [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block, which sets a default value for the [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
. Your interview can contain [`question`](https://docassemble.org/docs/questions.html#question)
s in English that don’t have a [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
, and [`question`](https://docassemble.org/docs/questions.html#question)
s in French that have the `language: fr` modifier set. If the current language in an interview (as determined by the [`set_language()`](https://docassemble.org/docs/functions.html#set_language)
function) is French (`fr`), then when **docassemble** seeks a block to set a given variable, it will search the French blocks first.
This method of creating [multi-lingual interviews](https://docassemble.org/docs/language.html)
is good if the person who translates text from English to French is someone who understands how **docassemble** [YAML](https://en.wikipedia.org/wiki/YAML)
files work.
There is another method of creating [multi-lingual interviews](https://docassemble.org/docs/language.html)
that may be preferable if the translator is someone who does not understand how **docassemble** [YAML](https://en.wikipedia.org/wiki/YAML)
files work. This second method extracts the phrases from an interview (specifically, everywhere in the YAML where [Mako](https://www.makotemplates.org/)
templating is allowed) and lists them all in an Excel spreadsheet. The spreadsheet can then be given to a French translator, and the translator fills out a column in the spreadsheet with the translation of each phrase. Then the completed spreadsheet can be stored in the [sources](https://docassemble.org/docs/playground.html#sources)
folder of a package and referenced in an interview using the `translations` block:
translations:
- custody.xlsx
Then, if the current language in an interview is French, the interview will use the French version of each phrase.
This allows you to support multi-lingual interviews while having a code base that is all in one language.
To obtain such a spreadsheet for a given interview, visit the [Utilities](https://docassemble.org/docs/admin.html#utilities)
.
The `translations` block is only capable of defining translations for blocks that come after the `translations` block. Therefore, it is a good practice to make sure that the `translations` block is placed as one of the very first blocks in your interview [YAML](https://en.wikipedia.org/wiki/YAML)
file.
Since the `translations` block only translates phrases in which [Mako](https://www.makotemplates.org/)
templating is allowed, there are some parts of the YAML that are not translatable through a `translations` block. For example, `sections` blocks do not use [Mako](https://www.makotemplates.org/)
and thus do not use the `translations` system.
For more information about using translation files, read the section [Download an interview phrase translation file](https://docassemble.org/docs/admin.html#translation%20file)
. See [language support](https://docassemble.org/docs/language.html)
for more information about how to create [multi-lingual interviews](https://docassemble.org/docs/language.html)
. See [question modifiers](https://docassemble.org/docs/modifiers.html)
for information about the `language` setting of a question. See also the [`default language`](https://docassemble.org/docs/initial.html#default%20language)
block and the [`default language` specifier under `metadata`](https://docassemble.org/docs/initial.html#metadata%20default%20language)
.
Default screen parts[¶](https://docassemble.org/docs/initial.html#default%20screen%20parts)
============================================================================================
The `default screen parts` allows you to write [Mako](https://www.makotemplates.org/)
and [Markdown](https://daringfireball.net/projects/markdown/)
to create text that will appear by default in parts of the screen on every page.
default screen parts:
under: |
You have seen
${ quantity_noun(counter, 'screen') }
of this interview so far.
help label: |
About
help button color: warning
back button label: |
Back
back button color: secondary
continue button label: |
Go to next step
continue button color: success
subtitle: |
A _groovy_ interview
pre: |
The text below **does not**
constitute legal advice.
submit: |
Please re-read the question
before moving forward.
post: |
This interview was generously
sponsored by Example, Inc.
css class: normalquestion
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/default-screen-parts.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/default-screen-parts.yml&reset=1 "Click to try this interview")
When using this, make sure you do not cause your interview to go into an infinite loop. If any of your screen parts require information from the user, your interview will need to pose a [`question`](https://docassemble.org/docs/questions.html#question)
to the user to gather that information, but in order to pose the [`question`](https://docassemble.org/docs/questions.html#question)
, it will need the information. To avoid this, you can use the [`defined()`](https://docassemble.org/docs/functions.html#defined)
function, the [`showifdef()`](https://docassemble.org/docs/functions.html#showifdef)
function, or other methods.
In a multi-lingual interview, you can use multiple `default screen parts` blocks with different [`language` modifier](https://docassemble.org/docs/modifiers.html#language)
s. If you are using a [`translations`](https://docassemble.org/docs/initial.html#translations)
block, the screen parts in a `default screen parts` block can be translated through the XLSX or XLIFF files that your [`translations`](https://docassemble.org/docs/initial.html#translations)
block references.
For information about other ways to set defaults for different parts of the screens during interviews, see the [screen parts](https://docassemble.org/docs/questions.html#screen%20parts)
section.
Custom validation messages[¶](https://docassemble.org/docs/initial.html#default%20validation%20messages)
=========================================================================================================
The **docassemble** user interface uses the [jQuery Validation Plugin](https://jqueryvalidation.org/)
to pop up messages when the user does not enter information for a required field, or if a number does not meet a minimum, or if an e-mail address is not valid, and other circumstances.
The messages that are displayed can be customized in a number of ways.
On a server-wide level, the messages can be customized the same way other built-in phrases in **docassemble** can be customized: using the [`words`](https://docassemble.org/docs/config.html#words)
directive in the [Configuration](https://docassemble.org/docs/config.html)
to make a “translation table” between the built-in text to the values you want to be used in their place.
On an interview-wide level, the messages can be customized using a `default validation messages` block:
default validation messages:
required: |
I would really like to know this.
Please tell me!
max: |
No more than %s, please!
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/default-validation-messages.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/default-validation-messages.yml&reset=1 "Click to try this interview")
Within an individual field in a `question`, you can use the [`validation messages`](https://docassemble.org/docs/fields.html#validation%20messages)
field modifier to define what validation messages should be used. These will override the `default validation messages`.
Each validation message has a code. In the above example, the codes used were `required` and `max`. The complete list of codes is:
* `required` for `This field is required.` There is a default text transformation for language `en` that translates this to “You need to fill this in.” This is the standard message that users see when they fail to complete a required field.
* `multiple choice required` for `You need to select one.` This is shown for multiple-choice fields.
* `combobox required` for `You need to select one or type in a new value.` This is shown for [`combobox`](https://docassemble.org/docs/fields.html#combobox)
fields.
* `checkboxes required` for `Check at least one option, or check "%s"` This is shown for [`checkboxes`](https://docassemble.org/docs/fields.html#fields%20checkboxes)
fields with a “None of the above” option. It is also used for [`yesno`](https://docassemble.org/docs/fields.html#fields%20yesno)
fields with [`uncheck others`](https://docassemble.org/docs/fields.html#uncheck%20others)
set, which is shown when the user does not check any of the [`yesno`](https://docassemble.org/docs/fields.html#fields%20yesno)
fields. `%s` is a code that is replaced with the label of the “None of the above” choice.
* `minlength` for `You must type at least %s characters.` This is shown when there is a [`minlength`](https://docassemble.org/docs/fields.html#minlength)
field modifier.
* `maxlength` for `You cannot type more than %s characters.` This is shown when there is a [`maxlength`](https://docassemble.org/docs/fields.html#maxlength)
field modifier.
* `checkbox minmaxlength` for `Please select exactly %s.` This is shown when there is a [`checkboxes`](https://docassemble.org/docs/fields.html#fields%20checkboxes)
field with a [`minlength`](https://docassemble.org/docs/fields.html#minlength)
field modifier that is the same as the [`maxlength`](https://docassemble.org/docs/fields.html#maxlength)
field modifier.
* `checkbox minlength` for `Please select at least %s.` This is shown when there is a [`checkboxes`](https://docassemble.org/docs/fields.html#fields%20checkboxes)
field with a [`minlength`](https://docassemble.org/docs/fields.html#minlength)
field modifier set to something other than `1`.
* `checkbox maxlength` for `Please select no more than %s.` This is shown when there is a [`checkboxes`](https://docassemble.org/docs/fields.html#fields%20checkboxes)
field with a [`maxlength`](https://docassemble.org/docs/fields.html#maxlength)
field modifier.
* `multiselect minmaxlength` for `Please select exactly %s.` This is shown when there is a [`multiselect`](https://docassemble.org/docs/fields.html#fields%20multiselect)
field with a [`minlength`](https://docassemble.org/docs/fields.html#minlength)
field modifier that is the same as the [`maxlength`](https://docassemble.org/docs/fields.html#maxlength)
field modifier.
* `multiselect minlength` for `Please select at least %s.` This is shown when there is a [`multiselect`](https://docassemble.org/docs/fields.html#fields%20multiselect)
field with a [`minlength`](https://docassemble.org/docs/fields.html#minlength)
field modifier set to something other than `1`.
* `multiselect maxlength` for `Please select no more than %s.` This is shown when there is a [`multiselect`](https://docassemble.org/docs/fields.html#fields%20multiselect)
field with a [`maxlength`](https://docassemble.org/docs/fields.html#maxlength)
field modifier.
* `date` for `You need to enter a valid date.` This is shown for [`date`](https://docassemble.org/docs/fields.html#date)
fields when the text entered is not an actual date.
* `date minmax` for `You need to enter a date between %s and %s.` This is shown for [`date`](https://docassemble.org/docs/fields.html#date)
fields with [`min`](https://docassemble.org/docs/fields.html#min)
and [`max`](https://docassemble.org/docs/fields.html#max)
set.
* `date min` for `You need to enter a date on or after %s.` This is shown for [`date`](https://docassemble.org/docs/fields.html#date)
fields with [`min`](https://docassemble.org/docs/fields.html#min)
set.
* `date max` for `You need to enter a date on or before %s.` This is shown for [`date`](https://docassemble.org/docs/fields.html#date)
fields with [`max`](https://docassemble.org/docs/fields.html#max)
set.
* `time` for `You need to enter a valid time.` This is shown for [`time`](https://docassemble.org/docs/fields.html#time)
fields.
* `datetime` for `You need to enter a valid date and time.` This is shown for [`datetime`](https://docassemble.org/docs/fields.html#datetime)
fields.
* `email` for `You need to enter a complete e-mail address.` This is shown for [`email`](https://docassemble.org/docs/fields.html#email)
fields.
* `number` for `You need to enter a number.` This is shown for numeric fields (`number`, `currency`, `float`, and `integer`) when the input is not valid.
* `min` for `You need to enter a number that is at least %s.` This is shown for numeric fields with a [`min`](https://docassemble.org/docs/fields.html#min)
field modifier.
* `max` for `You need to enter a number that is at most %s.` This is shown for numeric fields with a [`max`](https://docassemble.org/docs/fields.html#max)
field modifier.
* `file` for `You must provide a file.` This is shown for [file upload fields](https://docassemble.org/docs/fields.html#minlength)
.
* `accept` for `Please upload a file with a valid file format.` This is shown for [file upload fields](https://docassemble.org/docs/fields.html#minlength)
with an [`accept`](https://docassemble.org/docs/fields.html#accept)
field modifier.
Machine learning training data[¶](https://docassemble.org/docs/initial.html#machine%20learning%20storage)
==========================================================================================================
If you use [machine learning](https://docassemble.org/docs/ml.html#howtouse)
in your interviews, then by default, **docassemble** will use training data associated with the particular interview in the particular [package](https://docassemble.org/docs/packages.html)
in which the interview resides.
If you would like your interview to share training data with another interview, you can use the `machine learning storage` specifier to point to the training data of another interview.
For example, suppose you have developed an interview called `child_custody.yml` that uses [machine learning](https://docassemble.org/docs/ml.html#howtouse)
, and you have built rich training sets for variables within this interview. Then you decide to develop another interview, in the same [package](https://docassemble.org/docs/packages.html)
, called `child_support.yml`, which uses many of the same variables. It would be a lot of work to maintain two identical training sets in two places.
In this scenario, you can add the following block to the `child_support.yml` interview:
---
machine learning storage: ml-child_custody.json
---
`ml-child_custody.json` is the name of a file in the `data/sources` directory of the [package](https://docassemble.org/docs/packages.html)
. This file contains the training data for the `child-custody.yml` interview. The naming convention for these data files is to start with the name of the interview [YAML](https://en.wikipedia.org/wiki/YAML)
file, add `ml-` to the beginning, and replace `.yml` with `.json`.
Now, both the `child-custody.yml` and `child-support.yml` interviews will use `ml-child_custody.json` as “storage” area for training data. In the [Training](https://docassemble.org/docs/ml.html#train)
interface, you will find this data set under the name `child_custody`.
If you had run the `child-support.yml` interview before adding `machine learning storage`, you may still see a data set called `child-support` in the [Training](https://docassemble.org/docs/ml.html#train)
interface. If you are using the [Playground](https://docassemble.org/docs/playground.html)
, you may see a file called `ml-child-support.json` in the [Sources folder](https://docassemble.org/docs/playground.html#sources)
. To get rid of this, go into the [Playground](https://docassemble.org/docs/playground.html)
and delete the `ml-child-support.json` file from the [Sources folder](https://docassemble.org/docs/playground.html#sources)
. Then go into the [Training](https://docassemble.org/docs/ml.html#train)
interface and delete any “items” that exist within the `child-support` interview.
If you want, you can set `machine learning storage` to a name that does not correspond with an actual interview. For example, you could include `machine learning storage: ml-family-law.json` in both the `child-custody.yml` and `child-support.yml` interviews. Even though there is no interview called `family-law.yml`, this will still work. If you are using the [Playground](https://docassemble.org/docs/playground.html)
, a file called `ml-family-law.json` will automatically be created in the `Sources folder`.
You can also share “storage” areas across packages. Suppose you are working within a package called `docassemble.missourifamilylaw`, but you want to take advantage of training sets in a package called `docassemble.generalfamilylaw`. You can write:
---
machine learning storage: docassemble.generalfamilylaw:data/sources/ml-family.json
---
For more information about managing training data, see the [machine learning](https://docassemble.org/docs/ml.html#howtouse)
section on [packaging your training sets](https://docassemble.org/docs/ml.html#packaging)
Optional `features`[¶](https://docassemble.org/docs/initial.html#features)
===========================================================================
The `features` block sets some optional features of the interview.
Whether debugging features are available[¶](https://docassemble.org/docs/initial.html#debug)
---------------------------------------------------------------------------------------------
If the [`debug` directive](https://docassemble.org/docs/config.html#debug)
in the [Configuration](https://docassemble.org/docs/config.html)
is `True`, then by default, the main navigation bar will contain a “Source” link that shows information about how the interview arrived at the question being shown. If the [`debug` directive](https://docassemble.org/docs/config.html#debug)
is `False`, then this will not be shown.
This can be overridden in the `features` by setting `debug` to `True` or `False` depending on the behavior you want.
The following example demonstrates turning the `debug` feature off.
features:
debug: False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/debug-mode.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/debug-mode.yml&reset=1 "Click to try this interview")
On the server that hosts the demonstration interviews, the [`debug` directive](https://docassemble.org/docs/config.html#debug)
is `True`, so the “Source” link is normally shown. Setting `debug: False` makes the “Source” link disappear.
Note that if you are running an interview in the [Playground](https://docassemble.org/docs/playground.html)
, the “Source” link is always shown, because the [Playground](https://docassemble.org/docs/playground.html)
is a development and testing environment. You will need to install your package to see the effect of the `debug` feature.
Whether interview is centered[¶](https://docassemble.org/docs/initial.html#centered)
-------------------------------------------------------------------------------------
If you do not want your interview questions to be centered on the screen, set `centered` to `False`.
features:
centered: False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/centered.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/centered.yml&reset=1 "Click to try this interview")
Widening the screen when `right` is used[¶](https://docassemble.org/docs/initial.html#wide%20side%20by%20side)
---------------------------------------------------------------------------------------------------------------
If you want the effect of `centered: False` when there is text in the `right` screen part, set `wide side by side` to `True`.
features:
wide side by side: True
Progress bar[¶](https://docassemble.org/docs/initial.html#progress%20bar)
--------------------------------------------------------------------------
The `progress bar` feature controls whether a progress bar is shown during the interview. You can use the [`progress`](https://docassemble.org/docs/modifiers.html#progress)
modifier or the [`set_progress()`](https://docassemble.org/docs/functions.html#set_progress)
function to indicate the setting of the progress bar.
features:
progress bar: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/progress-features.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/progress-features.yml&reset=1 "Click to try this interview")
If you want the progress bar to display the percentage, include `show progress bar percentage: True`:
features:
progress bar: True
show progress bar percentage: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/progress-features-percentage.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/progress-features-percentage.yml&reset=1 "Click to try this interview")
By default, if you do not set the [`progress`](https://docassemble.org/docs/modifiers.html#progress)
modifier on a [`question`](https://docassemble.org/docs/questions.html#question)
, then each time the user takes a step, the progress bar will advance 5% of the way toward the end.
The 5% figure is known as the `progress bar multiplier` and it is configurable:
features:
progress bar: True
progress bar multiplier: 0.01
The default is 0.05.
If you set `progress bar method: stepped`, the progress bar advances a different way when there is no [`progress`](https://docassemble.org/docs/modifiers.html#progress)
modifier.
features:
progress bar: True
progress bar method: stepped
Instead of advancing toward 100%, it advances toward the next greatest [`progress`](https://docassemble.org/docs/modifiers.html#progress)
value that is defined on a [`question`](https://docassemble.org/docs/questions.html#question)
in the interview. (Note that **docassemble** cannot predict the future, so whether the [`question`](https://docassemble.org/docs/questions.html#question)
with the next highest [`progress`](https://docassemble.org/docs/modifiers.html#progress)
value will actually be reached is unknown; **docassemble** just looks at all the [`question`](https://docassemble.org/docs/questions.html#question)
s in the interview that have [`progress`](https://docassemble.org/docs/modifiers.html#progress)
values defined.) The amount by which it advances is determined by `progress bar multiplier`.
To use the default method for advancing the progress bar, omit `progress bar method`, or set it to `default`.
features:
progress bar: True
progress bar method: default
By default, the progress bar will not regress if the `progress` specifier of the current `question` has a lower value than the current progress value. You can turn off this limit by setting `progress can go backwards` to `True`:
features:
progress bar: True
progress can go backwards: True
Navigation bar[¶](https://docassemble.org/docs/initial.html#navigation%20bar)
------------------------------------------------------------------------------
The `navigation` feature controls whether a secondary navigation bar is shown during the interview to show users the sections of the interview. You can use the [`sections`](https://docassemble.org/docs/initial.html#sections)
initial block or the [`nav.set_sections()`](https://docassemble.org/docs/functions.html#DANav.set_sections)
function to define the sections of your interview. The [`section`](https://docassemble.org/docs/modifiers.html#section)
modifier or the [`nav.set_section()`](https://docassemble.org/docs/functions.html#DANav.set_section)
function can be used to change the current section.
sections:
- Introduction
- About you:
- Contact info
- Demographics
- Preferences
- Conclusion
---
features:
navigation: True
progress bar: True
---
mandatory: True
code: |
menu_items = [ action_menu_item('Roadmap', 'road_map') ]
---
initial: True
code: |
if returning_user(minutes=0.5):
welcome_back
---
mandatory: True
question: |
Welcome to the interview
subquestion: |
If you are not on a
smartphone-sized device,
you should see a navigation
bar to the left.
field: sees_nav_bar
---
mandatory: True
question: |
I am going to ask you some
questions about yourself.
field: intro_to_about_you
section: About you
---
mandatory: True
question: |
What is your name?
fields:
- First Name: first_name
- Last Name: last_name
section: Contact info
---
mandatory: True
question: |
What is your e-mail address?
fields:
- E-mail: email_address
datatype: email
---
mandatory: True
question: |
What is your gender?
field: gender
choices:
- Male
- Female
- Something else
section: Demographics
---
mandatory: True
question: |
What kind of belly button
do you have?
subquestion: |
To see what a user would
see after returning to
the interview after a period
of absence, try waiting
thirty seconds, then
[click into the\
interview](${ interview_url(local=True) }).
In addition, there is a similar
screen available on the Menu in the
upper-right, under "Roadmap."
field: belly_button
choices:
- Innie
- Outie
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Favorite fruit: favorite_fruit
section: Preferences
---
mandatory: True
question: |
What is your favorite vegetable?
fields:
- Favorite vegetable: favorite_vegetable
---
progress: 100
mandatory: True
question: Thank you.
subquestion: |
${ first_name },
Your answers mean a lot to me.
I am going to go eat some
${ favorite_vegetable }
now.
section: Conclusion
---
event: welcome_back
question: |
Welcome back!
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to pick up
where you left off.
buttons:
Continue: continue
---
event: road_map
question: |
Roadmap
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to resume the
interview.
buttons:
Continue: continue
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections.yml&reset=1 "Click to try this interview")
If you want the secondary navigation bar to be horizontal across the top of the page, set `navigation` to `horizontal`:
features:
navigation: horizontal
progress bar: True
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-horizontal.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-horizontal.yml&reset=1 "Click to try this interview")
On small screens, it is not possible for the vertical navigation bar to display, so it is hidden, and the horizontal navigation bar is shown in its place.
The horizontal navigation bar functions differently than the vertical navigation. Subsections are shown if the user is inside a subsection, but otherwise they are not visible. There is no caret symbol for opening and closing subsections.
On smaller screens, the horizontal navigation bar uses less padding and a smaller font. This works well if there are only a few sections, but if there are too many sections, it may be impossible to fit them on the screen.
If you want the navigation bar to be hidden on small screens, set `small screen navigation: False` in your `features`:
sections:
- Introduction
- About you:
- Contact info
- Demographics
- Preferences
- Conclusion
---
features:
navigation: True
small screen navigation: False
progress bar: True
---
mandatory: True
code: |
menu_items = [ action_menu_item('Roadmap', 'road_map') ]
---
initial: True
code: |
if returning_user(minutes=0.5):
welcome_back
---
mandatory: True
question: |
Welcome to the interview
field: sees_nav_bar
---
mandatory: True
question: |
I am going to ask you some
questions about yourself.
field: intro_to_about_you
section: About you
---
mandatory: True
question: |
What is your name?
fields:
- First Name: first_name
- Last Name: last_name
section: Contact info
---
mandatory: True
question: |
What is your e-mail address?
fields:
- E-mail: email_address
datatype: email
---
mandatory: True
question: |
What is your gender?
field: gender
choices:
- Male
- Female
- Something else
section: Demographics
---
mandatory: True
question: |
What kind of belly button
do you have?
subquestion: |
To see what a user would
see after returning to
the interview after a period
of absence, try waiting
thirty seconds, then
[click into the\
interview](${ interview_url(local=True) }).
In addition, there is a similar
screen available on the Menu in the
upper-right, under "Roadmap."
field: belly_button
choices:
- Innie
- Outie
---
mandatory: True
question: |
What is your favorite fruit?
fields:
- Favorite fruit: favorite_fruit
section: Preferences
---
mandatory: True
question: |
What is your favorite vegetable?
fields:
- Favorite vegetable: favorite_vegetable
---
progress: 100
mandatory: True
question: Thank you.
subquestion: |
${ first_name },
Your answers mean a lot to me.
I am going to go eat some
${ favorite_vegetable }
now.
section: Conclusion
---
event: welcome_back
question: |
Welcome back!
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to pick up
where you left off.
buttons:
Continue: continue
---
event: road_map
question: |
Roadmap
subquestion: |
You are currently in the
**${ nav.get_section(display=True) }**
section.
${ nav }
Press "Continue" to resume the
interview.
buttons:
Continue: continue
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-small-screen-false.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-small-screen-false.yml&reset=1 "Click to try this interview")
If you want the navigation bar to be converted into a dropdown on small screens, set `small screen navigation: dropdown` in your `features`:
features:
navigation: True
small screen navigation: dropdown
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/sections-small-screen-dropdown.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/sections-small-screen-dropdown.yml&reset=1 "Click to try this interview")
The dropdown version shows all of the sections, including subsections that are not active.
Back button style[¶](https://docassemble.org/docs/initial.html#question%20back%20button)
-----------------------------------------------------------------------------------------
By default, there is a “Back” button located in the upper-left corner of the page. (However, the “Back” button is not present when the user is on the first page of an interview, or the [`prevent_going_back()`](https://docassemble.org/docs/functions.html#prevent_going_back)
function has been used, or the [`prevent going back`](https://docassemble.org/docs/modifiers.html#prevent%20going%20back)
modifier is in use.)
Whether this back button is present can be controlled using the `navigation back button` feature. This will hide the “Back” button:
features:
navigation back button: False
You can also place a “Back” button inside the body of a question, next to the other buttons on the screen, by setting the `question back button` feature to `True` (the default is `False`).
features:
question back button: True
navigation back button: False
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-back-button.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-back-button.yml&reset=1 "Click to try this interview")
You can also place a “Back” button inside the body of a question on some questions but not others, using the [`back button`](https://docassemble.org/docs/modifiers.html#back%20button)
modifier.
Help tab style[¶](https://docassemble.org/docs/initial.html#question%20help%20button)
--------------------------------------------------------------------------------------
When [`interview help`](https://docassemble.org/docs/initial.html#interview%20help)
is available, or the [`help`](https://docassemble.org/docs/modifiers.html#help)
modifier is present on a question, the “Help” tab will be present in the navigation bar. When the [`help`](https://docassemble.org/docs/modifiers.html#help)
modifier is present, the “Help” tab is highlighted yellow and marked with a yellow star. When the user presses the help tab, the help screen will be shown.
If you set the `question help button` to `True`, users will be able to access the help screen by pressing a “Help” button located within the body of the question, to the right of the other buttons on the page. When `question help button` is `True`, the “Help” tab will not be highlighted yellow.
Here is an interview in which the `question help button` is not enabled (which is the default).
features:
question help button: False
---
interview help:
label: More info
heading: About this interview
content: |
This is an interview about fruit.
---
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
help:
label: Huh?
heading: What is a fruit?
content: |
A fruit is a fleshy edible
part of a plant that has
seeds.
---
question: |
What is your favorite color?
fields:
- Color: favorite_color
help:
heading: What is a color?
content: |
Every photon has a frequency, which
determines its color.
---
question: |
What is your favorite vegetable?
fields:
- Vegetable: favorite_vegetable
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-help-button-off.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-help-button-off.yml&reset=1 "Click to try this interview")
Here is the same interview, with the `question help button` feature enabled:
features:
question help button: True
---
interview help:
label: More info
heading: About this interview
content: |
This is an interview about fruit.
---
question: |
What is your favorite fruit?
fields:
- Fruit: favorite_fruit
help:
label: Huh?
heading: What is a fruit?
content: |
A fruit is a fleshy edible
part of a plant that has
seeds.
---
question: |
What is your favorite color?
fields:
- Color: favorite_color
help:
heading: What is a color?
content: |
Every photon has a frequency, which
determines its color.
---
question: |
What is your favorite vegetable?
fields:
- Vegetable: favorite_vegetable
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/question-help-button.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/question-help-button.yml&reset=1 "Click to try this interview")
Note that when `question help button` is enabled, the label for the help tab in the navigation bar always defaults to “Help” or to the `label` of the [`interview help`](https://docassemble.org/docs/initial.html#interview%20help)
, and it is not highlighted yellow when question-specific help is available.
Positioning labels above fields[¶](https://docassemble.org/docs/initial.html#labels%20above%20fields)
------------------------------------------------------------------------------------------------------
By default, the **docassemble** user interface uses [Bootstrap](https://getbootstrap.com/)
’s [horizontal form](https://getbootstrap.com/docs/5.2/forms/layout/#horizontal-form)
style. If you want your interview to use the [Bootstrap](https://getbootstrap.com/)
’s [standard](https://getbootstrap.com/docs/5.2/forms/layout/#utilities/)
style, set `labels above fields` to `True`:
features:
labels above fields: True
Floating labels[¶](https://docassemble.org/docs/initial.html#floating%20labels)
--------------------------------------------------------------------------------
By default, the **docassemble** user interface uses [Bootstrap](https://getbootstrap.com/)
’s [horizontal form](https://getbootstrap.com/docs/5.2/forms/layout/#horizontal-form)
style. If you want your interview to use the [Bootstrap](https://getbootstrap.com/)
’s [floating labels](https://getbootstrap.com/docs/5.2/forms/floating-labels/)
style, set `floating labels` to `True`:
features:
floating labels: True
If floating labels are used, then the [`hint` field modifier](https://docassemble.org/docs/fields.html#hint)
and the [`help` field modifier](https://docassemble.org/docs/fields.html#help)
are unavailable.
Suppress autofill[¶](https://docassemble.org/docs/initial.html#suppress%20autofill)
------------------------------------------------------------------------------------
By default, web browsers try to be helpful by presenting you with a drop-down menu of past values you have used when you filled in a form field. Luckily, it is possible to tell the browser not to autofill form fields. If you want to tell the browser not to autofill, set `suppress autofill` to `True`.
features:
suppress autofill: True
Hiding the standard menu items[¶](https://docassemble.org/docs/initial.html#hide%20standard%20menu)
----------------------------------------------------------------------------------------------------
By default, the menu in the corner provides logged-in users with the ability to edit their “Profile” and the ability to go to “My Interviews,” which is a list of interview sessions they have started. If you want to disable these links, you can use the `hide standard menu` specifier:
features:
hide standard menu: True
If you want to add any of these links manually, or add them with different names, you can do so with the [`menu_items` special variable](https://docassemble.org/docs/special.html#menu_items)
and the [`url_of()`](https://docassemble.org/docs/functions.html#url_of)
function.
mandatory: True
code: |
menu_items = [\
{'label': 'Edit my Profile', 'url': url_of('profile')},\
{'label': 'Saved Sessions', 'url': url_of('interviews')}\
]
Hiding the menu and login interface entirely[¶](https://docassemble.org/docs/initial.html#hide%20corner%20interface)
---------------------------------------------------------------------------------------------------------------------
The interface in the upper-right corner lets the user log in, or shows a menu, shows the exit button, and/or shows the [`navigation bar html`](https://docassemble.org/docs/initial.html#navigation%20bar%20html)
. This interface can be removed by setting `hide corner interface` to `True`.
features:
hide corner interface: True
Javascript and CSS files[¶](https://docassemble.org/docs/initial.html#javascript)
----------------------------------------------------------------------------------
If you are a web developer and you know how to write [HTML](https://en.wikipedia.org/wiki/HTML)
, [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
, and [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
, you can embed [HTML](https://en.wikipedia.org/wiki/HTML)
in your interview text. You can also bring [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
and [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
files into the user’s browser.
For example, the following interview brings in a [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
file, [`my-functions.js`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/static/my-functions.js)
, and a [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
file, [`my-styles.css`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/static/my-styles.css)
, into the user’s browser. These files are located in the `data/static` folder of the same [package](https://docassemble.org/docs/packages.html)
in which the interview is located.
features:
javascript: my-functions.js
css: my-styles.css
---
mandatory: True
question: |
A test of Javascript and CSS
subquestion: |
[GitHub](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/questions/examples/external_files.yml "Open on GitHub")
[](https://demo.docassemble.org/interview?i=docassemble.base:data/questions/examples/external_files.yml&reset=1 "Click to try this interview")
The contents of [`my-functions.js`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/static/my-functions.js)
are:
$(document).on('daPageLoad', function(){
$(".groovy").html("I am purple");
});
The contents of [`my-styles.css`](https://github.com/jhpyle/docassemble/blob/master/docassemble_base/docassemble/base/data/static/my-styles.css)
are:
.groovy {
color: purple;
}
You can write whatever you want in these files; they will simply be loaded by the user’s browser. Note that your [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
files will be loaded after [jQuery](https://jquery.com/)
is loaded, so your code can use [jQuery](https://jquery.com/)
, as this example does.
If you have [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
code that you want to run after each screen of the interview is loaded, attach a [jQuery](https://jquery.com/)
event handler to `document` for the event `daPageLoad`, which is a **docassemble**\-specific event that is triggered after each screen loads. (Since **docassemble** uses [Ajax](https://en.wikipedia.org/wiki/Ajax_(programming))
to load each new screen, if you attach code using [jQuery](https://jquery.com/)
’s [`ready()`](https://api.jquery.com/ready/)
method, the code will run when the browser first loads, but not every time the user sees a new screen.) The example above demonstrates this; every time the page loads, the code will replace the contents of any element with the class `groovy`.
This example demonstrates bringing in [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
and [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
files that are located in the `data/static` directory of the same package as the interview. You can also refer to files in other packages:
features:
css: docassemble.demo:data/static/my.css
or on the internet at a URL:
features:
javascript: https://example.com/js/my-functions.js
Also, if you want to bring in multiple files, specify them with a [YAML](https://en.wikipedia.org/wiki/YAML)
list:
features:
css:
- my-styles.css
- https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css
javascript:
- https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js
- https://cdnjs.cloudflare.com/ajax/libs/offline-js/0.7.18/offline.min.js
If you want to include [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
or [JavaScript](https://en.wikipedia.org/wiki/JavaScript)
code in a specific question, rather than in all questions of your interview you can use the [`script`](https://docassemble.org/docs/modifiers.html#script)
and [`css`](https://docassemble.org/docs/modifiers.html#css)
modifiers.
The HTML of the screen showing a [`question`](https://docassemble.org/docs/questions.html#question)
contains a number of placeholder [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets)
classes that are not used for formatting, but that are available to facilitate customization:
* If a `question` is tagged with an [`id`](https://docassemble.org/docs/modifiers.html#precedence)
, the `
` will be given a class beginning with `question-` followed by the [`id`](https://docassemble.org/docs/modifiers.html#precedence)
, except that the [`id`](https://docassemble.org/docs/modifiers.html#precedence)
will be transformed into lowercase and non-alphanumeric characters will be converted into hyphens. For example, if the [`id`](https://docassemble.org/docs/modifiers.html#precedence)
is `Intro screen`, the class name will be `question-intro-screen`.
* `