Overview

MEG Workflows allow administrators to configure automated workflows that can process and change data within the system in response to events. A workflow can currently be triggered by the creation or modification of an observation from the review screen. The only supported action is to create a new observation in a different form. Workflows also support multiple conditional stages, allowing for complex conditional logic. This model of workflows is highly extensible and will support new trigger events and actions in the future.

@startuml

title MEG Workflow

class Workflow {
  +name: CharField
  +description: TextField
  +initial_stage: ForeignKey(Stage)
  +audit_form: ForeignKey(AuditForm)
  +trigger_type: CharField
  +date field: CustomField
}

class Stage {
  +name: CharField
  +conditions: JSONField
  +next_stage_if_true: ForeignKey(Stage)
  +next_stage_if_false: ForeignKey(Stage)
}

class Operation {
  +operation_type: CharField
  +stage: ForeignKey(Stage)
  +config: JSONField
}

Workflow --> Stage : initial_stage
Workflow --> AuditForm : audit_form
Operation --> Stage : stage
Stage --> Stage : next_stage_if_true
Stage --> Stage : next_stage_if_false

@enduml

Data diagram for MEG Workflows

Usage Guide

  1. Create the Workflow. This is the entrypoint that triggers the workflow. You can specify the form, and the trigger type.

  2. Create the Stage. Once created, add it to the Workflow created in the prior step.

  3. Create the Operation. This model defines the logic the workflow will implement. The only operation currently supported is to create another observation. The json config field allows you to configure this:

    Use Case 1: Risk Tracking Workflow - This workflow copies risk-related values from one form to another to track risks over time.

    Example of copying a risk rating to another form designed to track them over time.
    {
        "audit_form_id": 1,
        "ward_id": 2,
        "auditor_id": 3,
        "custom_answers": {
            "risk_name": "{trigger_object.custom_answers.risk_name}",
            "risk_rating": "{trigger_object.custom_answers.risk_rating}",
            "severity": "{trigger_object.custom_answers.severity}",
            "likelihood": "{trigger_object.custom_answers.likelihood}"
        }
    }
    

    Note

    {trigger_object.<PROPERTY_NAME>} allows you to use the data from the object that triggered the workflow. If you’re using a placeholder for the auditor_id you must use it for the ward_id, and vice versa. This is because if a hardcoded ward is used we can’t be sure that the {trigger_object.auditor_id} has access to that ward. {trigger_object.audit_form_id} is not supported as that would create a self referencing workflow.

    Use Case 2: Contract Expiry Workflow - This workflow automatically updates the status of a contract based on its expiry date.

    Example of updating the contract status based on the expiry date.
    {
        "audit_form_id": 2,
        "ward_id": 3,
        "auditor_id": 4,
        "custom_answers": {
            "contract_status": "Expired"
        }
    }
    

    Use Case 3: Conditional Workflow Cessation - This workflow evaluates conditions at each stage and ceases if the conditions are met or no next stage is defined.

    Example of a workflow that ceases based on condition evaluation.
    {
        "type": "query_form_entries",
        "form": 3,
        "filters": {
            "field": [
                {
                    "field": "mrn",
                    "answer": "{trigger_object.custom_answers.mrn}"
                },
                {
                    "field": "created__gte",
                    "answer": "{trigger_object.custom_answers.discharge_date}"
                }
            ],
            "operator": "and"
        },
        "stop_if": true
    }
    

    Use Case 4: Patient Follow-up Email Notification - This workflow sends an automated email notification.

    Example of sending a follow-up email after patient observation
    {
        "subject": "How was your stay?",
        "addr_to": "{trigger_object.custom_answers.email}",
        "message": "Dear {trigger_object.custom_answers.first_name},\n\nThis is a follow-up regarding your recent medical observation. Please click the link below to give us your feedback on your stay.\n\nBest regards,\nMEG",
        "links": [
            {
                "label": "View Observation Details",
                "url": "https://patient-portal.example.com/observation/?mrn={trigger_object.custom_answers.mrn}"
            }
        ],
        "delay_hours": 48,
        "placeholders": {
            "trigger_object.custom_answers.first_name": "Patient",
            "trigger_object.custom_answers.email": "patient@example.com"
        }
    }
    

    Note

    Email operation supports dynamic placeholders using {trigger_object.custom_answers.<field_name>} syntax. Placeholders can be used in subject, recipient, sender, message, and link configurations.

    Use Case 5: Patient Follow-up SMS Notification - This workflow sends an automated sms notification.

    Example of sending a follow-up sms after patient observation
    {
        "phone_number": "+1234567890",
        "language": "en",
        "content": "Dear {trigger_object.custom_answers.first_name}, We hoped you enjoyed your stay here. Please fill out this feedback form: https://audits.megsupporttools.com/webforms/ID?mrn={trigger_object.custom_answers.mrn}",
        "placeholders": {
            "trigger_object.custom_answers.first_name": "Patient"
        }
    }
    

    Note

    The language field is optional and inherits from institution settings when not specified. SMS operation supports dynamic placeholders using {trigger_object.custom_answers.first_name} syntax. Placeholders can be used in phone_number and content.

    Use Case 6: Multiple Observation Creation From Multi-Choice - This workflow creates multiple observations for each fruit selected (choice in a multiple choice field)

    Example of creating an observation for each selected choice
    {
        "audit_form_id": 38,
        "ward_id": "{trigger_object.ward_id}",
        "auditor_id": "{trigger_object.auditor_id}",
        "choice_field_name": "fruits",
        "custom_answers": {
            "fruit": "{choice_value}",
            "comment": "{trigger_object.custom_answers.comment}"
        }
    }
    

    Note

    The choice_field_name parameter specifies which multiple-choice field to process. The {choice_value} placeholder contains the current choice being processed. Standard placeholders like {trigger_object.custom_answers.<field_name>} can also be used.

    Use Case 7: Multiple Output Forms for Multi-Choice Field - This workflow creates multiple observations for each selected choice, with different output forms based on choice value

    Example of creating observations in different forms based on selected choices
    {
        "ward_id": "{trigger_object.ward_id}",
        "auditor_id": "{trigger_object.auditor_id}",
        "audit_forms": [
            {"choice": "banana", "audit_form_id": 90},
            {"choice": "apple", "audit_form_id": 90},
            {"choice": "orange", "audit_form_id": 95},
            {"choice": "pear", "audit_form_id": 95}
        ],
        "choice_field_name": "fruits",
        "custom_answers": {
            "fruit": "{choice_value}",
            "comment": "{trigger_object.custom_answers.comment}"
        }
    }
    

    Note

    Use audit_forms instead of audit_form_id to route different choices to different output forms. You cannot use both audit_forms and audit_form_id simultaneously. If a selected choice doesn’t have a matching entry in audit_forms, it will be ignored (no observation created). Each target form must contain the custom fields referenced in custom_answers.

    Use Case 8: Send Web Requests Via Workflow - This workflow creates multiple observations for each fruit selected (choice in a multiple choice field)

    Example of creating an observation for each selected choice
    {
        "url": "https://url/resource/endpoint",
        "headers": {
            "api-specific-header": "gateway-key"
        },
        "time_format": "%H:%M",
        "request_type": "xml",
        "ssl_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
        "field_mapping": {
            "event_date": "EventOccurrenceDate",
            "event_time": "EventOccurrenceTime",
            "facilitymfid": "FacilityMFID",
        },
        "related_forms": true,
        "authentication": {
            "password": "password",
            "username": "username"
        },
        "object_mapping": {
            "id": "FaclityIncidentRecID",
            "session.start_date": "EventSubmittedDate",
            "session.updated_date": "EventUpdatedDate",
            "session.start_time_cleaned": "EventSubmittedTime",
            "audit_form.institution.name": "HealthCareFacilityName"
        },
        "request_template": "<Root><MainDet><EventOccurrenceDate value=\"\"/><EventOccurrenceTime value=\"\"/><EventSubmittedDate value=\"\"/><EventSubmittedTime value=\"\"/><EventUpdatedDate value=\"\"/></MainDet></Root>",
        "multichoice_join_character": " ",
        "http_transaction_field_mapping": {
            "status_code": "doh_status",
            "url": "doh_url",
            "body": "doh_request_body",
            "request_data": "doh_request_payload"
    }
    

    Note

    url

    Specifies the endpoint the request will be sent to.

    headers

    Specified any additional headers that should be included in the request.

    request_type

    Specifies the type of request to be sent; currently only xml processing is supporting and can only be sent via REST protocol.

    ssl_cert

    Optional. A string containing the SSL certificate (.pem format) to be used for the request.

    field_mapping

    Specifies the key/value mapping of custom_fields/answers to the request payload the endpoint expects. The key is the custom field and the value is the payload’s key.

    related_forms

    A boolean value that specifies whether or not related forms should be queried for the mapping.

    authentication

    Specifies the type of authentication protocol to send with the request; currently only basic auth is supported.

    object_mapping

    Similar to the field_mapping parameter but instead uses the dotted path of the trigger object (observation) to retrieve values.

    request_template

    Specifies the structure of the request payload to be sent; at present, only stringified xml is accepted.

    time_format

    Optional. Specifies a format to use for time strings instead of the default which is %H:%M:%S. Must be a valid python time format string.

    multichoice_join_character

    Optional. Specifies a format to join multiple choices into a string.

    http_transaction_field_mapping

    Optional. A dictionary mapping http transaction attributes to custom field names. Specifies a field on the form to save the value for each attribute from the http transaction.

  4. Test the workflow. To verify it’s working, you can check the Celery dashboard in the Django admin. Look for tasks like process_workflow_stage.

Use Cases in Detail

Use Case 1: Risk Tracking Workflow

  • Trigger: Creation or update of an observation in the Risk Form.

  • Operation: Copies risk details to the Metric Form for tracking over time.

  • Workflow Configuration:
    • Workflow: Triggered by Risk Form.

    • Stage: Adds a stage to process risk tracking.

    • Operation: Copies the risk_name, risk_rating, severity, and likelihood fields to the Metric Form.

Use Case 2: Contract Expiry Workflow

  • Trigger: Update of the Contract Form when the expiry date equals the current date.

  • Operation: Updates the contract_status field in the same observation to Expired.

  • Workflow Configuration:
    • Workflow: Triggered by Contract Form and expiry_date.

    • Workflow Config: For answer date workflows, can specify the time of day to run the scheduled task on that expiry_date using: {“scheduled_time”: “14:30”}. If no scheduled_time is configured, the default scheduled time is 00:00 (midnight).

    • Stage: Adds a stage to process the expiry condition.

    • Operation: Evaluates the expiry_date and updates the contract_status.

Use Case 3: Conditional Workflow Cessation

  • Trigger: Evaluation of conditions at each stage.

  • Operation: Ceases the workflow if conditions are met or no next stage is defined.

  • Workflow Configuration:
    • Workflow: Configured with multiple stages.

    • Stage: Each stage evaluates conditions to determine the next action.

    • Operation: Workflow ceases if conditions are met and next_stage_if_true is not specified.

Use Case 4: Send Email via Workflow

  • Trigger: Update of the Admission Details Form when the discharge_date equals the current date.

  • Operation: Sends an email to the patient with a Feedback Form link. The email content dynamically includes patient-specific details (e.g., name, MRN).

  • Workflow Configuration:
    • Workflow: Triggered by Admission Details Form and discharge_date.

    • Stage: Adds a stage to process the send_email.

    • Operation: Evaluates the trigger object’s custom_answers fields to personalize the email body.

Use Case 5: Send SMS via Workflow

  • Trigger: Update of the Admission Details Form when the discharge_date equals the current date.

  • Operation: Sends sms to the patient with a Feedback Form link. The sms content dynamically includes patient-specific details (e.g., name, MRN).

  • Workflow Configuration:
    • Workflow: Triggered by Admission Details Form and discharge_date.

    • Stage: Adds a stage to process the send_sms.

    • Operation: Evaluates the trigger object’s custom_answers fields to personalize the sms content.

    • Delay Options: Configure delayed email sending using delay_hours parameter (e.g., 48 hours after trigger)

Use Case 6: Multiple Observation Creation From Multi-Choice

  • Trigger: Creation or update of an observation with a multiple-choice field.

  • Operation: Creates a separate observation for each selected choice in the target form.

  • Workflow Configuration:
    • Workflow: Triggered by a form with a multiple-choice field.

    • Stage: Adds a stage to process each selected choice.

    • Operation: Creates an observation in the target form for each selected choice.

Use Case 7: Multiple Output Forms for Multi-Choice Field

  • Trigger: Creation or update of an observation with a multiple-choice field.

  • Operation: Creates separate observations in different target forms based on each selected choice.

  • Workflow Configuration:
    • Workflow: Triggered by a form with a multiple-choice field.

    • Stage: Adds a stage to process each selected choice.

    • Operation: Dynamically selects the appropriate target form for each choice and creates observations accordingly.

    • Mapping: Uses the audit_forms configuration to map specific choices to their corresponding output forms.

Use Case 8: Send Web Request Via Workflow

  • Trigger: Creation or update of an observation with a multiple-choice field.

  • Operation: Maps the custom fields of that observation to the predetermined request payload and sends a HTTP POST request to an endpoint

  • Workflow Configuration:
    • Workflow: Triggered by a form with a multiple-choice field.

    • Stage: Adds a stage to send request once value has changed to the desired input

    • Operation: Sends web request with mapping of custom fields to api endpoint

Note

When the trigger_web_request function request fails, client errors aren’t retried (4xx). Other crashes cause the celery task to be retried with a 5 minute exponential backoff.