def test_initial_recur_baseline_done(self): # backdate to be within the first recurrence window backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=3, days=2)) self.bless_with_basics( setdate=backdate, local_metastatic='metastatic') # add baseline QNRs, as if submitted nearly 3 months ago, during # baseline window backdated = nowish - relativedelta(months=2, days=25) baseline = QuestionnaireBank.query.filter_by( name='metastatic').one() for instrument in metastatic_baseline_instruments: mock_qr(instrument, qb=baseline, timestamp=backdated) self.test_user = db.session.merge(self.test_user) # Check status during baseline window a_s_baseline = QB_Status( user=self.test_user, as_of_date=backdated) assert a_s_baseline.overall_status == OverallStatus.completed assert not a_s_baseline.instruments_needing_full_assessment() # Whereas "current" status for the initial recurrence show due. a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.due # in the initial window w/ no questionnaires submitted # should include all from initial recur assert set(a_s.instruments_needing_full_assessment()) == metastatic_3
def test_intervention_in_progress(self): # testing intervention-based QBs q = self.add_questionnaire('q') interv = Intervention(name='interv', description='test') with SessionScope(db): db.session.add(interv) db.session.commit() q, interv, self.test_user = map(db.session.merge, (q, interv, self.test_user)) qb = QuestionnaireBank( name='qb', intervention_id=interv.id, classification='baseline', start='{"days": 0}', expired='{"days": 2}') qbq = QuestionnaireBankQuestionnaire(rank=0, questionnaire=q) qb.questionnaires.append(qbq) # user with biopsy should return biopsy date self.login() self.test_user.save_observation( codeable_concept=CC.BIOPSY, value_quantity=CC.TRUE_VALUE, audit=Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID), status='', issued=now) self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) obs = self.test_user.observations.first() assert obs.codeable_concept.codings[0].display == 'biopsy' assert trigger_date(self.test_user, qb) == obs.issued # add mock in-process QB - confirm current qb is still correct mock_qr('q', 'in-progress', qb=qb) self.test_user, qb = map(db.session.merge, (self.test_user, qb)) qb_stat = QB_Status(user=self.test_user, as_of_date=now) assert qb_stat.current_qbd().questionnaire_bank == qb
def test_lookup_with_intervention(self): intv = Intervention(name='TEST', description='Test Intervention') epic26 = self.add_questionnaire(name='epic26') eproms_add = self.add_questionnaire(name='eproms_add') with SessionScope(db): db.session.add(intv) db.session.commit() intv, epic26, eproms_add = map(db.session.merge, (intv, epic26, eproms_add)) bank = QuestionnaireBank(name='CRV', intervention_id=intv.id, start='{"days": 7}', expired='{"days": 90}') for rank, q in enumerate((epic26, eproms_add)): qbq = QuestionnaireBankQuestionnaire(questionnaire_id=q.id, rank=rank) bank.questionnaires.append(qbq) self.test_user.interventions.append(intv) self.login() self.add_required_clinical_data() with SessionScope(db): db.session.add(bank) db.session.commit() # Patient associated with INTV intervention should generate appropriate # questionnaires as of start date self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) qb_status = QB_Status(self.test_user, as_of_date=now + relativedelta(days=8)) qb = qb_status.current_qbd().questionnaire_bank results = list(qb.questionnaires) assert len(results) == 2
def test_localized_using_org(self): self.bless_with_basics(local_metastatic='localized', setdate=now) self.test_user = db.session.merge(self.test_user) # confirm appropriate instruments a_s = QB_Status(user=self.test_user, as_of_date=now) assert (set(a_s.instruments_needing_full_assessment()) == localized_instruments)
def test_enrolled_in_localized(self): """localized should include baseline but not indefinite""" self.bless_with_basics(local_metastatic='localized') user = db.session.merge(self.test_user) a_s = QB_Status(user=user, as_of_date=now) assert a_s.enrolled_in_classification('baseline') assert not a_s.enrolled_in_classification('indefinite')
def test_overdue_stats(self): self.promote_user(user=self.test_user, role_name=ROLE.PATIENT.value) rp = ResearchProtocol(name='proto') with SessionScope(db): db.session.add(rp) db.session.commit() rp = db.session.merge(rp) rp_id = rp.id crv = Organization(name='CRV') crv.research_protocols.append(rp) epic26 = self.add_questionnaire(name='epic26') with SessionScope(db): db.session.add(crv) db.session.commit() crv, epic26 = map(db.session.merge, (crv, epic26)) crv_id = crv.id bank = QuestionnaireBank(name='CRV', research_protocol_id=rp_id, start='{"days": 1}', overdue='{"days": 2}', expired='{"days": 90}') qbq = QuestionnaireBankQuestionnaire(questionnaire_id=epic26.id, rank=0) bank.questionnaires.append(qbq) with SessionScope(db): db.session.add(bank) db.session.commit() self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) self.consent_with_org(org_id=crv_id) self.test_user = db.session.merge(self.test_user) # test user with status = 'Expired' (should not show up) a_s = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert a_s.overall_status == OverallStatus.expired ostats = self.get_ostats() assert len(ostats) == 0 # test user with status = 'Overdue' (should show up) self.consent_with_org(org_id=crv_id, backdate=relativedelta(days=18)) with SessionScope(db): db.session.add(bank) db.session.commit() crv, self.test_user = map(db.session.merge, (crv, self.test_user)) invalidate_users_QBT(self.test_user.id) a_s = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert a_s.overall_status == OverallStatus.overdue ostats = self.get_ostats() assert len(ostats) == 1 assert ostats[(crv.id, crv.name)] == [(15, TEST_USER_ID)]
def test_visit_baseline(self): crv = self.setup_org_qbs() self.bless_with_basics(setdate=now) # pick up a consent, etc. self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) qstats = QB_Status(self.test_user, now) qbd = qstats.current_qbd() assert visit_name(qbd) == "Baseline"
def test_visit_9mo(self): crv = self.setup_org_qbs() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=9)) self.bless_with_basics(setdate=backdate) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) qstats = QB_Status(self.test_user, nowish + timedelta(hours=1)) qbd = qstats.current_qbd() assert visit_name(qbd) == "Month 9"
def test_no_start_date(self): # W/O a biopsy (i.e. event start date), no questionnaries self.promote_user(role_name=ROLE.PATIENT.value) # toggle default setup - set biopsy false for test user self.login() self.test_user = db.session.merge(self.test_user) self.test_user.save_observation( codeable_concept=CC.BIOPSY, value_quantity=CC.FALSE_VALUE, audit=Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID), status='final', issued=now) qstats = QB_Status(self.test_user, now) assert not qstats.current_qbd() assert not qstats.enrolled_in_classification("baseline")
def test_localized_on_time(self): # User finished both on time self.bless_with_basics(local_metastatic='localized', setdate=now) mock_qr(instrument_id='eproms_add', timestamp=now) mock_qr(instrument_id='epic26', timestamp=now) mock_qr(instrument_id='comorb', timestamp=now) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=now) assert a_s.overall_status == OverallStatus.completed # confirm appropriate instruments assert not a_s.instruments_needing_full_assessment('all')
def test_localized_in_process(self): # User finished one, time remains for other self.bless_with_basics(local_metastatic='localized', setdate=now) mock_qr(instrument_id='eproms_add', timestamp=now) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=now) assert a_s.overall_status == OverallStatus.in_progress # confirm appropriate instruments assert (localized_instruments - set(a_s.instruments_needing_full_assessment('all')) == {'eproms_add'}) assert not a_s.instruments_in_progress()
def test_secondary_recur_due(self): # backdate so baseline q's have expired, and we are within the # second recurrence window backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=6, hours=1)) self.bless_with_basics( setdate=backdate, local_metastatic='metastatic') self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.due # w/ no questionnaires submitted # should include all from second recur assert set(a_s.instruments_needing_full_assessment()) == metastatic_4
def test_metastatic_due(self): # hasn't taken, but still in OverallStatus.due period self.bless_with_basics(local_metastatic='metastatic', setdate=now) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=now) assert a_s.overall_status == OverallStatus.due # confirm list of expected intruments needing attention assert (metastatic_baseline_instruments == set(a_s.instruments_needing_full_assessment())) assert not a_s.instruments_in_progress() # metastatic indefinite should also be 'due' assert (metastatic_indefinite_instruments == set(a_s.instruments_needing_full_assessment('indefinite'))) assert not a_s.instruments_in_progress('indefinite')
def test_metastatic_on_time(self): # User finished both on time self.bless_with_basics( local_metastatic='metastatic', setdate=now) for i in metastatic_baseline_instruments: mock_qr(instrument_id=i, timestamp=now) mi_qb = QuestionnaireBank.query.filter_by( name='metastatic_indefinite').first() mock_qr(instrument_id='irondemog', qb=mi_qb, timestamp=now) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=now) assert a_s.overall_status == OverallStatus.completed # shouldn't need full or any inprocess assert not a_s.instruments_needing_full_assessment('all') assert not a_s.instruments_in_progress('all')
def test_early(self): # Prior to days passing, no message should be generated mock_communication_request('symptom_tracker', '{"days": 90}') self.promote_user(role_name=ROLE.PATIENT.value) self.login() self.add_required_clinical_data(backdate=relativedelta(days=89)) self.test_user = db.session.merge(self.test_user) # Confirm test user qualifies for ST QB qbstatus = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert qbstatus.enrolled_in_classification('baseline') # Being a day short, shouldn't fire update_patient_loop(update_cache=False, queue_messages=True) expected = Communication.query.first() assert not expected
def mock_qr( instrument_id, status='completed', timestamp=None, qb=None, doc_id=None, iteration=None, user_id=TEST_USER_ID, entry_method=None): if not doc_id: doc_id = ''.join(choice(ascii_letters) for _ in range(10)) timestamp = timestamp or datetime.utcnow() qr_document = { "questionnaire": { "display": "Additional questions", "reference": "https://{}/api/questionnaires/{}".format( 'SERVER_NAME', instrument_id)}, "identifier": { "use": "official", "label": "cPRO survey session ID", "value": doc_id, "system": "https://stg-ae.us.truenth.org/eproms-demo"} } enc = Encounter( status='planned', auth_method='url_authenticated', user_id=user_id, start_time=timestamp) if entry_method: enc.type.append(entry_method.codings[0]) with SessionScope(db): db.session.add(enc) db.session.commit() enc = db.session.merge(enc) if not qb: qstats = QB_Status(get_user(user_id), timestamp) qbd = qstats.current_qbd() qb, iteration = qbd.questionnaire_bank, qbd.iteration qr = QuestionnaireResponse( subject_id=user_id, status=status, authored=timestamp, document=qr_document, encounter_id=enc.id, questionnaire_bank=qb, qb_iteration=iteration) with SessionScope(db): db.session.add(qr) db.session.commit() invalidate_users_QBT(user_id=user_id)
def test_completed_older_rp(self): """If current qb completed on older rp, should show as done""" fourmonthsago = now - timedelta(days=120) weekago = now - timedelta(weeks=1) twoweeksago = now - timedelta(weeks=2) org = self.setup_org_qbs(rp_name='v2', retired_as_of=weekago) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(org) audit = Audit( user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc = UserConsent( user_id=TEST_USER_ID, organization_id=org_id, audit=audit, agreement_url='http://no.com', acceptance_date=fourmonthsago) with SessionScope(db): db.session.add(audit) db.session.add(uc) db.session.commit() user = db.session.merge(self.test_user) # Now, should still be rp v3, 3mo recurrence a_s = QB_Status(user=user, as_of_date=now) assert (a_s.current_qbd().questionnaire_bank.name == 'CRV_recurring_3mo_period v3') assert a_s.instruments_needing_full_assessment() == [ 'epic26_v3', 'eortc_v3'] # Complete the questionnaire from the 3mo v2 QB v2qb = QuestionnaireBank.query.filter( QuestionnaireBank.name == 'CRV_recurring_3mo_period v2').one() mock_qr('epic26_v2', timestamp=twoweeksago, qb=v2qb, iteration=0) mock_qr('eortc_v2', timestamp=twoweeksago, qb=v2qb, iteration=0) # Two weeks ago, should be completed user = db.session.merge(user) a_s = QB_Status(user=user, as_of_date=twoweeksago) assert a_s.overall_status == OverallStatus.completed # Current should also be completed, even tho protocol changed a_s = QB_Status(user=user, as_of_date=now) assert a_s.overall_status == OverallStatus.completed
def test_2nd_recur_due(self): # backdate so baseline q's have expired, and we within the 2nd # recurrence window backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=9, hours=1)) self.bless_with_basics( setdate=backdate, local_metastatic='metastatic') self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.due # in the initial window w/ no questionnaires submitted # should include all from initial recur assert set(a_s.instruments_needing_full_assessment()) == metastatic_3 # however, we should be looking at iteration 2 (zero index)! assert a_s.current_qbd().iteration == 1
def test_boundary_overdue(self): self.login() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=3, hours=-1)) self.bless_with_basics( setdate=backdate, local_metastatic='localized') self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.overdue
def test_none_org(self): # check users w/ none of the above org self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(Organization.query.get(0)) self.login() self.bless_with_basics( local_metastatic='metastatic', setdate=now) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=now) assert a_s.overall_status == OverallStatus.due
def mock_qr( instrument_id, status='completed', timestamp=None, qb=None, doc_id=None, iteration=None, user_id=TEST_USER_ID): if not doc_id: doc_id = ''.join(choice(ascii_letters) for _ in range(10)) timestamp = timestamp or datetime.utcnow() qr_document = { "questionnaire": { "display": "Additional questions", "reference": "https://{}/api/questionnaires/{}".format( 'SERVER_NAME', instrument_id)}, "identifier": { "use": "official", "label": "cPRO survey session ID", "value": doc_id, "system": "https://stg-ae.us.truenth.org/eproms-demo"} } enc = Encounter( status='planned', auth_method='url_authenticated', user_id=user_id, start_time=timestamp) with SessionScope(db): db.session.add(enc) db.session.commit() enc = db.session.merge(enc) if not qb: qstats = QB_Status(get_user(user_id), timestamp) qbd = qstats.current_qbd() qb, iteration = qbd.questionnaire_bank, qbd.iteration qr = QuestionnaireResponse( subject_id=user_id, status=status, authored=timestamp, document=qr_document, encounter_id=enc.id, questionnaire_bank=qb, qb_iteration=iteration) with SessionScope(db): db.session.add(qr) db.session.commit() invalidate_users_QBT(user_id=user_id)
def test_lookup_for_user(self): crv, rp, rp_id = self.setup_org_n_rp(org_name='CRV') epic26 = self.add_questionnaire(name='epic26') eproms_add = self.add_questionnaire(name='eproms_add') comorb = self.add_questionnaire(name='comorb') crv, epic26, eproms_add, comorb = map( db.session.merge, (crv, epic26, eproms_add, comorb)) bank = QuestionnaireBank( name='CRV', research_protocol_id=rp_id, start='{"days": 7}', expired='{"days": 90}') for rank, q in enumerate((epic26, eproms_add, comorb)): qbq = QuestionnaireBankQuestionnaire( questionnaire_id=q.id, rank=rank) bank.questionnaires.append(qbq) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) self.consent_with_org(org_id=crv.id, setdate=now) self.promote_user(role_name=ROLE.PATIENT.value) with SessionScope(db): db.session.add(bank) db.session.commit() # User associated with CRV org should generate appropriate # questionnaires self.test_user = db.session.merge(self.test_user) # Doesn't start for 7 days, initially shouldn't get any qb_stat = QB_Status(user=self.test_user, as_of_date=now) assert qb_stat.current_qbd() is None qb_stat = QB_Status( user=self.test_user, as_of_date=now + relativedelta(days=7)) qb = qb_stat.current_qbd().questionnaire_bank results = list(qb.questionnaires) assert len(results) == 3 # confirm rank sticks assert results[0].name == 'epic26' assert results[2].name == 'comorb'
def test_st_done(self): # Symptom Tracker QB with completed shouldn't fire mock_communication_request('symptom_tracker', '{"days": 90}') self.promote_user(role_name=ROLE.PATIENT.value) self.login() self.add_required_clinical_data(backdate=relativedelta(days=91)) self.test_user = db.session.merge(self.test_user) # Confirm test user qualifies for ST QB qbstatus = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert qbstatus.enrolled_in_classification('baseline') for instrument in symptom_tracker_instruments: mock_qr(instrument_id=instrument) # With all q's done, shouldn't generate a message update_patient_loop(update_cache=False, queue_messages=True) expected = Communication.query.first() assert not expected
def test_procedure_update(self): # Newer procedure should alter trigger date and suspend message mock_communication_request('symptom_tracker', '{"days": 90}') self.promote_user(role_name=ROLE.PATIENT.value) self.login() self.add_required_clinical_data(backdate=relativedelta(days=91)) self.test_user = db.session.merge(self.test_user) # Confirm test user qualifies for ST QB qbstatus = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert qbstatus.enrolled_in_classification('baseline') # Add fresh procedure self.add_procedure('4', 'External beam radiation therapy', ICHOM) # New procedure date should suspend message update_patient_loop(update_cache=False, queue_messages=True) expected = Communication.query.first() assert not expected
def test_metastatic_as_of_date(self): # backdating consent beyond expired and the status lookup date # within a valid window should show available assessments. backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=3)) self.bless_with_basics(setdate=backdate, local_metastatic='metastatic') # backdate so the baseline q's have expired mock_qr(instrument_id='epic23', status='in-progress', doc_id='doc-23', timestamp=backdate) self.test_user = db.session.merge(self.test_user) as_of_date = backdate + relativedelta(days=2) a_s = QB_Status(user=self.test_user, as_of_date=as_of_date) assert a_s.overall_status == OverallStatus.in_progress # with only epic26 started, should see results for both # instruments_needing_full_assessment and instruments_in_progress assert ['doc-23'] == a_s.instruments_in_progress() assert a_s.instruments_needing_full_assessment()
def test_st_metastatic(self): # Symptom Tracker QB on metastatic patient shouldn't qualify mock_communication_request('symptom_tracker', '{"days": 90}') self.promote_user(role_name=ROLE.PATIENT.value) self.login() self.add_required_clinical_data(backdate=relativedelta(days=91)) self.test_user = db.session.merge(self.test_user) self.test_user.save_observation( codeable_concept=CC.PCaLocalized, value_quantity=CC.FALSE_VALUE, audit=Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID), status='final', issued=None) # Confirm test user doesn't qualify for ST QB qbstatus = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert not qbstatus.enrolled_in_classification('baseline') # shouldn't generate a message either update_patient_loop(update_cache=False, queue_messages=True) expected = Communication.query.first() assert not expected
def test_boundary_in_progress_expired(self): self.login() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=3, hours=1)) self.bless_with_basics( setdate=backdate, local_metastatic='localized') for instrument in localized_instruments: mock_qr( instrument_id=instrument, status='in-progress', timestamp=nowish-relativedelta(days=1)) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.partially_completed
def test_localized_overdue(self): # if the user completed something on time, and nothing else # is due, should see the thank you message. backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=3, hours=1)) self.bless_with_basics( setdate=backdate, local_metastatic='localized') # backdate so the baseline q's have expired mock_qr( instrument_id='epic26', status='in-progress', timestamp=backdate) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.partially_completed # with all q's expired, # instruments_needing_full_assessment and instruments_in_progress # should be empty assert not a_s.instruments_needing_full_assessment() assert not a_s.instruments_in_progress()
def test_st_metastatic(self): # Symptom Tracker QB on metastatic patient shouldn't qualify mock_communication_request('symptom_tracker', '{"days": 90}') self.promote_user(role_name=ROLE.PATIENT.value) self.login() self.add_required_clinical_data(backdate=relativedelta(days=91)) self.test_user = db.session.merge(self.test_user) self.test_user.save_observation(codeable_concept=CC.PCaLocalized, value_quantity=CC.FALSE_VALUE, audit=Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID), status='final', issued=None) # Confirm test user doesn't qualify for ST QB qbstatus = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert not qbstatus.enrolled_in_classification('baseline') # shouldn't generate a message either update_patient_loop(update_cache=False, queue_messages=True) expected = Communication.query.first() assert not expected
def test_lookup_with_intervention(self): intv = Intervention(name='TEST', description='Test Intervention') epic26 = self.add_questionnaire(name='epic26') eproms_add = self.add_questionnaire(name='eproms_add') with SessionScope(db): db.session.add(intv) db.session.commit() intv, epic26, eproms_add = map( db.session.merge, (intv, epic26, eproms_add)) bank = QuestionnaireBank( name='CRV', intervention_id=intv.id, start='{"days": 7}', expired='{"days": 90}') for rank, q in enumerate((epic26, eproms_add)): qbq = QuestionnaireBankQuestionnaire( questionnaire_id=q.id, rank=rank) bank.questionnaires.append(qbq) self.test_user.interventions.append(intv) self.login() self.add_required_clinical_data() with SessionScope(db): db.session.add(bank) db.session.commit() # Patient associated with INTV intervention should generate appropriate # questionnaires as of start date self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) qb_status = QB_Status( self.test_user, as_of_date=now+relativedelta(days=8)) qb = qb_status.current_qbd().questionnaire_bank results = list(qb.questionnaires) assert len(results) == 2
def test_st_undone(self): # Symptom Tracker QB with incompleted should generate communication mock_communication_request('symptom_tracker', '{"days": 30}') self.app.config['NO_CHALLENGE_WO_DATA'] = False self.promote_user(role_name=ROLE.PATIENT.value) self.login() self.add_required_clinical_data(backdate=relativedelta(days=31)) self.test_user = db.session.merge(self.test_user) self.test_user.birthdate = '1969-07-16' # Confirm test user qualifies for ST QB qstats = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert qstats.enrolled_in_classification('baseline') # With most q's undone, should generate a message mock_qr(instrument_id='epic26') invalidate_users_QBT(TEST_USER_ID) self.test_user = db.session.merge(self.test_user) qstats = QB_Status(self.test_user, as_of_date=datetime.utcnow()) assert OverallStatus.in_progress == qstats.overall_status update_patient_loop(update_cache=False, queue_messages=True) expected = Communication.query.first() assert expected
def test_intervention_in_progress(self): # testing intervention-based QBs q = self.add_questionnaire('q') interv = Intervention(name='interv', description='test') with SessionScope(db): db.session.add(interv) db.session.commit() q, interv, self.test_user = map(db.session.merge, (q, interv, self.test_user)) qb = QuestionnaireBank(name='qb', intervention_id=interv.id, classification='baseline', start='{"days": 0}', expired='{"days": 2}') qbq = QuestionnaireBankQuestionnaire(rank=0, questionnaire=q) qb.questionnaires.append(qbq) # user with biopsy should return biopsy date self.login() self.test_user.save_observation(codeable_concept=CC.BIOPSY, value_quantity=CC.TRUE_VALUE, audit=Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID), status='', issued=now) self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) obs = self.test_user.observations.first() assert obs.codeable_concept.codings[0].display == 'biopsy' assert trigger_date(self.test_user, qb) == obs.issued # add mock in-process QB - confirm current qb is still correct mock_qr('q', 'in-progress', qb=qb) self.test_user, qb = map(db.session.merge, (self.test_user, qb)) qb_stat = QB_Status(user=self.test_user, as_of_date=now) assert qb_stat.current_qbd().questionnaire_bank == qb
def test_completed_older_rp(self): """If current qb completed on older rp, should show as done""" fourmonthsago = now - timedelta(days=120) weekago = now - timedelta(weeks=1) twoweeksago = now - timedelta(weeks=2) org = self.setup_org_qbs(rp_name='v2', retired_as_of=weekago) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(org) audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc = UserConsent(user_id=TEST_USER_ID, organization_id=org_id, audit=audit, agreement_url='http://no.com', acceptance_date=fourmonthsago) with SessionScope(db): db.session.add(audit) db.session.add(uc) db.session.commit() user = db.session.merge(self.test_user) # Now, should still be rp v3, 3mo recurrence a_s = QB_Status(user=user, as_of_date=now) assert (a_s.current_qbd().questionnaire_bank.name == 'CRV_recurring_3mo_period v3') assert a_s.instruments_needing_full_assessment() == [ 'epic26_v3', 'eortc_v3' ] # Complete the questionnaire from the 3mo v2 QB v2qb = QuestionnaireBank.query.filter( QuestionnaireBank.name == 'CRV_recurring_3mo_period v2').one() mock_qr('epic26_v2', timestamp=twoweeksago, qb=v2qb, iteration=0) mock_qr('eortc_v2', timestamp=twoweeksago, qb=v2qb, iteration=0) # Two weeks ago, should be completed user = db.session.merge(user) a_s = QB_Status(user=user, as_of_date=twoweeksago) assert a_s.overall_status == OverallStatus.completed # Current should also be completed, even tho protocol changed a_s = QB_Status(user=user, as_of_date=now) assert a_s.overall_status == OverallStatus.completed
def test_boundary_recurring_in_progress(self): self.login() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=6, hours=-1)) self.bless_with_basics( setdate=backdate, local_metastatic='metastatic') mr3_qb = QuestionnaireBank.query.filter_by( name='metastatic_recurring3').first() for instrument in metastatic_3: mock_qr( instrument_id=instrument, status='in-progress', qb=mr3_qb, timestamp=nowish, iteration=0) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.in_progress
def test_all_expired_old_tx(self): self.login() # backdate outside of baseline window (which uses consent date) backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=4, hours=1)) self.bless_with_basics( setdate=backdate, local_metastatic='localized') # provide treatment date outside of all recurrences tx_date = datetime(2000, 3, 12, 0, 0, 00, 000000) self.add_procedure(code='7', display='Focal therapy', system=ICHOM, setdate=tx_date) self.test_user = db.session.merge(self.test_user) a_s = QB_Status(user=self.test_user, as_of_date=nowish) assert a_s.overall_status == OverallStatus.expired
def test_lookup_for_user(self): crv, rp, rp_id = self.setup_org_n_rp(org_name='CRV') epic26 = self.add_questionnaire(name='epic26') eproms_add = self.add_questionnaire(name='eproms_add') comorb = self.add_questionnaire(name='comorb') crv, epic26, eproms_add, comorb = map( db.session.merge, (crv, epic26, eproms_add, comorb)) bank = QuestionnaireBank(name='CRV', research_protocol_id=rp_id, start='{"days": 7}', expired='{"days": 90}') for rank, q in enumerate((epic26, eproms_add, comorb)): qbq = QuestionnaireBankQuestionnaire(questionnaire_id=q.id, rank=rank) bank.questionnaires.append(qbq) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) self.consent_with_org(org_id=crv.id, setdate=now) self.promote_user(role_name=ROLE.PATIENT.value) with SessionScope(db): db.session.add(bank) db.session.commit() # User associated with CRV org should generate appropriate # questionnaires self.test_user = db.session.merge(self.test_user) # Doesn't start for 7 days, initially shouldn't get any qb_stat = QB_Status(user=self.test_user, as_of_date=now) assert qb_stat.current_qbd() is None qb_stat = QB_Status(user=self.test_user, as_of_date=now + relativedelta(days=7)) qb = qb_stat.current_qbd().questionnaire_bank results = list(qb.questionnaires) assert len(results) == 3 # confirm rank sticks assert results[0].name == 'epic26' assert results[2].name == 'comorb'
def test_outdated_done_indef(self): """Confirm completed indefinite counts after RP switch""" # boiler plate to create baseline and indef with retired RP yesterday = now - timedelta(days=1) weekago = now - timedelta(weeks=1) org, rp2, rp2_id = self.setup_org_n_rp( org_name='testorg', rp_name='v2', retired_as_of=yesterday) org, rp3, rp3_id = self.setup_org_n_rp(org=org, rp_name='v3') org_id = org.id self.promote_user(role_name=ROLE.PATIENT.value) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(org) audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc = UserConsent( user_id=TEST_USER_ID, organization_id=org_id, audit=audit, agreement_url='http://no.com', acceptance_date=weekago) with SessionScope(db): db.session.add(audit) db.session.add(uc) db.session.commit() self.setup_qb( questionnaire_name='epic23', qb_name='baseline v2', classification='baseline', rp_id=rp2_id) self.setup_qb( questionnaire_name='epic26', qb_name='baseline v3', classification='baseline', rp_id=rp3_id) qb2_indef = self.setup_qb( questionnaire_name='irondemog', qb_name='indef v2', classification='indefinite', rp_id=rp2_id) self.setup_qb( questionnaire_name='irondemog_v3', qb_name='indef v3', classification='indefinite', rp_id=rp3_id) # for today, should get the v3 baseline user = db.session.merge(self.test_user) a_s = QB_Status(user=user, as_of_date=now) assert (['epic26', 'irondemog_v3'] == a_s.instruments_needing_full_assessment(classification='all')) # create done QNR for indefinite dated prior to rp transition # belonging to older qb - confirm that clears indef work as of then mock_qr('irondemog', timestamp=weekago, qb=qb2_indef) user = db.session.merge(self.test_user) invalidate_users_QBT(user_id=TEST_USER_ID) a_s = QB_Status(user=user, as_of_date=weekago) assert (a_s.instruments_needing_full_assessment( classification='indefinite') == []) # move forward in time; user should no longer need indefinite, even # tho RP changed qb2_indef = db.session.merge(qb2_indef) a_s = QB_Status(user=user, as_of_date=now) assert qb2_indef == a_s.current_qbd( classification='indefinite').questionnaire_bank assert (a_s.instruments_needing_full_assessment( classification='indefinite') == []) assert (a_s.instruments_needing_full_assessment( classification='all') == ['epic26'])
def test_outdated_inprogress_qb(self): # create base QB/RP org, rp, rp_id = self.setup_org_n_rp(org_name='testorg') qn = self.add_questionnaire(name='epic26') qn, org, self.test_user = map( db.session.merge, (qn, org, self.test_user)) org_id = org.id qb = QuestionnaireBank( name='Test Questionnaire Bank', classification='baseline', research_protocol_id=rp_id, start='{"days": 0}', overdue='{"days": 7}', expired='{"days": 90}') qbq = QuestionnaireBankQuestionnaire(questionnaire=qn, rank=0) qb.questionnaires.append(qbq) self.test_user.organizations.append(org) self.promote_user(role_name=ROLE.PATIENT.value) audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc = UserConsent( user_id=TEST_USER_ID, organization=org, audit=audit, agreement_url='http://no.com', acceptance_date=now) with SessionScope(db): db.session.add(qb) db.session.add(self.test_user) db.session.add(audit) db.session.add(uc) db.session.commit() qb = db.session.merge(qb) # create in-progress QNR for User/QB/RP mock_qr( instrument_id='epic26', status='in-progress', timestamp=now, qb=qb) # User associated with CRV org should generate appropriate # questionnaires self.test_user = db.session.merge(self.test_user) qb_stat = QB_Status(self.test_user, now) qb = qb_stat.current_qbd().questionnaire_bank assert qb.research_protocol.name == 'proto' # Pointing the User's org to a new QB/RP # marking the old RP as retired as of yesterday old_rp = OrganizationResearchProtocol.query.filter( OrganizationResearchProtocol.organization_id == org_id, OrganizationResearchProtocol.research_protocol_id == rp_id).one() old_rp.retired_as_of = now - timedelta(days=1) rp2 = ResearchProtocol(name='new_proto') qn2 = self.add_questionnaire(name='epic27') with SessionScope(db): db.session.add(rp2) db.session.commit() rp2 = db.session.merge(rp2) rp2_id = rp2.id qn2, org = map(db.session.merge, (qn2, org)) qb2 = QuestionnaireBank( name='Test Questionnaire Bank 2', classification='baseline', research_protocol_id=rp2_id, start='{"days": 0}', overdue='{"days": 7}', expired='{"days": 90}') qbq2 = QuestionnaireBankQuestionnaire(questionnaire=qn2, rank=0) qb2.questionnaires.append(qbq2) org.research_protocols.append(rp2) with SessionScope(db): db.session.add(qb2) db.session.add(org) db.session.commit() qb2 = db.session.merge(qb2) # outdated QB/RP should be used as long as User has in-progress QNR self.test_user = db.session.merge(self.test_user) qb_stat = QB_Status(self.test_user, now) qb = qb_stat.current_qbd().questionnaire_bank assert qb.name == 'Test Questionnaire Bank' assert qb.research_protocol.name == 'proto' # completing QNR should result in completed status # shouldn't pick up new protocol till next iteration mock_qr( instrument_id='epic26', status='completed', timestamp=now, qb=qb) self.test_user = db.session.merge(self.test_user) invalidate_users_QBT(TEST_USER_ID) qb_stat = QB_Status(self.test_user, now) qb = qb_stat.current_qbd().questionnaire_bank assert qb.name == 'Test Questionnaire Bank' assert qb_stat.overall_status == OverallStatus.completed