Patient Program Workflow State design

There is currently no strict DFA/FSA/FST transition table definition for Patient Program Workflow states in OpenMRS. Theoretically, as an example, someone could implement a NFA somewhere with two outgoing edges given a single input symbol. I’m not aware if it’s possible to have two states with null end dates in the same program in the current core code (or if it’s written with the assumption that no one will try that, though it is possible to convert a NFA to a DFA), but e.g. the HFE code relies on the NULL end_date as the way to identify the current active state. Ordering by start_date, end_date, and date_created alone could be problematic in the case that the above situation could happen.

Any other thoughts or additional comments?

@mogoodrich @mksd @dkayiwa

The use case which led to the current design was that within a workflow, a patient has one state at any one time, and can move from one state to another over time.

Sorry @long27km I’m not a state expert and you’ve gone a bit over my head here… :slight_smile: could you give a quick summary of (or provide a link to) what DFA/FSA/FST means?

@mogoodrich You may be more familiar with them than you’re aware (know regex?).

I understand the OpenMRS usage of the terms “initial” and “terminal” has somewhat different meanings than I may use at times in the following explanation and could also add to some confusion if misunderstood to mean the patient would “immediately” be transitioned through the entire program workflow or, though it is another valid application of this type of model, allowing or rejecting enrollment to a program. I wouldn’t call myself an expert either, but from my study and use of these models, they seem like a reasonable starting point. I hope this thread can be useful in spurring discussion on this topic, and determining if any of it would be a helpful/needed addition to OpenMRS Patient Programs.

A Finite State Automaton (FSA) is a general category of these models, and you may see it mentioned more frequently. In Deterministic Finite Automata (DFA) the current state is known and repeatable given a sequence of inputs. The states and transitions can be viewed as a graph (in the CS ADT sense of the word), with each edge between vertices representing a particular valid transition (i.e. given some valid input “symbol”, that outgoing edge will be taken from the current vertex to the next vertex, or if there is no valid edge, reset to the initial state/output “invalid”/et c.). Here “Finite” refers to the set of vertices (that it is limited and known) and Deterministic in that the state (vertex) arrived at, given a particular input sequence, is always the same. In drawings of these graphs, the edges have the input “symbol” that selects that edge for traversal written over them. A Finite State Transducer (FST) produces output corresponding to the edge traversed (drawings usually show the input/output pair over the edge). A Non-deterministic Finite Automaton (NFA) allows two or more outgoing edges to correspond to a single input (not that they’re both traversed, but that either edge could be taken, hence the non-determinism).

For more complex conditions for edges (e.g. patient has treatment planned and a referred specialist for treatment and a treatment date scheduled), the complexity of the graph could be reduced using combinational logic to reduce those conditions to a single “symbol” designated for that edge/transition ( similarly to strongly connected component based complexity reduction in graphs, or select lines/output on a muxer ).

I’m not suggesting the strictest definition of any of these be adhered to (there’s a rigorous formal and mathematical language for all of these various things, but I won’t go into that).

Generally, in their most basic form, these systems are simply used to determine if a string or pattern is present or valid (DFA/FSA) (basically output a 0 or 1 after all is said and done, depending on if a “terminal” state was reached) (e.g. was there a regex match? is this a keyword of the language or a previously defined variable?). FSTs are used in translating from one “language” to another (FST, e.g. outputs: “walk -ed” => “verb past tense”, a programming language compiled to intermediate or machine code, simple physical machines: a player piano, a Jacquard loom). Both of these are frequently used in compilers and still in use in computational linguistics even today post neural nets.

In systems like Regular Expressions (e.g. a search pattern for grep), self referencing loop edges are possible (e.g. a*b), though I’m not suggesting there’s any use for that here (though it currently appears to be allowed in OpenMRS, see ProgramWorkflow().isLegalTransition() and PatientProgram.transitionToState()).

(I’ll try not to digress too much further, as I could go into more electrical engineering terminology to describe Finite State Machines (FSM), like Moore, Mealy and Turing machine definitions. Each of which with their own complexities, but can be used to describe functions (of various complexities) with varying conditions on what is considered when output is calculated, if there is “memory”, et c).

While there’s no specific need to “verify” a sequence of states at the moment (though that could be a nice benefit in more complicated systems), it is a more rigorous model than the current implementation (even if the conversation just leads to designing workflows using formal state transition diagrams). If it’s easier to talk about it as just a specialized graph model in this context, that’s fine by me.

Currently in OpenMRS, the actual initial state (when the patient is in a program but no program workflow state has been entered) is followed by those states marked as “initial”, where they are the only valid transitions from the actual initial state. Then any transition to any state within that program workflow is “valid” until a “terminal” state has been selected, at which point the enrollment in the program ends. Since there’s limited restrictions on which states are valid when, it is very loosely defined, which some developers might see as beneficial. However, while it is somewhat flexible, it does allow some unintended transitions, and prevents some valid transitions e.g. in @dkayiwa 's previously linked diagram

The diagram in question (note that it is not a formal “state transition diagram”), using the current OpenMRS patient program workflow state logic, it is possible to go from second trimester back to first or from first directly to third. It also omits a few terribly unfortunate alternative “terminal” states like false pregnancy, miscarriage, and premature birth, and misses other “initial state” possibilities of someone entering care when they’re already in second or third trimester.

Another example might be a cancer treatment program. This is just an example, I don’t want to make it too complicated by breaking it out into multiple workflows, but that might be more accurate in through a providers’ eyes.

A patient might have some symptom of a cancer (or it could be specifically identified though some regular test), at which point they enter the program and undergo further tests, eventually to determine if it is malignant or benign, they then enter a state where the cells were benign, but still need regular checkups to monitor for malignancy. At some point, malignant cells may be found, and they enter a treatment state. After treatment, the cancer goes into remission and they return to a regular checkup schedule state to monitor for recurrence. The patient could also be moving to a new location, simply lost to followup, or changing healthcare systems and need continuance of care. This implies the need to have some states for leaving the program at any time (freely moving to a “terminal” state) or alternatively unique, intake “initial” state(s) before moving to specific main workflow state(s).

The design choices like whether the initial monitoring state and recurrence monitoring state differ, are still in the hands of the designers/implementers, but I think it’s important that those sorts of questions be considered more thoroughly, and that the underlying workflow design accounts for the designs that are in use, while allowing the for improvements in the Knowledge Organization and Navigation options of OpenMRS. With many reports, forms, and other pieces of related information stored in an OpenMRS deployment, providing directed and automated access to those forms and representations of information based on workflow state(s) seems like a useful improvement, even one that OWAs using REST endpoints could benefit from having.

@mogoodrich I know we’ve also had a brief conversation about how the patient program workflow state history is (and in some ways is not) stored in the database. The relational model doesn’t really lend itself to ordering by the previous state for instance (if it were to be stored with the current state). There is additionally the question of whether storing the input that leads to the transition is another useful piece of information that could/should be preserved.

I’m just putting this out there for discussion. For the current use case I’ve been implementing, I have been able to do what’s required by implementing the additional logic myself, but in general, if this is a common need, it might make sense to provide it in core or at least a specialized module.

@dkayiwa An example of a partial state transition diagram for the more realistic Maternity Program Workflow I suggested (also including a few other possible states), note that not all states have all the transitions I would expect (like lost to follow up). One point being, that even with just this partial state transition diagram there’s obviously much more going on than the simple diagram you previously linked to describes.

While the current implementation could make all states initial except the terminal state, it that would allow all states to go to any other, e.g. rather than miscarriage care only being possible via miscarriage from a “preganancy” state like 1-3 trimester or entering care of provider after a miscarriage already occurred.

@long27km if I’m understanding correctly, for this modelling we’d need to not just the idea of “Program States” but also “Program Transitions/Edges/Some better name” (the “Enter Care”, “Conception”, etc in the above graph) and then as long as we kept a history of “transitions” we could determine the patient’s state at any particular point in time. (And also verify that the “transition history” is valid based on the rules of the specific program).

So, yeah, Programs and States in OpenMRS don’t have that level of detail… others like @burke and @mseaton may have more insight into the history.

I suspect there would be interest in creating a more robust module for states as I think there’s always been the feeling that Programs in OpenMRS could be better designed. It may have to be an entirely new domain however because of the legacy baggage of Programs.

Take care, Mark

@long27km is this something that you are willing to try out in a module? Then we can later on move it to the core platform, just like we have done for diagnosis, allergies, and a few others.


Yes, implementing it as a module first makes sense to me. It should help to further develop it and see the needs and uses others put it to before committing any new design into core. I will see if it’s something I can invest some time in or find devs that can when/if I am not able.

Alternatively, if you can quickly come up with the project wiki page, and are willing to mentor it, you could list it as one of the Google Summer of Code projects.

Ken, thanks for leading this thoughtful conversation.

Have you thought about how your ideas map to the fhir representation of work flows?

I had discussed this idea briefly with @ibacher and was hoping to begin working on this towards the end of next month as the current fhir2 work finishes up.

This would create a new data model to support care plans and would not utilize the existing program data model.

Thanks Jonathan, I was reviewing the last FHIR planning meetings recording, and noticed some mention of workflows there during the discussion regarding the tasks based model proposed between OpenMRS and OpenELIS. I’ll see if I can invest some time in reviewing FHIRs workflows to see how it might integrate and what challenges it could pose. Maybe @janflowers or @pmanko is aware of any other current work or plans to consider here regard the new FHIR module designs needs for OpenMRS functionality?