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
Esempio n. 3
0
    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
Esempio n. 4
0
    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')
Esempio n. 6
0
    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_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)
Esempio n. 8
0
    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)]
Esempio n. 9
0
    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_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"
Esempio n. 12
0
    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"
Esempio n. 13
0
 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")
Esempio n. 14
0
    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_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')
Esempio n. 17
0
    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_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()
Esempio n. 19
0
    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_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')
Esempio n. 22
0
    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')
Esempio n. 23
0
    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 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 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 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
Esempio n. 28
0
    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_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
Esempio n. 30
0
 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
Esempio n. 31
0
 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
Esempio n. 32
0
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
Esempio n. 36
0
    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
Esempio n. 37
0
    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()
Esempio n. 39
0
    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
Esempio n. 41
0
    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
Esempio n. 42
0
 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
Esempio n. 43
0
    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_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()
Esempio n. 45
0
    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
Esempio n. 47
0
    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_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
Esempio n. 49
0
    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
Esempio n. 50
0
    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
Esempio n. 51
0
    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
Esempio n. 52
0
    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
Esempio n. 53
0
    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')
Esempio n. 54
0
    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