How to embed forms to the workflow

Overview of workflow

Forms can be embedded into a workflow.

Workflow is a skeleton for forms composition in more complex structures.

Initially Workflow have nested structure of items, where each item can be:

  • Section - used for forms grouping.

  • Form - reference to existed form.

Workflow and items has status model , model is slightly different

Workflow statuses:

  • new

  • in-progress

  • canceled

  • completed

  • in-amendment

  • amended

Item statuses:

  • new

  • in-progress

  • skipped

  • completed

  • in-amendment

  • amended

canceled status used for WF because skipped status is not obvious in this domain. WF is a process of action, but item is just a step that can be optionally and can be omitted.

Workflow definition

 DemoWF3
 ;; Tag to mark entity as Workflow
 {:zen/tags #{aidbox.sdc/Workflow} 
  ;; title of the WF
  :title "Demo workflow"
  ;; items consists of sections/forms in any level deep
           ;; section key can be arbitrary keyword
  :items {:section1
                    ;; section definition with 2 forms
                    {:item aidbox.sdc/SectionItem
                     :title "My custom section 1"
                     :items {:phisical-exam 
                                            ;; form item definition.
                                            {:item aidbox.sdc/FormItem
                                             :form VitalsForm}
                             :questionnaire  {:item aidbox.sdc/FormItem
                                              :form PHQ2PHQ9Form}}}}}

When WF is started - it will be populated with items :order information, launched forms, document's references and state statuses.

 DemoWF3
 ;; Tag to mark entity as Workflow
 {:zen/tags #{aidbox.sdc/Workflow} 
  :title "Demo workflow"
  :order [:section1]
  :status "new"
  :items {:section1 {:item aidbox.sdc/SectionItem
                     :title "My custom section 1"
                     :status "new"
                     :order [:phisical-exam :questionnaire]
                     :items {:phisical-exam {:item aidbox.sdc/FormItem
                                             :form myforms/Demographic
                                             :title "Demographic"
                                             :layout {... layout DSL}
                                             :rules {... rules ...}
                                             :document {id: doc-id :resourceType}
                                             :status "new"}
                             :questionnaire  {... launched form ...}}}}}

Also see Optional features section for some workflow payload customization.

Section

Section item is item with type aidbox.sdc/SectionItemIt containstitleand childrenitems`. These fields are mandatory

{:item aidbox.sdc/SectionItem
 :title "My custom Section"
 :items {:form1 {:item aidbox.sdc/FormItem
                 :form myforms./VitalsForm}
         :section1 {:item aidbox.sdc/SectionItem
                    :title "My custom Section"
                    :items {:form1 {:item aidbox.sdc/FormItem
                                    :form myforms/Demographic}}}}}

When workflow is started - all SectionItems will be populated with meta information of items order.

Order generated from :items natural placement in ZEN file.

{:item aidbox.sdc/SectionItem
 :title "My custom Section"
 :order [:form1 :section1]
 :items {:form1 {:item aidbox.sdc/FormItem
                 :form myforms./VitalsForm}
         :section1 {:item aidbox.sdc/SectionItem
                    :title "My custom Section"
                    :order [:form1]
                    :items {:form1 {:item aidbox.sdc/FormItem
                                    :form myforms/Demographic}}}}}

Form

FormItem is item with type aidbox.sdc/FormItem It contains only reference to existed form. This field is mandatory

{:item aidbox.sdc/FormItem
 :form myforms/Demographic}

Zen validates reference - it can refer to symbols with aidbox.sdc/Form tag only.

When workflow is started - all FormItems will be populated with Form definition, Launched Layout, form+document rules and reference to created Document.

{:item aidbox.sdc/FormItem
 :form myforms/Demographic
 :title "Demographic"
 :layout {... layout DSL}
 :rules {... rules ...}
 :document {id: doc-id :resourceType}}

StatelessForm

StatelessForm is item with type aidbox.sdc/StatelessFormItem It contains only reference to existed form. This field is mandatory

It is different from typical FormItem in that way:

  • data populated to it's state on every read-workflow operation.

  • data extracted and populated back on every save operation.

  • this form can't be skipped.

{:item aidbox.sdc/StatelessFormItem
 :form myforms/AllergyIntolerance}

Zen validates reference - it can refer to symbols with aidbox.sdc/Form tag only.

There is no difference between StatelessFormItems and FormItem on workflow-start

{:item aidbox.sdc/StatelessFormItem
 :form myforms/AllergyIntolerance
 :title "AllergyIntolerance"
 :layout {... layout DSL}
 :rules {... rules ...}
 :document {id: doc-id :resourceType}}

Form rule keys

FormItem also supports special rule based keys: sdc/inject, sdc/enable-when

:sdc/inject used for injecting values to form context.

  • This rule is client side rule. You don't have access to DB.

  • This rule is invoked on each time when form is focused.

Value of the :sdc/inject key should be rules map - where key should match expected key by form's rules, and value is a lisp expression. Lisp expressions are invoked in the context of workflow state.

{:item aidbox.sdc/SectionItem
 :title "My custom Section"
 :order [:form1 :form2]
 :items {:form1 {:item aidbox.sdc/FormItem
                 :form myforms./VitalsForm}
         :form2 {:item aidbox.sdc/FormItem
                 :sdc/inject {:vitals/bmi (get-in [:items :form1 :document :bmi])}
                 :form myforms/Demographic}}}

;; myforms namespace
DemographicDocument
{:zen/tags #{aidbox.sdc/doc}
 :sdc/rules {:external-bmi (get :vitals/bmi)}
 ...}
 
Demographic
{:zen/tags #{aidbox.sdc/Form}
 :document DemographicDocument 
 ...}

:sdc/enable-when used in situations when you have optional forms.

  • The rule is used on the client to show/hide the form

  • The rule is used on the server

    • to launch forms without storing SDCDocuments in DB.

    • to validate form optionality on the complete-workflow/complete-step rpcs

Value of the key :sdc/enable-when should be boolean lisp expression. Lisp expression is invoked in the context of workflow state.

{:item aidbox.sdc/SectionItem
 :title "My custom Section"
 :order [:form1 :form2]
 :items {:form1 {:item aidbox.sdc/FormItem
                 :form myforms./VitalsForm}
         :form2 {:item aidbox.sdc/FormItem
                 :sdc/enable-when (> (get-in [:items :form1 :document :bmi]) 5)
                 :form myforms/Demographic}}}

Basic WF usage scenarios

Assume we have such WF with one section and two forms

 DemoWF
 {:zen/tags #{aidbox.sdc/Workflow}
  :title "Demo workflow"
  :version "0.0.1"
  :items {:section1 {:item aidbox.sdc/SectionItem
                     :title "My custom section 1"
                     :items {:phisical-exam {:item aidbox.sdc/FormItem
                                             :form VitalsForm}
                             :questionnaire  {:item aidbox.sdc/FormItem
                                             :form PHQ2PHQ9Form}}}}}

From the start (after WF start) we are working with form-items directly via step functions (step-save/step-complete/step-skip) When form-items pushed to some final state (completed/skipped) - sections will complete/skip automatically. And as final action - you need to complete/cancel workflow.

Fast forward scenario

Fast skip scenario

Optional features

Workflow support 2 additional features:

  • versioning - is automatic and based on hashing essential fields of definitions. If some essential field of form/wf is changed - created a new version and snapshotted to DB

  • section id generation - is generated from item path (path from WF root to item itself).

These features you can configure via api-constructor in zen-project.

You need to configure your aidbox/system with sdc-service and it's configuration

Your zen-project entrypoint namespace

aidbox/system with sdc-service

 box
 {:zen/tags #{aidbox/system}
  :zen/desc "test server"
  :services {:sdc sdc-service}}

sdc-service configuration

 sdc-service
 ;; bind your sdc/service with engine `aidbox.sdc/service`
 {:zen/tags         #{aidbox/service}
  :engine           aidbox.sdc/service
  ;; Enable Form/Workflow versioning
  :versioning       {:enabled false}
  ;; Enable WF items id generation
  :wf-items-ids-gen {:enabled true}}

Section id generation

When :wf-items-ids-gen feature is enabled - each section will receive :id property. That id returned on start-workflow, read-workflow rpc payloads, and don't affect WF storage.

Id is generated from item path (path from WF root to item itself).

Example:

This WF definition

 {:zen/tags #{aidbox.sdc/Workflow} 
  :title "My Workflow"
  :items {:section1 {:item aidbox.sdc/SectionItem
                     :items {:phisical-exam {:item aidbox.sdc/FormItem
                                             :form VitalsForm}
                             :questionnaire  {:item aidbox.sdc/FormItem
                                              :form PHQ2PHQ9Form}}}}}

on start-workflow/read-workflow rpcs call - adds items :id information to payload

 {:title "My Workflow"
  :items {:section1 {:id "section1"
                     :items {:phisical-exam {... :id "section1.phisical-exam" ...}
                             :questionnaire {... :id "section1.questionnaire" ...}}
                     ...}}}

You can use Workflow API

Last updated

#2416:

Change request updated