π§ Problem State Processor
The ProblemStateProcessor
class manages and processes state transitions for problem goals. It determines the next state, updates it, creates interventions, and triggers any associated side effects.
All it's decisions are based in the protocol definition. All state evaluations are delegated to the ProtocolDefinition
.
βοΈ Overviewβ
Purposeβ
The processor is called whenever an event might trigger a transition in a problem goal state. It:
- Handles both manual and automatic state transitions (evaluates whether we need to transition to a new state or not)
- Updates the problem goal state (which also creates a new problem state transition)
- Creates interventions based on the new state
- Executes side-effect callbacks for manual transitions
- Logs the process and errors
Key Featuresβ
- Manual and Automatic Transitions: Supports both user-initiated (manual) and system-initiated (automatic) state transitions.
- Intervention Creation: Automatically creates interventions based on the new state.
- Dynamic Field Resolution: Resolves dynamic fields for due dates and custom fields.
- Error Handling: Reports errors to the logging system and gracefully handles failures.
- Dry Run Support: Allows testing transitions without making actual changes.
π process
Methodβ
Descriptionβ
This is the entry point. Called to evaluate if we need to transition the problem state, and if so, execute the transition effects (e.g. create interventions)
Parametersβ
problem_goal
(required): the problem goal to process.new_state
(optional): manually selected state. Used from the front end state dropdown.transition_reason
(optional): reason for manual transition. Filled by the user that is changing the state manually.dry_run
(optional): if true, doesnβt persist changes. This is useful to make sure the outcome is what we expect before affecting actual data. Used in backfill scripts.fetcher
(optional): data fetcher instance. Used by the transition's conditions to decide if we should move to a certain state.problem_protocol
(optional): protocol to evaluate against.skip_interventions
(optional): skips creating interventions. Useful for backfill scripts, in which we might want to set the state, but not create interventions associated with the sate.trigger_source
(optional): the source of the trigger for the transition. Used for logging purposes.force_intervention_creation
(optional): creates interventions even if the state hasnβt changed.
Returnsβ
true
if processing succeeds.false
if an error occurs.
β‘ When Is It Triggered?β
The process
method runs in several scenarios:
- β When a PHQ-9 form is completed by the patient. Calls the process asyncronically for every active problem goal of that patient with an enabled problem type
- β When an Intake form is completed by the patient. Calls the process asyncronically for every active problem goal of that patient with an enabled problem type
- β On intervention completion. Calls the process syncronically for all problem goals linked to that intervention
- β When a problem is created (initial state, no interventions)
- β When a problem is started (creates interventions)
- β
When running
ProblemProtocolEvaluatorJob
π§© Intervention Creationβ
Interventions are automatically created when:
- The state changes and the problem is
in_progress
- A problem is started, even if the state hasn't changed yet
Deduplication is handled using:
deduplication_key
deduplication_params
π Callbacksβ
Manual transitions can trigger callbacks.
Example: In the housing protocol, changing the state manually updates the patientβs housing_status
.
β³ Dynamic Valuesβ
Some values can't be hardcoded in the protocol definition and must be resolved at runtime.
- Dates: Expressed in
unit
.unit_type
. Examples:1.week
,3.months
- Dynamic values: Use the
dynamic#
prefix and are resolved by theDynamicFieldResolver
. In that file you can find the currently supported dynamic resolvers
π¨ Error Handlingβ
If processing fails:
- Error is reported to
Rails.error.report
- The
Collector
logs and finalizes the trace - The method returns
false