Skip to main content

🧠 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 the DynamicFieldResolver. 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