From c0fac9307a284c9bca1d669f78f4f9aea6c11c04 Mon Sep 17 00:00:00 2001 From: Paul Bugni Date: Wed, 6 Sep 2023 15:53:38 -0700 Subject: [PATCH] TN-3236, sequential hard trigger counts kept in trigger_states.triggers, not previous answers! --- portal/trigger_states/empro_domains.py | 17 +++++++++++------ portal/trigger_states/empro_states.py | 15 ++++++++++----- tests/test_trigger_states.py | 20 ++++++++++++++++---- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/portal/trigger_states/empro_domains.py b/portal/trigger_states/empro_domains.py index d60b69d91..d5167c4b4 100644 --- a/portal/trigger_states/empro_domains.py +++ b/portal/trigger_states/empro_domains.py @@ -28,7 +28,7 @@ class DomainTriggers(object): """ def __init__( - self, domain, current_answers, previous_answers, initial_answers): + self, domain, current_answers, previous_answers, initial_answers, previous_triggers): self.domain = domain self._triggers = dict() @@ -38,6 +38,9 @@ def __init__( self.previous_answers = previous_answers or dict() self.initial_answers = initial_answers or dict() + # Trigger state triggers from previous month, if defined + self.previous_triggers = previous_triggers + @property def triggers(self): self.eval() @@ -92,9 +95,9 @@ def eval(self): sequential_hard_trigger_count = 1 if ( sequential_hard_trigger_count and - self.previous_answers and - sequential_hard_trigger_count_key in self.previous_answers): - sequential_hard_trigger_count = self.previous_answers[sequential_hard_trigger_count_key] + 1 + self.previous_triggers and + sequential_hard_trigger_count_key in self.previous_triggers): + sequential_hard_trigger_count = self.previous_triggers[sequential_hard_trigger_count_key] + 1 self._triggers[sequential_hard_trigger_count_key] = sequential_hard_trigger_count @@ -145,17 +148,19 @@ def obtain_observations(self, qnr): results[domain][link_id] = (int(score), severity) setattr(self, f"{timepoint}_obs", results) - def eval_triggers(self): + def eval_triggers(self, previous_triggers): triggers = dict() triggers['domain'] = dict() for domain in EMPRO_DOMAINS: if domain in self.cur_obs: + prev_triggers_for_domain = previous_triggers["domain"][domain] if previous_triggers else None dt = DomainTriggers( domain=domain, current_answers=self.cur_obs[domain], previous_answers=self.prev_obs.get(domain), - initial_answers=self.initial_obs.get(domain) + initial_answers=self.initial_obs.get(domain), + previous_triggers=prev_triggers_for_domain, ) triggers['domain'][domain] = dt.triggers diff --git a/portal/trigger_states/empro_states.py b/portal/trigger_states/empro_states.py index 70e490711..b3a68e151 100644 --- a/portal/trigger_states/empro_states.py +++ b/portal/trigger_states/empro_states.py @@ -210,9 +210,18 @@ def evaluate_triggers(qnr): ts = users_trigger_state(qnr.subject_id) sm = EMPRO_state(ts) + # include previous month resolved row, if available + previous = TriggerState.query.filter( + TriggerState.user_id == qnr.subject_id).filter( + TriggerState.state == 'resolved').order_by( + TriggerState.timestamp.desc()).first() + # bring together and evaluate available data for triggers dm = DomainManifold(qnr) - ts.triggers = dm.eval_triggers() + previous_triggers = ( + previous if previous and previous.visit_month + 1 == ts.visit_month + else None) + ts.triggers = dm.eval_triggers(previous_triggers) ts.questionnaire_response_id = qnr.id # transition and persist state @@ -225,10 +234,6 @@ def evaluate_triggers(qnr): # a submission closes the window of availability for the # post-intervention clinician follow up. mark state if # one is found - previous = TriggerState.query.filter( - TriggerState.user_id == qnr.subject_id).filter( - TriggerState.state == 'resolved').order_by( - TriggerState.timestamp.desc()).first() if previous and previous.triggers.get('action_state') not in ( 'completed', 'missed', 'not applicable', 'withdrawn'): triggers = copy.deepcopy(previous.triggers) diff --git a/tests/test_trigger_states.py b/tests/test_trigger_states.py index d15b060b3..de471e5b4 100644 --- a/tests/test_trigger_states.py +++ b/tests/test_trigger_states.py @@ -127,6 +127,15 @@ def test_2nd_eval( def test_cur_hard_trigger(): # Single result with a severe should generate a hard (and soft) trigger + + # include a previous triggered state to test sequential count + previous_triggers = { + "ironman_ss.11": "hard", + "ironman_ss.12": "hard", + "ironman_ss.13": "hard", + "_sequential_hard_trigger_count": 3, + } + dt = DomainTriggers( domain='anxious', current_answers={ @@ -134,10 +143,11 @@ def test_cur_hard_trigger(): 'ironman_ss.11': ('2', None), 'ironman_ss.13': ('4', 'penultimate')}, previous_answers=None, - initial_answers=None) + initial_answers=None, + previous_triggers=previous_triggers) assert len([k for k in dt.triggers.keys() if not k.startswith('_')]) == 1 assert 'ironman_ss.13' in dt.triggers - assert dt.triggers[sequential_hard_trigger_count_key] == 1 + assert dt.triggers[sequential_hard_trigger_count_key] == 4 def test_worsening_soft_trigger(): @@ -147,7 +157,8 @@ def test_worsening_soft_trigger(): previous_answers={'ss.21': (2, None), 'ss.15': (2, None)}, current_answers={ 'ss.15': (3, None), 'ss.12': (3, None), 'ss.21': (1, None)}, - initial_answers=None) + initial_answers=None, + previous_triggers=None) assert len([k for k in dt.triggers.keys() if not k.startswith('_')]) == 1 assert dt.triggers['ss.15'] == 'soft' assert dt.triggers[sequential_hard_trigger_count_key] == 0 @@ -162,7 +173,8 @@ def test_worsening_baseline(): domain='anxious', initial_answers=initial_answers, previous_answers=previous_answers, - current_answers=current_answers) + current_answers=current_answers, + previous_triggers=None) assert len([k for k in dt.triggers.keys() if not k.startswith('_')]) == 2 assert dt.triggers['12'] == dt.triggers['21'] == 'hard'