def test_withdrawn(self): # qbs should halt beyond withdrawal crv = self.setup_org_qbs() crv_id = crv.id # consent 17 months in past backdate = datetime.utcnow() - relativedelta(months=17) self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) self.consent_with_org(org_id=crv_id, setdate=backdate) # withdraw user now, which should provide result # in QBs prior to 17 months. user = db.session.merge(self.test_user) withdraw_consent( user=user, org_id=crv_id, acting_user=user, acceptance_date=datetime.utcnow()) gen = ordered_qbs(user=user) # expect each in order despite overlapping nature expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' for n in (3, 6, 9, 15): assert visit_name(next(gen)) == 'Month {}'.format(n) with pytest.raises(StopIteration): next(gen)
def test_withdrawn(self): # qbs should halt beyond withdrawal crv = self.setup_org_qbs() crv_id = crv.id # consent 17 months in past backdate = datetime.utcnow() - relativedelta(months=17) self.test_user.organizations.append(crv) self.consent_with_org(org_id=crv_id, setdate=backdate) # withdraw user now, which should provide result # in QBs prior to 17 months. user = db.session.merge(self.test_user) withdraw_consent(user=user, org_id=crv_id, acting_user=user, acceptance_date=datetime.utcnow()) gen = ordered_qbs(user=user) # expect each in order despite overlapping nature expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' for n in (3, 6, 9, 15): assert visit_name(next(gen)) == 'Month {}'.format(n) with pytest.raises(StopIteration): next(gen)
def test_change_before_start_rp(self): now = datetime.utcnow() back7, nowish = associative_backdate(now=now, backdate=relativedelta(months=7)) back14, nowish = associative_backdate( now=now, backdate=relativedelta(months=14)) org = self.setup_org_qbs(rp_name='v2', retired_as_of=back14) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.consent_with_org(org_id=org_id, setdate=back7) user = db.session.merge(self.test_user) gen = ordered_qbs(user) # expect everything in v3 expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' assert ( expect_baseline.questionnaire_bank.research_protocol.name == 'v3') for n in (3, 6, 9, 15, 18, 21, 30): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v3' with pytest.raises(StopIteration): next(gen)
def test_change_before_start_rp_w_result(self): now = datetime.utcnow() back7, nowish = associative_backdate( now=now, backdate=relativedelta(months=7)) back14, nowish = associative_backdate( now=now, backdate=relativedelta(months=14)) org = self.setup_org_qbs(rp_name='v2', retired_as_of=back14) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.consent_with_org(org_id=org_id, setdate=back7) # submit a mock response for baseline QB on old RP # which should result in v2 for baseline and v3 thereafter qb_name = "CRV Baseline v2" baseline = QuestionnaireBank.query.filter( QuestionnaireBank.name == qb_name).one() mock_qr('epic_26_v2', qb=baseline, iteration=None) user = db.session.merge(self.test_user) gen = ordered_qbs(user) # expect everything in v3 post baseline expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' assert ( expect_baseline.questionnaire_bank.research_protocol.name == 'v2') for n in (3, 6, 9, 15, 18, 21, 30): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v3' with pytest.raises(StopIteration): next(gen)
def test_change_before_start_rp(self): now = datetime.utcnow() back7, nowish = associative_backdate( now=now, backdate=relativedelta(months=7)) back14, nowish = associative_backdate( now=now, backdate=relativedelta(months=14)) org = self.setup_org_qbs(rp_name='v2', retired_as_of=back14) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.consent_with_org(org_id=org_id, setdate=back7) user = db.session.merge(self.test_user) gen = ordered_qbs(user) # expect everything in v3 expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' assert ( expect_baseline.questionnaire_bank.research_protocol.name == 'v3') for n in (3, 6, 9, 15, 18, 21, 30): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v3' with pytest.raises(StopIteration): next(gen)
def test_change_before_start_rp_w_result(self): now = datetime.utcnow() back7, nowish = associative_backdate(now=now, backdate=relativedelta(months=7)) back14, nowish = associative_backdate( now=now, backdate=relativedelta(months=14)) org = self.setup_org_qbs(rp_name='v2', retired_as_of=back14) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.consent_with_org(org_id=org_id, setdate=back7) # submit a mock response for baseline QB on old RP # which should result in v2 for baseline and v3 thereafter qb_name = "CRV Baseline v2" baseline = QuestionnaireBank.query.filter( QuestionnaireBank.name == qb_name).one() mock_qr('epic_26_v2', qb=baseline, iteration=None) user = db.session.merge(self.test_user) gen = ordered_qbs(user) # expect everything in v3 post baseline expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' assert ( expect_baseline.questionnaire_bank.research_protocol.name == 'v2') for n in (3, 6, 9, 15, 18, 21, 30): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v3' with pytest.raises(StopIteration): next(gen)
def test_visit_6mo(self): crv = self.setup_qbs() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=6)) self.bless_with_basics(setdate=backdate) self.test_user.organizations.append(crv) self.test_user = db.session.merge(self.test_user) qbd = QuestionnaireBank.most_current_qb(self.test_user, as_of_date=nowish + timedelta(hours=1)) self.assertEqual("Month 6", visit_name(qbd)) qbd_i2 = qbd._replace(iteration=1) self.assertEqual("Month 18", visit_name(qbd_i2))
def test_visit_6mo(self): crv = self.setup_org_qbs() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=6)) 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, as_of_date=nowish + timedelta(hours=1)) qbd = qstats.current_qbd() assert visit_name(qbd) == "Month 6" qbd.iteration = 1 assert visit_name(qbd) == "Month 18"
def test_visit_6mo(self): crv = self.setup_org_qbs() backdate, nowish = associative_backdate( now=now, backdate=relativedelta(months=6)) 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, as_of_date=nowish + timedelta(hours=1)) qbd = qstats.current_qbd() assert visit_name(qbd) == "Month 6" qbd.iteration = 1 assert visit_name(qbd) == "Month 18"
def test_full_list(self): crv = self.setup_org_qbs() self.bless_with_basics() # pick up a consent, etc. self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) gen = ordered_qbs(user=self.test_user) # expect each in order despite overlapping nature expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' for n in (3, 6, 9, 15, 18, 21, 30): assert visit_name(next(gen)) == 'Month {}'.format(n) with pytest.raises(StopIteration): next(gen)
def test_full_list(self): crv = self.setup_org_qbs() self.bless_with_basics() # pick up a consent, etc. self.test_user = db.session.merge(self.test_user) self.test_user.organizations.append(crv) gen = ordered_qbs(user=self.test_user) # expect each in order despite overlapping nature expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' for n in (3, 6, 9, 15, 18, 21, 30): assert visit_name(next(gen)) == 'Month {}'.format(n) with pytest.raises(StopIteration): next(gen)
def test_recurring_starts(self): # should get full list of QBDs for recurring qb self.setup_org_qbs() td = datetime.utcnow().replace(month=1, day=1) sixMoQB = QuestionnaireBank.query.filter( QuestionnaireBank.name == 'CRV_recurring_6mo_period v2').one() results = sixMoQB.recurring_starts(trigger_date=td) # Expect in order, 6mo, 18mo, 30mo expect6 = next(results) assert visit_name(expect6) == 'Month 6' expect18 = next(results) assert visit_name(expect18) == 'Month 18' expect30 = next(results) assert visit_name(expect30) == 'Month 30' with pytest.raises(StopIteration): next(results)
def test_recurring_starts(self): # should get full list of QBDs for recurring qb self.setup_org_qbs() td = datetime.utcnow().replace(month=1, day=1) sixMoQB = QuestionnaireBank.query.filter( QuestionnaireBank.name == 'CRV_recurring_6mo_period v2').one() results = sixMoQB.recurring_starts(trigger_date=td) # Expect in order, 6mo, 18mo, 30mo expect6 = next(results) assert visit_name(expect6) == 'Month 6' expect18 = next(results) assert visit_name(expect18) == 'Month 18' expect30 = next(results) assert visit_name(expect30) == 'Month 30' with pytest.raises(StopIteration): next(results)
def test_visit_baseline(self): crv = self.setup_qbs() self.bless_with_basics() # pick up a consent, etc. self.test_user.organizations.append(crv) self.test_user = db.session.merge(self.test_user) qbd = QuestionnaireBank.most_current_qb(self.test_user, as_of_date=now) self.assertEqual("Baseline", visit_name(qbd))
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_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_intervention_list(self): self.setup_intervention_qbs() self.bless_with_basics() # pick up a consent, etc. # user with biopsy should return biopsy date self.login() user = db.session.merge(self.test_user) 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=None) user = db.session.merge(self.test_user) gen = ordered_qbs(user=user) # expect all intervention QBs - baseline then every 3mos expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' for n in (3, 9, 15, 21, 27): assert visit_name(next(gen)) == 'Month {}'.format(n) with pytest.raises(StopIteration): next(gen)
def test_change_midstream_results_rp(self): now = datetime.utcnow() back1, nowish = associative_backdate( now=now, backdate=relativedelta(months=1)) back10, nowish = associative_backdate( now=now, backdate=relativedelta(months=10)) org = self.setup_org_qbs(rp_name='v2', retired_as_of=back1) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.consent_with_org(org_id=org_id, setdate=back10) # submit a mock response for 9 month QB on old RP # which should result in v2 for up to 9 month and v3 thereafter qb_name = "CRV_recurring_3mo_period v2" nineMo = QuestionnaireBank.query.filter( QuestionnaireBank.name == qb_name).one() mock_qr('epic_26_v2', qb=nineMo, iteration=1) user = db.session.merge(self.test_user) gen = ordered_qbs(user) # expect baseline and 3 month in v2, rest in v3 expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' assert ( expect_baseline.questionnaire_bank.research_protocol.name == 'v2') for n in (3, 6, 9): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v2' for n in (15, 18, 21, 30): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v3' with pytest.raises(StopIteration): next(gen)
def test_change_midstream_results_rp(self): now = datetime.utcnow() back1, nowish = associative_backdate(now=now, backdate=relativedelta(months=1)) back10, nowish = associative_backdate( now=now, backdate=relativedelta(months=10)) org = self.setup_org_qbs(rp_name='v2', retired_as_of=back1) org_id = org.id self.setup_org_qbs(org=org, rp_name='v3') self.consent_with_org(org_id=org_id, setdate=back10) # submit a mock response for 9 month QB on old RP # which should result in v2 for up to 9 month and v3 thereafter qb_name = "CRV_recurring_3mo_period v2" nineMo = QuestionnaireBank.query.filter( QuestionnaireBank.name == qb_name).one() mock_qr('epic_26_v2', qb=nineMo, iteration=1) user = db.session.merge(self.test_user) gen = ordered_qbs(user) # expect baseline and 3 month in v2, rest in v3 expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' assert ( expect_baseline.questionnaire_bank.research_protocol.name == 'v2') for n in (3, 6, 9): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v2' for n in (15, 18, 21, 30): qbd = next(gen) assert visit_name(qbd) == 'Month {}'.format(n) assert qbd.questionnaire_bank.research_protocol.name == 'v3' with pytest.raises(StopIteration): next(gen)
def test_intervention_list(self): self.setup_intervention_qbs() self.bless_with_basics() # pick up a consent, etc. # user with biopsy should return biopsy date self.login() user = db.session.merge(self.test_user) 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=None) user = db.session.merge(self.test_user) gen = ordered_qbs(user=user) # expect all intervention QBs - baseline then every 3mos expect_baseline = next(gen) assert visit_name(expect_baseline) == 'Baseline' for n in (3, 9, 15, 21, 27): assert visit_name(next(gen)) == 'Month {}'.format(n) with pytest.raises(StopIteration): next(gen)
def upgrade(): # Tricky task to locate the errors. One time expensive process, # loop through patients identifying problems. admin = admin_id() qb_map = qb_pairs_by_rp() if not qb_map: return # nothing to do on systems w/o RP changes fix_map = dict() raise RuntimeError( "Unsupported migration; code has changed - not maintained") for patient in active_patients(include_test_role=True).order_by(User.id): qnrs = QuestionnaireResponse.query.filter( QuestionnaireResponse.subject_id == patient.id).order_by( QuestionnaireResponse.authored) if not qnrs.count(): # Only care if the user submitted QNRs: continue for qnr in qnrs: # Obtain qb_timeline row at the time of QNR.authored as_of_date = qnr.authored qbt = QBT.query.filter(QBT.user_id == patient.id).filter( QBT.at <= as_of_date).order_by( QBT.at.desc(), QBT.id.desc()).first() if qnr.questionnaire_bank_id is None: print( "qnr {} user_id {} authored {} {}" " with NO qb_id set".format( qnr.id, patient.id, as_of_date, qnr.document['questionnaire']['reference'])) continue if qbt and qbt.status != OverallStatus.withdrawn: recorded_qb = QuestionnaireBank.query.get( qnr.questionnaire_bank_id) qbd = qbt.qbd() expected_qb = qbd.questionnaire_bank if recorded_qb.classification == 'indefinite': # indefinite case is handled differently... qb_status = QB_Status(patient, as_of_date) expected_qb = QuestionnaireBank.query.get( qb_status._current_indef.qb_id) if recorded_qb != expected_qb: replacement = qb_map.get(recorded_qb.id) if replacement == expected_qb.id: # retain the change request, as altering it inline # interrupts the iteration patched_qbd = QBD( relative_start=qbd.relative_start, iteration=qbd.iteration, recur=qbd.recur, qb_id=replacement) fix_map[qnr.id] = (expected_qb.id, visit_name(patched_qbd)) print( "qnr {} replacing incorrect qb_id {} with {} " "for user {}".format( qnr.id, recorded_qb.id, expected_qb.id, patient.id)) else: print( "qnr {} PROBLEM: recorded qb_id {}, " "expected {} doesn't match replacement {} " "for user_id {}".format( qnr.id, recorded_qb.id, expected_qb.id, replacement, patient.id)) for qnr_id, row in fix_map.items(): qb_id, visit = row qnr = QuestionnaireResponse.query.get(qnr_id) comment = ( "Migration correct qnr.id {} ({}) questionnaire_bank_id" " from {} to {}".format( qnr.id, visit, qnr.questionnaire_bank_id, qb_id)) audit = Audit( user_id=admin, subject_id=qnr.subject_id, context='assessment', comment=comment) db.session.add(audit) qnr.questionnaire_bank_id = qb_id # clear out cached qb_timeline data for user as it # needs to be recalculated given the change invalidate_users_QBT(qnr.subject_id) db.session.commit()
def upgrade(): # Tricky task to locate the errors. One time expensive process, # loop through patients identifying problems. admin = admin_id() qb_map = qb_pairs_by_rp() if not qb_map: return # nothing to do on systems w/o RP changes fix_map = dict() for patient in active_patients(include_test_role=True).order_by(User.id): qnrs = QuestionnaireResponse.query.filter( QuestionnaireResponse.subject_id == patient.id).order_by( QuestionnaireResponse.authored) if not qnrs.count(): # Only care if the user submitted QNRs: continue for qnr in qnrs: # Obtain qb_timeline row at the time of QNR.authored as_of_date = qnr.authored qbt = QBT.query.filter(QBT.user_id == patient.id).filter( QBT.at <= as_of_date).order_by( QBT.at.desc(), QBT.id.desc()).first() if qnr.questionnaire_bank_id is None: print( "qnr {} user_id {} authored {} {}" " with NO qb_id set".format( qnr.id, patient.id, as_of_date, qnr.document['questionnaire']['reference'])) continue if qbt and qbt.status != OverallStatus.withdrawn: recorded_qb = QuestionnaireBank.query.get( qnr.questionnaire_bank_id) qbd = qbt.qbd() expected_qb = qbd.questionnaire_bank if recorded_qb.classification == 'indefinite': # indefinite case is handled differently... qb_status = QB_Status(patient, as_of_date) expected_qb = QuestionnaireBank.query.get( qb_status._current_indef.qb_id) if recorded_qb != expected_qb: replacement = qb_map.get(recorded_qb.id) if replacement == expected_qb.id: # retain the change request, as altering it inline # interrupts the iteration patched_qbd = QBD( relative_start=qbd.relative_start, iteration=qbd.iteration, recur=qbd.recur, qb_id=replacement) fix_map[qnr.id] = (expected_qb.id, visit_name(patched_qbd)) print( "qnr {} replacing incorrect qb_id {} with {} " "for user {}".format( qnr.id, recorded_qb.id, expected_qb.id, patient.id)) else: print( "qnr {} PROBLEM: recorded qb_id {}, " "expected {} doesn't match replacement {} " "for user_id {}".format( qnr.id, recorded_qb.id, expected_qb.id, replacement, patient.id)) for qnr_id, row in fix_map.items(): qb_id, visit = row qnr = QuestionnaireResponse.query.get(qnr_id) comment = ( "Migration correct qnr.id {} ({}) questionnaire_bank_id" " from {} to {}".format( qnr.id, visit, qnr.questionnaire_bank_id, qb_id)) audit = Audit( user_id=admin, subject_id=qnr.subject_id, context='assessment', comment=comment) db.session.add(audit) qnr.questionnaire_bank_id = qb_id # clear out cached qb_timeline data for user as it # needs to be recalculated given the change invalidate_users_QBT(qnr.subject_id) db.session.commit()