App Framework: AF-59 Extensions to support a "requiredStates" property

Hi @mogoodrich, @ball, @ssmusoke,

We are working on AF-59 and I would like to finalise the design.

Our need is to control the enabling/disabling of extensions depending on whether the patient is in either of the listed states, eg. something like that:

"requiredStates": [
  "CIEL:1234",
  "52ff1d71-c230-492c-b383-d04b9232af8f",
  "PATIENT_ADMITTED"
]

This would read as

“enable this extension if the patient is in either of those states: CIEL:1234, 52ff1d71-c230-492c-b383-d04b9232af8f, PATIENT_ADMITTED

Do you guys envision needing more complicated logical combination of states?

Cc @jesplana @samuel34

1 Like

@mksd one thing I’d mention is that the same concept might be used by different states in different program workflows, so just referring to states by concept might not be a sufficient approach.

Thanks @mseaton, that’s a good point. Do you think we should refer to the ProgramWorkflowState's name (or UUID)? Rather than a reference to the defining concepts.

I don’t think name is ever populated in program workflow state, as we made some effort in earlier days (probably misguided) to delegate that to the associated concept. I think the state UUID or some combination of state concept (name, uuid, mapping, etc) and program/workflow information would be needed.

Actually Iniz does fill the name in accordance to the concept name. See here.

But sure, name + UUID.

Interesting… the java object ProgramWorkflowState still have a name property because it inherits from BaseOpenMRSMetadata, but that is never stored behind the scenes:

So @mksd so I think that line that does the setName just ends up putting it into the ether somewhere… :slight_smile:

Yeah I would agree with @mseaton that we’d want to support a pattern where you specify a workflow and a state. And you would reference a workflow either by it’s name or uuid, and a state by the underlying concept (name, uuid or mapping).

I could see I us wanted to support more complex logic in the future, but I think a list of workflow/workflow state pairs ORed together is great for now and then we could update if needed in the future.

Thanks for working on this!

Take care, Mark

fyi @ball @ddesimone I think we’ve talked about wanting to make some forms only available to patients if they are enrolled in a program, and this would facilitate that.

@mksd Excellent feature, would it be requiredProgramStates for clarity, not sure if there will be any other states (but just my OCD acting up)

@mogoodrich Would that need (which we also have) also be something like requiredEncounterType (so that the form only appears if there is a specified encounter type)

@mksd I am hoping that I am not overloading this request, but would this design also be extended to cater for forms that only appear when a certain concept value is present?

@ssmusoke you mean like the extension is only present if the patient has any encounter of a certain type? (Ie the “follow up” form is only present after a “initial” encounter type had been created)?

That could be valuable as well, but I’m fine with just states for now if that’s all @mksd needs.

Would this be delivered by? [RA-1619] - OpenMRS Issues

A developer in Nigeria is taking on this ticket. @aeze

Would this be delivered by? [RA-1620] - OpenMRS Issues

A developer in Nigeria is taking on this ticket. @aeze

@mogoodrich yes that is correct, I am just wondering if we can use the same architecture or design to simplify those addition use cases

Lol, I guess better in the ether than nowhere at all right.

So, looks like we would want to make an AND across multiple ‘requires’:

"requiredPrograms": "...",
"requiredWorkflows": "...",
"requiredStates": "..."

Cc @samuel34


@ssmusoke we might need to cover also one the use cases that you mentioned, aka enabling/disabling a form depending on whether a certain obs was recorded for that patient, is that what you’ve got in mind?

@mksd Yes, then in that case you can use

"requiredPrograms": "...",
"requiredProgramWorkflows": "...",
"requiredProgramStates": "..."

that way you have an and across the ones which are provided

I think "requiredEncounterTypes": "..." would be a great addition too. We can deal with concept values at a later date :wink:

@ssmusoke you really don’t like requiredState do you? :wink:

If we wanted to be completely consistent, in particular with the data model, we should name them like this:

  • requiredPrograms
  • requiredProgramWorkflows
  • requiredProgramWorkflowStates

Thoughts?

This doesn’t seem exactly right to me. I’d think requiring a program, a workflow, or a state are all independently useful things, but this model still doesn’t allow you to distinguish what program or workflow a state refers to if multiple are included.

I think we should establish some standard like we have in htmlformentry, where referring to concepts by mapping is always done as a single configuration string: “source:term”. So referring to a program workflow state could be simply “uuid-of the-state” or it could be “program-ref:workflow-ref:state-ref”, etc.

Mike

Ok first of all yes, having three ‘requires’ will not work to cover all possible use cases, it is the wrong model.

Ok so here we assume that a single UUID necessarily points to a ProgramWorkflowState, but what if we want to simply say something like “enable this extension is the patient is enrolled in program XYZ”?

I guess we will need something more descriptive, such as:

"required...": [
  {
    "program": "ref-to-prog-A"
  },
  {
    "program": "ref-to-prog-B",
    "workflow": "ref-to-flow-A",
    "state": "ref-to-state-A"
  },
  {
    "state": "ref-to-state-B"
  }
]

This kind of stuff would enable the extension if any of those conditions are met:

  • Patient is enrolled in program A.
  • Patient is enrolled in program B and within workflow A and is in state A.
  • Patient is in state B.

That sounds good to me. #2 and #3 are the same thing, and I would think #2 is unnecessary assuming your ‘ref-to-state-B’ in #3 is a uuid to the actual program_workflow_state, as a state is foreign key’d back to the workflow it belongs to. While a concept can be associated with multiple states, one state is only assigned to one workflow. So, #2 is needed only if your ‘ref-to-state-A’ is a concept reference rather than a state reference.

If I gat you right, we wanna have more specificity as possible, specify ‘program + the-worflow + the-target-state’

We could have a simple lightweight java wrapper class to account for the json object literally something like :

public class ProgramWorflowStateContextModel {
        // program uuid
        private String program;
        // workflow name
        private String workflow;
        // reference by associated concept
        private String state;

        // getters and setters
}

So the requiredStates property is an array of object literals parseable to ProgramWorflowStateContextModel

"requiredStates": [
  {
    "program": "ref-to-prog-A",
    "workflow": "ref-to-flow-A",
    "state": "ref-to-state-A"
  },
  {
    "program": "ref-to-prog-B",
    "workflow": "ref-to-flow-B",
    "state": "ref-to-state-B"
  },
  {
    "program": "ref-to-prog-C",
    "workflow": "ref-to-flow-C",
    "state": "ref-to-state-C"
  }
]

Does something like the above make sense @mseaton ?

cc: @mksd

A couple of things, it won’t be called requiredStates, since the whole thing goes beyond that.

Keep in mind that both combinations of requirements and isolated requirements should work, as much as possible. For instance this should work:

{
  "program": "ref-to-prog-A"
}

This means “the patient is enrolled in program A.” This assumes that the reference to the program is enough to identify it uniquely. If not, we will have to throw I guess.

This should also work:

{
  "state": "ref-to-state-A"
}

This means “the patient should be in the state A”. This assumes that the reference to the state is enough to identify it uniquely.

This should also work:

{
  "program": "ref-to-prog-A",
  "workflow": "ref-to-flow-A",
  "state": "ref-to-state-A"
}

This means “patient is enrolled in program A, is within workflow A and is in the state A.” There should be enough information there to spot this combination of requirements.

@mksd I like this, maybe the tag can be requiredProgramWorkflowAndStates :slight_smile: (long but very clear)

Also is the expectation that there will be a global “AND” between the different array of object literals or do you expect to have cases where the operator can be changed to an “OR”

by the way I see the concept approach similar as

"requiredObservations": [
  {
     "concept": "conceptId, conceptuuid or mapping",
      "value": "observation value",
      "operator": " > < = !="
  }
]