Links

Aidbox Form 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 forward scenario

Fast skip 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 modified 2mo ago