1. Generally
  2. Blocks
    1. Generally
    2. Question Block
      1. Generally on Question Blocks
      2. Example of a Question Block
  3. Buttons
    1. Custom Button
    2. Exit Button
  4. Disable Input Field
    1. Scenario
    2. Two Ways
    3. disable if
    4. JavaScript
  5. Functions
    1. Generally
    2. indefinite_article()
    3. message()
  6. Give Users Information
    1. Display User’s Answer with Exit Button
    2. Help
    3. Progressive Disclosure
    4. note
  7. Groups
    1. Generally
    2. Types of Groups in docassemble
    3. Lists
      1. Generally on Lists
      2. Lists in Python
      3. In docassemble: DAList class
      4. How to Initialize a List
    4. Dictionaries
  8. If/Then
    1. If/Then Using Mako
    2. If/Then Using Jinja2 in Word
    3. If/Then Using Python in Code Block
  9. Interview
    1. Set Title of Interview
    2. Share Interview
    3. Reset Questions and Restart Interview
  10. Objects
    1. Generally
    2. Initializing an Object
    3. Generic Object
      1. Generally
      2. Using Two DAList
      3. DAList.using, x.
      4. DAList.using, x[i]
  11. Variables
    1. Address
      1. Generally
      2. Text Attributes
      3. List States
      4. List Countries
      5. Return Address in Interview Using Mako
      6. Additional Resources on Address
    2. Ask User for More than One Variables
    3. Checkbox
      1. Generally
      2. Checkbox in Word
      3. Require Checkbox to Be Checked
    4. Date Variable
    5. Multiple Choice Variable
    6. Name
      1. Generally on Asking for a Name
      2. Separate variables
      3. Object for Name
      4. Generic Object for Name Using x[i]
    7. Number Variable
      1. Ask User for Number Variable
      2. Manipulate User’s Number Variable
    8. Reset Variable
    9. Set Variable
    10. Text Variable
      1. Ask User for Text Variable
    11. Yes/No
      1. Yes/No in Playground
      2. Yes/No in Word
  12. Word Template
    1. Generally
    2. Inline Variable
    3. Capitalize, Lower Case, Title Case
    4. Checkbox
    5. Conditional Paragraph
    6. Conditional Numbered Paragraph
    7. Format Numbers
    8. Multiple Choice
    9. Perform Math
    10. Singular/Plural
    11. Use a/an
    12. Yes/No in Word
    13. Resources on Jinja
  13. Example: Collecting Signatures
    1. The Problem
    2. The Advice
    3. The Code
    4. Comments on the Code
  14. Resources on docassemble

Generally

docassemble is a web app for document assembly.

docassemble uses:

Blocks

Generally

docassemble has several blocks:

Question Block

Generally on Question Blocks

Every interview screen that a docassemble user sees is generated either by a question block or by the message() function.1

See Question blocks, docassemble.

Example of a Question Block

Here is an example of a question block from Hello world example, docassemble:

---
 question: Hello, world!
 buttons:
   - Exit: exit
 mandatory: True
---

Buttons

Custom Button

In getField() JavaScript function, docassemble’s documentation shows an example of how you can use JavaScript to create an “Eat it” button on the screen:

question: |
  What do you want for dessert<span id="the_dessert"></span>?
fields: 
  - Dessert: dessert
  - html: |
      <a href="#" id="show_me" class="btn btn-primary">Eat it</a>
script: |
  <script>
    $("#show_me").click(function(){
      $("#the_dessert").html(" besides " + getField('dessert').value);
      return false;
    });
  </script>

Exit Button

Example of an exit button from Hello world example, docassemble:

---
 question: Hello, world!
 buttons:
   - Exit: exit
 mandatory: True
---

Disable Input Field

Scenario

Scenario: You want to display a field with a prefilled value but prevent the user from modifying the field.

Two Ways

There are two ways to disable an input field:

  1. disable if
  2. Use JavaScript

disable if

See disable if, docassemble.

N.B. You cannot use code inside disable if. So, for example, the following will not work:2

question: |
  Survey Title 
subquestion: |
fields: 
  - Please provide a ticket ID: q146005986
  required: False
  default: |
    ${ url_argument_variable }
  disable if: 
    code: |
      2 + 2 == 4

JavaScript

Jonathan Pyle gives the following example of how you can use JavaScript to disable a field:

question: |
  Survey Title 
subquestion: |
fields: 
  - Please provide a ticket ID: q146005986
    required: False
    default: |
    ${ url_argument_variable }
script: |
  % if url_argument_variable == `unknown`: 
  <script>
   $(getField(`q1460005986`)).prop('disabled', true); 
  </script>
  % endif

For more information on getField(), see getField() JavaScript function, docassemble.

Functions

Generally

See Functions, docassemble.

indefinite_article()

You use this function in a question block for question or subquestion in Mako.

Example:

question: | 
  Would you like to have ${ indefinite_article(fruit) }?
yesno: like_fruit 

See indefinite_article(), docassemble.

See DAList, docassemble, for an example of indefinite_article with objects.

message()

Every interview screen that a docassemble user sees is generated either by a question block or by the message() function.1

See message(), docassemble.

Give Users Information

Display User’s Answer with Exit Button

Scenario: The user is in an interview. The user answered the variable. In another screen, you want to display that variable back to the user.

To display the user’s answer, use ${ varname }.

Example:

---
question: Hello, ${ planet }!
buttons:
  - Exit: exit
mandatory: True
---
question: |
  What is your planet's name?
fields:
  - Your Planet: planet
---

Help

You can provide users help. See Providing help text to users, docassemble.

Progressive Disclosure

*See Progressive disclosure, docassemble.

note

You can have a note as its own field or as a field modifier. See note, docassemble.

Groups

Generally

A group is a way to gather information that is repeated. A group allows you to avoid typing the same question over and over.

Types of Groups in docassemble

docassemble has three types of groups: Lists, dictionaries, and sets. They correspond to their Python counterparts.

docassemble Python
list list
dictionary dict
set set

Lists

Generally on Lists

A list is a collection of variables that has a numerically defined order. The index for the elements starts at 0. You retrieve an element by referencing its number.

Lists in Python

(1) Create the list by defining its elements:

fruit = ['apple', 'orange', 'pear']

Note:

  • fruit[0] returns apple
  • fruit[1] returns orange
  • fruit[2] returns pear

(2) Add an element to the list by using append:

fruit.append('grape')

Note:

  • In Python, adding an element to a list is called “appending.”
  • The result of print(fruit) is now: ['apple', 'orange', 'pear', 'grape']

(3) Arrange the list in order by using sorted function:

print(sorted(fruit))

Note:

  • The result is now ['apple', 'grape', 'orange', 'pear']

In docassemble: DAList class

A list is an object. In docassemble, lists are part of the DAList class. A list can also contain objects.

How to Initialize a List

The following shows how to initialize a list in docassemble and how to tell what type of object the list will hold.

---
objects: 
  - my_list: DAList
	- my_peeps: DAList.using(object_type=Individual)

Dictionaries

A dictionary is a collection of variables in a lookup table and every element has a key and value. You retrieve the element by referencing its key.

If/Then

If/Then Using Mako

% if my_var:
Here's the optional text. It could have an optional ${variable} too.
% endif

(This code example is from GBLS/docassemble-workinggroup, GitHub.)

If/Then Using Jinja2 in Word

Example:

{% if test == 'My text' %}
Optional text. It can have an optional {{ variable }}.
{% endif %}

This example would leave an empty line. To avoid an empty line:

{%p if x >= 2 %}
Optional text. It can have an optional {{ variable }}.
{%p endif %}

(These code examples are from GBLS/docassemble-workinggroup, GitHub.)

If/Then Using Python in Code Block

---
question: What is your favorite number?
fields:
  - Number: favorite_number
    datatype: number
---
code: |
  if favorite_number == 42:
    inhabitant_count = 2
  else:
    inhabitant_count = 2000 + favorite_number * 45
---

Interview

Set Title of Interview

You can set the title of an interview by using the metadata block.

metadata:
  title: |
    Residential Lease Docs

Share Interview

You can share your interview with others in several ways:

Reset Questions and Restart Interview

Use the Restart special button:

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.

Objects

Generally

DAObject, Generic Object, Individual, Organization, Person

Initializing an Object

There are three ways to create (“initialize”) an object in docassemble: (1) objects block, (2) code block, or (3) importing (by using objects from file).

Creating objects explains that you can initialize an object using the objects block or a code block, but “[w]henever possible, you should use objects blocks rather than code to initialize your objects because objects blocks are clean and readable.”

Generic Object

Generally

See Reusable questions: generic object, docassemble.

See Index variables, docassemble.

Using Two DAList

objects:
  - fruit: DAList
  - vegetable: DAList
---
generic object: DAList
code: |
  x.there_are_any = True
---
generic object: DAList
question: |
  What is your ${ ordinal(i) } favorite ${ x.object_name() }?
fields:
  - label: |
      ${ x.object_name(capitalize=True) }
    field: x[i]
---
generic object: DAList
question: |
  Do you have any other favorite ${ noun_plural(x.object_name()) }?
yesno: x.there_is_another
---
mandatory: True
question: |
  Your favorites
subquestion: |
  Your favorite fruits are:
  
  % for item in fruit:
  * ${ item }
  % endfor
  
  Your favorite vegetables are:
  
  % for item in vegetable:
  * ${ item }
  % endfor

DAList.using, x.

√objects: 
  - grantor: DAList.using(object_type=Individual, there_are_any=True)
---
generic object: Individual
question: |
  What is ${ x.object_possessive('name') }?
fields:
  - First Name: x.name.first
    default: ${ x.first_name_hint() }
  - Middle Name: x.name.middle
    required: False
  - Last Name: x.name.last
    default: ${ x.last_name_hint() }
  - Suffix: x.name.suffix
    required: False
    code: |
      name_suffix()
---
generic object: DAList
question: |
  Are there are any more ${ noun_plural(x.object_name()) }?
yesno: x.there_is_another
---
question: |
 Hello, ${ grantor }!
mandatory: True

DAList.using, x[i]

objects: 
  - grantor: DAList.using(object_type=Individual, there_are_any=True)
---
generic object: DAList
question: |
  What is the name of the ${ ordinal(i) } ${ x.object_name() }?
fields:
  - First Name: x[i].name.first
    default: ${ x[i].first_name_hint() }
  - Middle Name: x[i].name.middle
    required: False
  - Last Name: x[i].name.last
    default: ${ x[i].last_name_hint() }
  - Suffix: x[i].name.suffix
    required: False
    code: |
      name_suffix()
---
generic object: DAList
question: |
  Are there are any more ${ noun_plural(x.object_name()) }?
yesno: x.there_is_another
---
question: |
 Hello, ${ grantor }!
mandatory: True

Variables

Address

Generally

An Address is a built-in class in docassemble. So, you have to state in the YAML file that you are using the Address object.

objects: 
  address: Address

Text Attributes

The Address object has several text attributes, which you ask for under fields:

attribute example
address “123 Main Street”
unit “Suite 100”
city “Springfield”
state “MA”
zip (or postal_code) “01199”
country “US”

List States

You can provide the user with a list of states:

fields:
  - State: address.state
    code: |
      states_list()
    required: False

List Countries

You can provide the user with a list of countries:

fields:
  - Country: address.country
    code: |
      countries_list()
    required: False

Return Address in Interview Using Mako

The following example shows several ways you can return the address in Mako.

mandatory: True
question: Your address
subquestion: |
  ### Address Block 
  
  Default:
  
  ${ address }
  
  Showing the country:
  
  ${ address.block(show_country=True) }
  
  International:
  
  ${ address.block(international=True) }
  
  ### State 
  
  You live in
  ${ state_name(address.state) },
  which is abbreviated
  ${ address.state }.

Use the .block() method to return a formatted address.

Additional Resources on Address

See

Ask User for More than One Variables

You can collect more than one variable on a screen.

Checkbox

Generally

See Checkboxes, docassemble.

Checkbox in Word

See Checkbox, below.

Require Checkbox to Be Checked

See Require a checkbox to be checked, documentation.

Date Variable

Dates, docassemble

datatype: date

dateutil.parser.parse, dateutil – “This module offers a generic date/time string parser which is able to parse most known formats to represent a date and/or time.”

Special date/time class DADateTime, docassemble

datetime Objects

Functions for working with dates and times

Multiple Choice Variable

The following example is from How to Use Datatypes in Docassemble, YouTube.

question: |
  What nuts do you like more - peanuts or pecans?
fields: 
  - Answer here: peanuts_or_pecans 
    choices: 
      - Peanuts 
      - Pecans 

The following examples shows how you can use code to access a multiple choice answer and set other variables:

---
question: |
  Who is the landlord? 
fields: 
  - Landlord: landlord 
    choices: 
      - Big Realty LLC
      - John Doe and Jane Doe
      - Other
  - Landlord's Name: landlord_name
    show if: 
      variable: landlord
      is: Other
---
code: |
  if landlord == "Big Realty LLC": 
    landlord_is_br = True
    landlord_is_jd = False
    landlord_is_other = False
if landlord == "John Doe and Jane Doe": 
    landlord_is_br = False
    landlord_is_jd = True
    landlord_is_other = False
if landlord == "Other": 
    landlord_is_br = False
    landlord_is_jd = False
    landlord_is_other = True

Name

Generally on Asking for a Name

There are two ways to ask for an person’s name: (1) Use separate variables or (2) use an object.

Separate variables

You can ask for each variable separately:

question: | 
  Grantor
fields: 
  - Full name: grantor_name_full
  - Last name: grantor_name_last
  - aka: grantor_name_aka

Object for Name

This example is from How docassemble uses objects:

objects:
  - grantor: Individual
  - grantee: Individual
  - trustee: Individual
---
generic object: Individual
question: |
  What is
  ${ x.object_possessive('name') }?
fields:
  - First Name: x.name.first
    default: ${ x.first_name_hint() }
  - Middle Name: x.name.middle
    required: False
  - Last Name: x.name.last
    default: ${ x.last_name_hint() }
  - Suffix: x.name.suffix
    required: False
    code: |
      name_suffix()

Generic Object for Name Using x[i]

objects: 
  - grantor: DAList.using(object_type=Individual, there_are_any=True)
---
generic object: DAList
question: |
  What is the name of the ${ ordinal(i) } ${ x.object_name() }?
fields:
  - First Name: x[i].name.first
    default: ${ x[i].first_name_hint() }
  - Middle Name: x[i].name.middle
    required: False
  - Last Name: x[i].name.last
    default: ${ x[i].last_name_hint() }
  - Suffix: x[i].name.suffix
    required: False
    code: |
      name_suffix()
---
generic object: DAList
question: |
  Are there are any more ${ noun_plural(x.object_name()) }?
yesno: x.there_is_another
---
question: |
 Hello, ${ grantor }!
mandatory: True

Number Variable

Ask User for Number Variable

---
question: What is your favorite number?
fields:
  - Number: favorite_number
    datatype: number
---

Manipulate User’s Number Variable

---
question: What is your favorite number?
fields:
  - Number: favorite_number
    datatype: number
---
code: |
  if favorite_number == 42:
    inhabitant_count = 2
  else:
    inhabitant_count = 2000 + favorite_number * 45
---

Reset Variable

You can set variables using code and reset them using a reset block.

Set Variable

There are two ways to set a variable:

  1. Use Python in a Code block
  2. Use the setField() JavaScript function

See setField() JavaScript function, docassemble documentation.

Text Variable

Ask User for Text Variable

---
question: |
  What is your planet's name?
fields:
  - Your Planet: planet
---

Yes/No

Yes/No in Playground

Yes/no fields, docassemble.

  • datatype: yesno
  • datatype: yesnowide
  • uncheck others: True – causes the field to act as a “none of the above” field for all the other yes/no checkbox fields on the page
  • uncheck others: – when followed by list of variables, it will uncheck only those variables
  • datatype: yesnoradio – shows “Yes” and “No” choices
  • datatype: yesnomaybe – shows “Yes,” “No,” and “I don’t know.”
  • help

Yes/No in Word

See Yes/No in Word, above.

Word Template

Generally

Variables use Jinja2.

Inline Variable

Insert inline variable:

Here is an inline variable,{{ var_name }}, which is part of a paragraph. 
  • Syntax: Two curly open brackets ({{), space ( ), variable, space ( ), two close curly brackets (}}).
  • A variable name cannot have spaces.

Capitalize, Lower Case, Title Case

See https://www.documate.org/automation/docassemble-word-document-templates-part-v-formatting-words/

Checkbox

Use the following if statement:

{% if var_name['cb_answer'] %}Some conditional text.{% endif %}

Conditional Paragraph

{%p if var_name %}
Conditional paragraph. 
{%p endif %}

Conditional Numbered Paragraph

1. {%p if var_name %}
2. Conditional paragraph. 
3. {%p endif %}

Format Numbers

See https://www.documate.org/automation/docassemble-word-document-templates-part-iv-formatting-numbers/

Multiple Choice

Use the following if statement:

{% if var_name=='mc_answer' %}Some conditional text.{% endif %}

Perform Math

{{ num_var1 + num_var2 | float }}
{{ num_var1 - num_var2 | float }}
{{ num_var1 * num_var2 | float }}
{{ num_var1 / num_var2 | float }}
{{ 100 + num_var2 | float }}

Singular/Plural

See https://www.documate.org/automation/docassemble-word-document-templates-part-v-formatting-words/

Use a/an

See https://www.documate.org/automation/docassemble-word-document-templates-part-v-formatting-words/

Yes/No in Word

Use the following if statement:

{% if var_name %}Some conditional text.{% endif %}

Resources on Jinja

Jinja built-in statements/tags and functions (like Django template tags), Web Forefront.

Example: Collecting Signatures

The Problem

On 8/28/2021, in docassemble’s Slack channel, Jonathan Pyle shared with sample code to gather signatures for landlords, landlord’s representatives, and tenants. The problem that I posed is that there can be more than one landlord, landlords who are entities or individuals, or landlords who have one address (such as a married couple). I will examine the code block-by-block.

The Advice

Pyle gave the following advice:

Keep in mind that the point of generic object is simply to avoid having to repeat yourself. It only makes sense to use generic object if you have so many different objects in your interview that it would be boring and repetitive to use a different block for each object. In the case of an interview involving landlords and tenants, I would just write separate blocks for questions about the tenant(s) and questions about the landlord(s). It’s up to you, though.

Anyway, I would recommend using a data structure that is general enough to encompass all the scenarios, so you don’t have to use if/else statements.

The Code

objects:
  - tenant: DAList.using(object_type=Individual, there_are_any=True)
  - landlord: DAList.using(object_type=(Individual if mom_and_pop else Person), there_are_any=True)
  - landlord[i].signers: DAList.using(object_type=Individual, auto_gather=(not mom_and_pop))
---
question: |
  Who is the ${ ordinal(i) } landlord?
fields:
  - First name: landlord[i].name.first
  - Last name: landlord[i].name.last
---
question: |
  Who is your landlord?
fields:
  - Name: landlord[i].name.text
---
question: |
  Is your landlord a company, or one or more individuals?
field: mom_and_pop
buttons:
  - Company: false
  - One or more individuals: True
---
question: |
  Is there another landlord besides ${ landlord }?
yesno: landlord.there_is_another
---
code: |
  if not mom_and_pop:
    landlord.there_is_another = False
---
only sets: landlord[i].signers.gathered
code: |
  log("auto gather is " + repr(landlord[i].signers.auto_gather))
  landlord[i].signers.clear()
  landlord[i].signers.append(landlord[i])
  landlord[i].signers[0].title = 'owner'
  landlord[i].signers.gathered = True
---
code: |
  landlord[i].signers.there_are_any = True
---
question: |
  Who is the ${ ordinal(j) } person signing for ${ landlord[i] }?
fields:
  - First name: landlord[i].signers[j].name.first
  - Last name: landlord[i].signers[j].name.last
  - Title: landlord[i].signers[j].title
---
question: |
  Do any more people need to sign for ${ landlord[i] }?
yesno: landlord[i].signers.there_is_another
---
need: x.signature_notification
generic object: Individual
signature: x.signature
question: Sign your name
under: ${ x }
---
generic object: Individual
question: |
  ${ x } needs to sign now.
subquestion: |
  Press Continue when ready.
continue button field: x.signature_notification
---
mandatory: True
question: |
  Signature page
attachment:
  content: |
    % for ll in landlord:
    For landlord: ${ ll }
    
    % for signer in ll.signers:
    ${ signer.signature } [BR] [BLANK] [BR]
    ${ signer }, ${ signer.title }
    
    % endfor
    % endfor

Comments on the Code

Object Block

The code opens with the following object block:

objects:
  - tenant: DAList.using(object_type=Individual, there_are_any=True)
  - landlord: DAList.using(object_type=(Individual if mom_and_pop else Person), there_are_any=True)
  - landlord[i].signers: DAList.using(object_type=Individual, auto_gather=(not mom_and_pop))

1) This block defines three objects: tenant, landlord, and landlord[i].signers.

  • Why is the third one so complicated?

2) The first object definition is tenant: DAList.using(object_type=Individual, there_are_any=True)

a) “A DAList acts like an ordinary Python list, except that docassemble can ask questions to define items of the list.”3

Resources on docassemble

GBLS/docassemble-workinggroup, GitHub.

  1. Question blocks, docassemble.  2

  2. This example is from a question that Ronald Ko asked on docasssemble’s Slack channel on August 31, 2021. 

  3. DAList, docassemble.