Creating and activating plan v2.0 - Action Refactor
Document Revision
Date | Version | Description of change | Author |
22 December 2021 | 1.0 | Initial document | Stefanus Heath |
Overview
This document outlines the process of creating a plan within the Reveal platform.
Definitions, Acronyms and Abbreviations
Name | Description |
|
|
Form
A form refers to the json representation of the process of collecting data against a task. The form is stored as a JSON object. This form will be sync’d to the Reveal Client for use in task presentation.
We will create a form library which contains pre-built forms that can be easily copied into the plan creation process.
For all resources where isTemplate is true, the form_template permission must be present in the token.
THOUGHTS ON THE WORD QUESTIONNAIRE
Form list
GET /api/v1/form
Search parameters as path parameters can be any combination of:
search
isTemplate
{ "content": [...], "totalItems": 100, "totalPages": 4, "currentPage": 1} |
Form fetch
GET /api/v1/form/{identifier}
{ "identifier": guid, "name": string, "title": string, "isTemplate": boolean, "payload": jsonb} |
Form create
GET /api/v1/form
{ "name": string, // ^[0-9a-z](\d|[0-9a-z]|-){2,30}$ and immutable "title": string, "isTemplate": boolean, // immutable "payload": jsonb, "version": string} |
Form update
PUT /api/v1/form/{identifier}
{ "title": string, "payload": jsonb} |
Form delete
DELETE /api/v1/form/{identifier}
Plan
Plan refers to the definition of a target group of objects, whether by geographic or other criteria, for which a set of tasks are generated. These tasks define the collection of data against the target objects within the plan. Typical examples are:
Indoor Residual Spray (IRS)
Visit each structure in the operational area and attempt to spray
Mass Drug Administration (MDA)
Register all families & family members in all residential structures enumerated (100%) within the operational area
Visit all residential structures (100%) and dispense prophylaxis to each registered person
Visit all residential structures (100%) and confirm adherence of each registered person
Each task completion results in an event describing the output of the task. The event is populated from the form associated with the task.
What are we doing, what goals do we want to achieve, against actions, that creates tasks to accomplish this goal
Within the planned current usage of the plan object, although changes will be made to the existing model, the plan definition model fully adheres to the FHIR definitions as defined in https://www.hl7.org/fhir/plandefinition.html .
What is required is that the backend validates the complete freeform nature of the plan object, in order to make it more resilient to usage. The frontend must be better controlled in terms of templating, in order to avoid the freeform nature of the plan object.
Goal
A goal is a component of a plan that provides a target against a due date which tracks the trajectory of a plan. A plan can have several goals, which can be driven by different actions. This means that a plan can be a multi-dimensional construct that has several goals to achieve. Each set of processes can be tracked against individual goals, but this has not been typically the case in the past.
The example below shows the typical implementation of a goal for IRS over 2020 and 2021, which uses goal as a soft placeholder without real meaning. The due date is not part of the actual planning process and has no real meaning in terms of the on the ground planning. Also goals are here mapped to be an ideal end state, which can be implemented with more meaning on the front-end to provide significant power to the planners. Goals have been historically loaded against the core task such as spray, where the other tasks have been treated as ad-hoc and/or ancillary, but using a correct planning approach other tasks can be brought into their own goals and form part of the complete plan.
Recommendation of the writer: a full and proper discussion should be scheduled to provide full context of how this could work, and the power that could be derived from this construct. Please bear in mind that this construct comes from the FHIR concept of plan, it does cater for implementations such as IRS very effectively, and thus could improve the thought process around planning.
example:
"goal": [ { "id": "IRS_Verification", "description": "Submit one verification form per operational area", "priority": "medium-priority", "target": [ { "measure":"Percent of forms submitted", "detail": { "detailQuantity":{ "value": 100.0, "comparator":"gt;", "unit": "Percent" } }, "due": "2022-05-31" } ] } ] |
concept one:
"goal": [ { "id": "IRS_Verification", "description": "Submit one verification form per operational area", "priority": "medium-priority", "target": [ { "measure":"Percent of forms submitted", "detail": { "detailQuantity":{ "value": 70.0, "comparator": "gt;", "unit": "Percent" } }, "due": "2022-05-31" }, { "measure":"Percent of forms submitted", "detail": { "detailQuantity":{ "value": 85.0, "comparator": "gt;", "unit": "Percent" } }, "due": "2022-06-31" }, { "measure":"Percent of forms submitted", "detail": { "detailQuantity":{ "value": 100.0, "comparator": "eq;", "unit": "Percent" } }, "due": "2022-07-31" } ] } ] |
concept two:
"goal": [ { "id": "IRS_Verification", "description": "Submit one verification form per operational area", "priority": "medium-priority", "target": [ { "measure":"Percent of forms submitted", "detail": { "detailQuantity":{ "value": 100.0, "comparator": "gt;", "unit": "Percent" } }, "due": "2022-05-31" } ] }, { "id": "IRS_daily_summary", "description": "Submit one daily summary form per operational area", "priority": "medium-priority", "target": [ { "measure":"Percent of forms submitted", "detail": { "detailQuantity":{ "value": 100.0, "comparator": "gt;", "unit": "Percent" } }, "due": "2022-05-31" } ] } ] |
Action
The Action component of a plan defines the tasks that need to be performed to accomplish the goal. It is used to determine the conditions and triggers under which tasks should be generated. Triggers refer to the preceding function that would cause the evaluation of the plan to determine if new tasks need to be generated. The conditions determine under which circumstances the task generation process will generate the required task, such as location, jurisdiction or person with certain criteria. Action links a form to a goal in order to create a task.
Trigger definition
The trigger section defines the condition under which a task must be generated. The trigger is ENUM that defines when an action item is evaluated.
List of ENUMS:
plan-activation
plan-jurisdiction-modification
event-submission
Condition definition
The condition section defines under which conditions a subject is considered to receive a task. The current condition implementation implements the targeting of subjects using FHIR path expressions. This functionality needs to be abstracted.
Process:
Inspect the trigger causing the evaluation
Inspect the if the subject is covered by the plan
Inspect if the status of the subject is within the conditions
Inspect if the trigger should affect other tasks on other plans of the same action against the same
Recommendation of the writer: Using FHIR path expressions makes the current system unusable without explicit development and FHIR coding experience. This element should be abstracted into predetermined conditions. All of the below require intensive understanding in order to know the meaning.
Previous examples of FHIR path expressions
Elements:
Questionnaire
questionnaire = 'Register_Structure' meaning that questionnaire is the name of the response
QuestionnaireResponse
$this.is(FHIR.QuestionnaireResponse) meaning
Location
$this.is(FHIR.Location) meaning that the location must exist which seems to be pointless in terms of joining
$this.item.where(linkId='structureType').answer.value = 'Residential Structure' meaning that an answer in the questionnaire must be something specific
Condition examples to consider
Location
Location is in the active plan
Location has been registered and falls within the active plan
Person
birthDate evaluation to determine a certain age; here we should consider the fact that age as considered at the start of the plan
gender evaluation
location evaluation; to determine if a person requires a task based on a relationship to a specific location
previous state evaluation; to determine if a person requires a task based on a previous state contained in metadata
Event
the Event of specified name is triggered
the Event is evaluated based on payload values
Plan search
GET /api/v1/plan
Search parameters as path parameters can be any combination of:
title
status
interventionType
jurisdiction
{ "content": [...], "totalItems": 100, "totalPages": 4, "currentPage": 1} |
Plan count aggregate
GET /api/v1/plan?_summary=count
Supports all the search parameters as above
{ "count": 8} |
Plan fetch
GET /api/v1/plan/{planIdentifier}
Path parameters for _summary=true will not return the jurisdiction, goal and action objects, _summary=false (default) will return jurisdiction, goal and action objects.
{ "identifier": guid, "name": string, // [a-z0-9\-] "title": string, // [a-zA-Z0-9\-\ ] "status": enum, // draft, active, retired, unknown “status”: { “identifier” :guid, “name”: string } "date": date, // last_modified_date (YYYY-MM-DD) "effectivePeriod": { "start": date, // (YYYY-MM-DD) "end": date // (YYYY-MM-DD) }, “interventionType”:{ “Identifier”:guid, “name” :string, “description”:string }, “locationHierarchyIdentifier”:guid "useContext": [ { "code": "interventionType", "valueCodableConcept": enum // IRS, IRS-Lite, MDA, MDA-Lite }, { "code": "locationHierarchy", "valueCodableConcept": guid // the identifier of the locationHierarchy in use } ], "jurisdiction": [ { "code": [guid,guid,guid] } ], "jurisdiction": [guid,guid,guid], "goal": [ { "identifier": string, "description": string, "priority": enum, // high-priority, medium-priority, low-priority "target": [ { "measure": string, "detail": { "detailQuantity": { "value": int, "comparator": "gt;=", "unit": enum // percent } }, "due": date } ] } ], "goal": [ { "identifier": string, "description": string, "priority": enum, // high-priority, medium-priority, low-priority "target": [ { "measure": string, "detail": { "detailQuantity": { "value": int, "comparator": "gt;=", "unit": enum // percent } }, "due": date } ], “entityType” :{ “identifier” : guid, “name”: string }, "action": [ { "identifier": guid, "title": string, "description": string, "code": string, “trigger”:???? "timingPeriod": { "start": date, "end": date }, "reason": enum }, “form” : { “identifier”: guid, “name”: string }, "target": [ { "description":string, "priority": enum, // high-priority, medium-priority,low-priority "measureType": string, “metaDataTag”:string, “metric”:integer, "due": date }], ], "type": enum // create, update, remove } ], "action": [ { "identifier": guid, "title": string, "description": string, "code": string, "timingPeriod": { "start": date, "end": date }, "reason": "Routine", “goal”: { "identifier": string, "description": string, "priority": enum, // high-priority, medium-priority, low-priority "target": [ { "measure": string, "detail": { "detailQuantity": { "value": int, "comparator": "gt;=", "unit": enum // percent } }, "due": date } ] } "goalId": fk_to_goalIdentifier, "subjectCodableConcept": { "text": enum, // location, person }, "trigger": [ { "type": "named-event", "name": enum // plan-activation, plan-jurisdiction-modification, event-creation } ], "condition": [ { "kind": "applicability", "expression": { "description": "Structure type does not exist", "expression": "$this.is(FHIR.Location)" } } ], "definitionUri": string, // path to json, "type": enum // create, update, remove } ] } |
|
Plan create
POST /api/v1/plan
A plan can only be created with a status of DRAFT, due to the fact that planning and targeting must be done before the plan can be activated.
The jurisdiction fields may be empty as it will come from planning, but screens to step through the selection of jurisdictions will work similar to the current functionality. Future enhancements refer to documentation on the planning module.
Due to the nature of the link between goal and link the plan should be loaded over several forms that complete with a POST and subsequent PUT operations. Until all minimum requirements have been met a plan cannot be changed to ACTIVE
{ "name": string, // [a-z0-9\-] IMMUTABLE "title": string, // [a-zA-Z0-9\-\ ] "status": enum, // draft, active, retired, unknown "effectivePeriod": { "start": date, // (YYYY-MM-DD) "end": date // (YYYY-MM-DD) }, "useContext": [ { "code": "interventionType", "valueCodableConcept": enum // IRS, IRS-Lite, MDA, MDA-Lite }, { "code": "locationHierarchy", "valueCodableConcept": guid // the identifier of the locationHierarchy in use } ], "hierarchy": [ { "code": [guid,guid,guid] } ], "goal": [ { "identifier": string, “title” : string, "description": string, "priority": enum, // high-priority, medium-priority, low-priority "target": [ { "measure": string, "detail": { "detailQuantity": { "value": int, "comparator": "gt;=", "unit": enum // percent } }, "due": date } ] } ], "action": [ { "title": string, "description": string, "code": string, "timingPeriod": { "start": date, "end": date }, "reason": "Routine", "goalId": fk_to_goalIdentifier, "subjectCodableConcept": { "text": enum, // location, person }, "trigger": [ { "type": "named-event", "name": enum // plan-activation, plan-jurisdiction-modification, event-creation } ], "condition": [ { "kind": "applicability", "expression": { "description": "Structure type does not exist", "expression": "$this.is(FHIR.Location)" } } ], "definitionUri": string, // FK to form "type": enum // create, update, remove } ] } |
|
|
GET /api/v1/plan/{identifier}
GET /api/v1/plan/{identifier}/goal
POST /api/v1/plan/{identifier}/goal
PUT /api/v1/plan/{identifier}/goal/{identifier}
GET /api/v1/plan/{identifier}/action
POST /api/v1/plan/{identifier}/action
PUT /api/v1/plan/{identifier}/action/{identifier}
GET /api/v1/plan/{identifier}/jurisdiction
POST /api/v1/plan/{identifier}/jurisdiction
PUT /api/v1/plan/{identifier}/jurisdiction/{identifier}
Activating a plan
Once a plan has at least one jurisdiction, one goal and one action before it can be activated. In order to have an action at least one form is required. Each action must have a form.
Once plan has been activated the following elements become immutable:
effectivePeriod => start
useContext => interventionType
??GOAL??
action => code
action => timing??
action =>
Task evaluation
On plan activation
On plan activation the task generation executes. It selects all conditions that are loaded into the plan and evaluates whether a task should be created. This includes checking if a duplicate task exists for the element, then if no task exists then if the conditions are met for the evaluation.
On plan modification
Each time the plan is modified the task generation function should re-evaluate task generation.
On event submission
Each time a new location or person is created, then task generation should evaluate that object.
Task
Task refers to the object that links and drives the questionnaire against the target object.
Within the planned current usage of the task object, although minor changes will be made to the existing model, the task definition model fully adheres to the FHIR definitions as defined in https://www.hl7.org/fhir/task.html .
Task search
GET /api/v1/task
Search parameters as path parameters can be any combination of:
planIdentifier
status
code
for
groupIdentifier/location??
{ "content": [...], "totalItems": 100, "totalPages": 4, "currentPage": 1} |
Task count aggregate
GET /api/v1/task?_summary=count
Supports all the search parameters as above
{ "count": 8} |
Task fetch
GET /api/v1/task/{taskIdentifier}
{ "identifier": guid, "planIdentifier": guid, "focus": guid, // What task is acting on (fk of target object) -> Location "action": { “identifier”: guid, “form” : {} }, "code": string, // fk of action code from plan "status": enum, // ready, cancelled, completed "status": { “identifier”: guid, “name” : string }, "priority": enum, // routine, urgent, asap, stat "description": string, // as defined by the action of the plan "businessStatus": enum, // needs definition "executionPeriod": { "end": datetime, // as defined by plan "start": datetime // as defined by plan }, "groupIdentifier": guid // refers to the Operational Area’ identifier for sync "form": form_URI // as defined in the plan action } |
|
NOTES ON DEFINITION OF ACTION:
We should look at the code that actually does task duplication so that we can look at effectively growing and shrinking tasks. Also we need to understand the impact of creating many tasks that are not being acted upon.
Task create
POST /api/v1/task
{ "planIdentifier": guid, "focus": guid, // What task is acting on (fk of target object) "code": string, // fk of action code from plan "status": enum, // ready, cancelled, completed "priority": enum, // routine, urgent, asap, stat "description": string, // as defined by the action of the plan "businessStatus": enum, // needs definition "executionPeriod": { "end": datetime, // as defined by plan "start": datetime // as defined by plan }, "groupIdentifier": guid // refers to the Operational Area’ identifier for sync "instantiatesUri": form_URI // as defined in the plan action } |
Task update
PUT /api/v1/task/{identifier}
{ "status": enum, // ready, cancelled, completed "priority": enum, // routine, urgent, asap, stat "description": string, // as defined by the action of the plan "businessStatus": enum, // needs definition "executionPeriod": { "end": datetime, // as defined by plan "start": datetime // as defined by plan } } |