def test_user_consent(self): self.shallow_org_tree() org1, org2 = [ org for org in Organization.query.filter(Organization.id > 0).limit(2) ] audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent(organization_id=org1.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc2 = UserConsent(organization_id=org2.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc1.staff_editable = True uc1.send_reminders = False uc2.staff_editable = True uc2.send_reminders = False uc2.status = 'suspended' with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.commit() self.test_user = db.session.merge(self.test_user) self.login() response = self.client.get('/api/user/{}/consent'.format(TEST_USER_ID)) assert response.status_code == 200 assert len(response.json['consent_agreements']) == 2 assert 'send_reminders' not in response.json['consent_agreements'][0] assert 'staff_editable' in response.json['consent_agreements'][0] assert response.json['consent_agreements'][0]['status'] == 'consented' assert response.json['consent_agreements'][1]['status'] == 'suspended'
def test_user_consent(self): self.shallow_org_tree() org1, org2 = [ org for org in Organization.query.filter(Organization.id > 0).limit(2) ] audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent(organization_id=org1.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc2 = UserConsent(organization_id=org2.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc1.staff_editable = True uc1.send_reminders = False uc2.staff_editable = True uc2.send_reminders = False with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.commit() self.test_user = db.session.merge(self.test_user) self.login() rv = self.client.get('/api/user/{}/consent'.format(TEST_USER_ID)) self.assert200(rv) self.assertEquals(len(rv.json['consent_agreements']), 2) self.assertTrue( 'send_reminders' not in rv.json['consent_agreements'][0]) self.assertTrue('staff_editable' in rv.json['consent_agreements'][0])
def test_staff_access(self): staff = self.add_user('*****@*****.**') self.promote_user(role_name=ROLE.PATIENT) self.promote_user(staff, role_name=ROLE.STAFF) self.shallow_org_tree() org = Organization.query.filter(Organization.id > 0).first() staff.organizations.append(org) self.test_user.organizations.append(org) audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID, comment='just test data') consent = UserConsent(user_id=TEST_USER_ID, organization_id=org.id, audit=audit, agreement_url='http://fake.org') with SessionScope(db): db.session.add(audit) db.session.add(consent) db.session.commit() staff = db.session.merge(staff) self.login(staff.id) rv = self.client.get('/api/user/{}/audit'.format(TEST_USER_ID)) self.assert200(rv) self.assertEquals(1, len(rv.json['audits'])) self.assertEquals( rv.json['audits'][0]['by']['reference'], Reference.patient(TEST_USER_ID).as_fhir()['reference']) self.assertEquals(rv.json['audits'][0]['on'], Reference.patient(TEST_USER_ID).as_fhir()) self.assertEquals(rv.json['audits'][0]['context'], 'other') self.assertEquals(rv.json['audits'][0]['comment'], 'just test data')
def test_content_options(self): audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) self.shallow_org_tree() org1, _ = [org for org in Organization.query.filter( Organization.id > 0).limit(2)] uc = UserConsent( user_id=TEST_USER_ID, organization=org1, audit=audit, agreement_url='http://no.com') uc.include_in_reports = True with SessionScope(db): db.session.add(uc) db.session.commit() uc = UserConsent.query.first() assert uc.include_in_reports assert not uc.staff_editable assert not uc.send_reminders
def test_user_consent(self): self.shallow_org_tree() org1, org2 = [org for org in Organization.query.filter( Organization.id > 0).limit(2)] audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent( organization_id=org1.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc2 = UserConsent( organization_id=org2.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc1.staff_editable = True uc1.send_reminders = False uc2.staff_editable = True uc2.send_reminders = False uc2.status = 'suspended' with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.commit() self.test_user = db.session.merge(self.test_user) self.login() response = self.client.get('/api/user/{}/consent'.format(TEST_USER_ID)) assert response.status_code == 200 assert len(response.json['consent_agreements']) == 2 assert 'send_reminders' not in response.json['consent_agreements'][0] assert 'staff_editable' in response.json['consent_agreements'][0] org1, org2 = db.session.merge(org1), db.session.merge(org2) org1_consent = [ca for ca in response.json[ 'consent_agreements'] if ca['organization_id'] == org1.id][0] org2_consent = [ca for ca in response.json[ 'consent_agreements'] if ca['organization_id'] == org2.id][0] assert org1_consent['status'] == 'consented' assert org2_consent['status'] == 'suspended'
def test_submit_assessment_for_qb(self): swagger_spec = swagger(self.app) data = swagger_spec['definitions']['QuestionnaireResponse']['example'] rp = ResearchProtocol(name='proto') with SessionScope(db): db.session.add(rp) db.session.commit() rp = db.session.merge(rp) rp_id = rp.id qn = self.add_questionnaire(name='epic26') org = Organization(name="testorg") org.research_protocols.append(rp) with SessionScope(db): db.session.add(qn) db.session.add(org) db.session.commit() qn, org = map(db.session.merge, (qn, org)) 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) test_user = get_user(TEST_USER_ID) 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=org, audit=audit, agreement_url='http://no.com') with SessionScope(db): db.session.add(qb) db.session.add(test_user) db.session.add(audit) db.session.add(uc) db.session.commit() qb = db.session.merge(qb) self.login() response = self.client.post( '/api/patient/{}/assessment'.format(TEST_USER_ID), content_type='application/json', data=json.dumps(data), ) assert response.status_code == 200 test_user = get_user(TEST_USER_ID) assert test_user.questionnaire_responses.count() == 1 assert test_user.questionnaire_responses[0].questionnaire_bank_id ==\ qb.id
def test_delete_user_consent(self): self.shallow_org_tree() org1, org2 = [ org for org in Organization.query.filter(Organization.id > 0).limit(2) ] org1_id, org2_id = org1.id, org2.id data = {'organization_id': org1_id} audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent(organization_id=org1_id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc2 = UserConsent(organization_id=org2_id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.commit() self.test_user = db.session.merge(self.test_user) assert self.test_user.valid_consents.count() == 2 self.login() response = self.client.delete( '/api/user/{}/consent'.format(TEST_USER_ID), content_type='application/json', data=json.dumps(data)) assert response.status_code == 200 assert self.test_user.valid_consents.count() == 1 assert self.test_user.valid_consents[0].organization_id == org2_id # We no longer omit deleted consent rows, but rather, include # their audit data. response = self.client.get('/api/user/{}/consent'.format(TEST_USER_ID)) assert 'deleted' in json.dumps(response.json) # confirm deleted status dc = UserConsent.query.filter_by(user_id=TEST_USER_ID, organization_id=org1_id).first() assert dc.status == 'deleted'
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_qbs(rp_name='v2', retired_as_of=weekago) org_id = org.id self.setup_qbs(org=org, rp_name='v3') 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() # Two weeks ago, still on rp v2, should be in 3mo recurrence user = db.session.merge(self.test_user) a_s = AssessmentStatus(user=user, as_of_date=twoweeksago) v2qb = a_s.qb_data.qbd.questionnaire_bank self.assertEqual('CRV_recurring_3mo_period v2', a_s.qb_data.qbd.questionnaire_bank.name) self.assertEqual('CRV_recurring_3mo_period v2', a_s.qb_name) self.assertEqual(['epic26_v2'], a_s.instruments_needing_full_assessment()) # Now, should still be rp v3, 3mo recurrence a_s = AssessmentStatus(user=user, as_of_date=now) self.assertEqual('CRV_recurring_3mo_period v3', a_s.qb_data.qbd.questionnaire_bank.name) self.assertEqual(['epic26_v3'], a_s.instruments_needing_full_assessment()) # Complete the questionnaire from the 3mo v2 QB mock_qr('epic26_v2', timestamp=twoweeksago, qb=v2qb, iteration=0) # Two weeks ago, should be completed user = db.session.merge(user) a_s = AssessmentStatus(user=user, as_of_date=twoweeksago) self.assertEqual('Completed', a_s.overall_status) # Current should also be completed, even tho protocol changed a_s = AssessmentStatus(user=user, as_of_date=now) self.assertEqual('Completed', a_s.overall_status) # Should see both as candidates qbds = QuestionnaireBank.qbs_for_user(user, classification='recurring', as_of_date=now) self.assertEqual(len(qbds), 2)
def test_content_options(self): audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) self.shallow_org_tree() org1, _ = [ org for org in Organization.query.filter(Organization.id > 0).limit(2) ] uc = UserConsent(user_id=TEST_USER_ID, organization=org1, audit=audit, agreement_url='http://no.com') uc.include_in_reports = True with SessionScope(db): db.session.add(uc) db.session.commit() uc = UserConsent.query.first() self.assertTrue(uc.include_in_reports) self.assertFalse(uc.staff_editable) self.assertFalse(uc.send_reminders)
def test_qb_pre_retired(self): # Confirm backdating returns the correct QB weekago = now - timedelta(days=7) org, rpv3, rpv3_id = self.setup_org_n_rp(rp_name='v3') org, rpv2, rpv2_id = self.setup_org_n_rp(org=org, rp_name='v2', retired_as_of=weekago) qn3 = self.add_questionnaire(name='epic26-v3') qn2 = self.add_questionnaire(name='epic26-v2') qb3 = QuestionnaireBank(name='Test Questionnaire Bank v3', classification='baseline', research_protocol_id=rpv3_id, start='{"days": 0}', overdue='{"days": 7}', expired='{"days": 90}') qbq3 = QuestionnaireBankQuestionnaire(questionnaire=qn3, rank=0) qb3.questionnaires.append(qbq3) qb2 = QuestionnaireBank(name='Test Questionnaire Bank v2', classification='baseline', research_protocol_id=rpv2_id, start='{"days": 0}', overdue='{"days": 7}', expired='{"days": 90}') qbq2 = QuestionnaireBankQuestionnaire(questionnaire=qn2, rank=0) qb2.questionnaires.append(qbq2) 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=org, audit=audit, agreement_url='http://no.com') with SessionScope(db): map(db.session.add, (qb2, qb3)) db.session.add(audit) db.session.add(uc) db.session.commit() user, org, qb2, qb3 = map(db.session.merge, (self.test_user, org, qb2, qb3)) # w/o backdating, should get current QB self.assertEqual( qb3, QuestionnaireBank.most_current_qb( user, as_of_date=now).questionnaire_bank) # backdate prior to retirement of previous, should get the previous QB pre_retirement = weekago - timedelta(days=1) self.assertEqual( qb2, QuestionnaireBank.most_current_qb( user, as_of_date=pre_retirement).questionnaire_bank)
def test_consent_order(self): self.shallow_org_tree() org1, org2 = [org for org in Organization.query.filter( Organization.id > 0).limit(2)] old = datetime.now() - relativedelta(years=10) older = old - relativedelta(years=10) oldest = older - relativedelta(years=50) audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent( organization_id=org1.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit, acceptance_date=older) uc2 = UserConsent( organization_id=org2.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit, acceptance_date=oldest) uc3 = UserConsent( organization_id=0, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit, acceptance_date=old) with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.add(uc3) db.session.commit() self.test_user = db.session.merge(self.test_user) self.login() response = self.client.get( '/api/user/{}/consent'.format(TEST_USER_ID)) assert response.status_code == 200 assert len(response.json['consent_agreements']) == 3 # should be ordered by acceptance date, descending: (uc3, uc1, uc2) uc1, uc2, uc3 = map(db.session.merge, (uc1, uc2, uc3)) assert response.json['consent_agreements'][0] == uc3.as_json() assert response.json['consent_agreements'][1] == uc1.as_json() assert response.json['consent_agreements'][2] == uc2.as_json()
def test_delete_user_consent(self): self.shallow_org_tree() org1, org2 = [ org for org in Organization.query.filter(Organization.id > 0).limit(2) ] org1_id, org2_id = org1.id, org2.id data = {'organization_id': org1_id} audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent(organization_id=org1_id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) uc2 = UserConsent(organization_id=org2_id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.commit() self.test_user = db.session.merge(self.test_user) self.assertEqual(self.test_user.valid_consents.count(), 2) self.login() rv = self.client.delete('/api/user/{}/consent'.format(TEST_USER_ID), content_type='application/json', data=json.dumps(data)) self.assert200(rv) self.assertEqual(self.test_user.valid_consents.count(), 1) self.assertEqual(self.test_user.valid_consents[0].organization_id, org2_id) # We no longer omit deleted consent rows, but rather, include # their audit data. rv = self.client.get('/api/user/{}/consent'.format(TEST_USER_ID)) self.assertTrue('deleted' in json.dumps(rv.json))
def test_consent_order(self): self.shallow_org_tree() org1, org2 = [ org for org in Organization.query.filter(Organization.id > 0).limit(2) ] old = datetime.now() - relativedelta(years=10) older = old - relativedelta(years=10) oldest = older - relativedelta(years=50) audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc1 = UserConsent(organization_id=org1.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit, acceptance_date=older) uc2 = UserConsent(organization_id=org2.id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit, acceptance_date=oldest) uc3 = UserConsent(organization_id=0, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit, acceptance_date=old) with SessionScope(db): db.session.add(uc1) db.session.add(uc2) db.session.add(uc3) db.session.commit() self.test_user = db.session.merge(self.test_user) self.login() response = self.client.get('/api/user/{}/consent'.format(TEST_USER_ID)) assert response.status_code == 200 assert len(response.json['consent_agreements']) == 3 # should be ordered by acceptance date, descending: (uc3, uc1, uc2) uc1, uc2, uc3 = map(db.session.merge, (uc1, uc2, uc3)) assert response.json['consent_agreements'][0] == uc3.as_json() assert response.json['consent_agreements'][1] == uc1.as_json() assert response.json['consent_agreements'][2] == uc2.as_json()
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 bless_with_basics(self, backdate=None, setdate=None): """Bless test user with basic requirements for coredata :param backdate: timedelta value. Define to mock consents happening said period in the past. See ``associative_backdate`` for issues with 'months'. :param setdate: datetime value. Define to mock consents happening at exact time in the past """ self.test_user = db.session.merge(self.test_user) self.test_user.birthdate = datetime.utcnow() # Register with a clinic self.shallow_org_tree() org = Organization.query.filter( Organization.partOf_id != None).first() assert org self.test_user.organizations.append(org) # Agree to Terms of Use and sign consent audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) tou = ToU(audit=audit, agreement_url='http://not.really.org', type='website terms of use') privacy = ToU(audit=audit, agreement_url='http://not.really.org', type='privacy policy') parent_org = OrgTree().find(org.id).top_level() options = (STAFF_EDITABLE_MASK | INCLUDE_IN_REPORTS_MASK | SEND_REMINDERS_MASK) consent = UserConsent( user_id=TEST_USER_ID, organization_id=parent_org, options=options, audit=audit, agreement_url='http://fake.org', acceptance_date=calc_date_params( backdate=backdate, setdate=setdate)) with SessionScope(db): db.session.add(tou) db.session.add(privacy) db.session.add(consent) db.session.commit() # Invalidate org tree cache, in case orgs are added by other # tests. W/o doing so, the new orgs aren't in the orgtree OrgTree.invalidate_cache()
def consent_with_org(self, org_id, user_id=TEST_USER_ID, backdate=None, setdate=None): """Bless given user with a valid consent with org :param backdate: timedelta value. Define to mock consents happening said period in the past :param setdate: datetime value. Define to mock consents happening at exact time in the past """ audit = Audit(user_id=user_id, subject_id=user_id) consent = UserConsent( user_id=user_id, organization_id=org_id, audit=audit, agreement_url='http://fake.org', acceptance_date=calc_date_params( backdate=backdate, setdate=setdate)) with SessionScope(db): db.session.add(consent) db.session.commit()
def test_user_check_roles(self): org = Organization(name='members only') user = self.test_user user.organizations.append(org) u2 = self.add_user(username='******') member_of = self.add_user(username='******') member_of.organizations.append(org) audit = Audit(comment='test data', user_id=TEST_USER_ID, subject_id=TEST_USER_ID) self.promote_user(user, ROLE.STAFF) self.promote_user(u2, ROLE.PATIENT) self.promote_user(member_of, ROLE.PATIENT) user, org, u2, member_of = map( db.session.merge, (user, org, u2, member_of)) consent = UserConsent( user_id=member_of.id, organization_id=org.id, audit=audit, agreement_url='http://example.org', options=STAFF_EDITABLE_MASK) with SessionScope(db): db.session.add(consent) db.session.commit() user, org, u2, member_of = map( db.session.merge, (user, org, u2, member_of)) kwargs = {'permission': 'view', 'other_id': user.id} self.assertTrue(user.check_role(**kwargs)) kwargs = {'permission': 'edit', 'other_id': u2.id} self.assertRaises(Unauthorized, user.check_role, **kwargs) kwargs = {'permission': 'view', 'other_id': u2.id} self.assertRaises(Unauthorized, user.check_role, **kwargs) kwargs = {'permission': 'edit', 'other_id': member_of.id} self.assertTrue(user.check_role(**kwargs)) kwargs = {'permission': 'view', 'other_id': member_of.id} self.assertTrue(user.check_role(**kwargs))
def test_withdraw_user_consent(self): self.shallow_org_tree() org = Organization.query.filter(Organization.id > 0).first() org_id = org.id audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) uc = UserConsent(organization_id=org_id, user_id=TEST_USER_ID, agreement_url=self.url, audit=audit) with SessionScope(db): db.session.add(uc) db.session.commit() self.test_user = db.session.merge(self.test_user) assert self.test_user.valid_consents.count() == 1 data = {'organization_id': org_id} self.login() resp = self.client.post('/api/user/{}/consent/' 'withdraw'.format(TEST_USER_ID), content_type='application/json', data=json.dumps(data)) assert resp.status_code == 200 # check that old consent is marked as deleted old_consent = UserConsent.query.filter_by(user_id=TEST_USER_ID, organization_id=org_id, status='deleted').first() assert old_consent.deleted_id # check new withdrawn consent new_consent = UserConsent.query.filter_by(user_id=TEST_USER_ID, organization_id=org_id, status='suspended').first() assert old_consent.agreement_url == new_consent.agreement_url assert new_consent.staff_editable == \ (not current_app.config.get('GIL')) assert not new_consent.send_reminders
def consent_with_org(self, org_id, user_id=TEST_USER_ID, backdate=None, setdate=None): """Bless given user with a valid consent with org NB - if existing consent for user/org is present, simply update with new date values :param backdate: timedelta value. Define to mock consents happening said period in the past :param setdate: datetime value. Define to mock consents happening at exact time in the past """ # doesn't make sense to consent w/o an association, add if missing user = User.query.get(user_id) if org_id not in (o.id for o in user.organizations): user.organizations.append(Organization.query.get(org_id)) acceptance_date = calc_date_params( backdate=backdate, setdate=setdate) consent = UserConsent.query.filter( UserConsent.user_id == user_id).filter( UserConsent.organization_id == org_id).first() if consent: consent.acceptance_date = acceptance_date else: audit = Audit(user_id=user_id, subject_id=user_id) consent = UserConsent( user_id=user_id, organization_id=org_id, audit=audit, agreement_url='http://fake.org', acceptance_date=acceptance_date) with SessionScope(db): if consent not in db.session: db.session.add(consent) db.session.commit()
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 patch_deceased_user_consents(uid): user = User.query.get(uid) user_consents = UserConsent.query.filter( UserConsent.user_id == uid).order_by(UserConsent.id).all() # if no user_consents exist for user, we're done if not user_consents: return # acquire the deceased date dd = user.deceased.timestamp # if the most recent consent looks correct, assume we're done if user_consents[-1].status == 'suspended' and not ( user_consents[-1].options & SEND_REMINDERS_MASK): return admin = User.query.filter_by(email='__system__').first() log.info("Patching broken consents for {}, with deceased date {}".format( user, dd)) keeper = user_consents.pop(0) log.info(" keeping original user_consent {} {} {} {}".format( keeper.id, keeper.status, keeper.options, keeper.acceptance_date)) obsolete_audits, obsolete_consents = [], [] for uc in user_consents: log.info(" deleting user_consent {} {} {} {}".format( uc.id, uc.status, uc.options, uc.acceptance_date)) a = Audit.query.get(uc.audit_id) log.info(" and the related {}".format(a)) obsolete_audits.append(a.id) obsolete_consents.append(uc.id) # out of loop, delete the unwanted db.session.query(UserConsent).filter( UserConsent.id.in_(obsolete_consents)).delete( synchronize_session=False) db.session.query(Audit).filter( Audit.id.in_(obsolete_audits)).delete(synchronize_session=False) db.session.commit() # now add back in the corrected consent, from the initial deceased_options = STAFF_EDITABLE_MASK | INCLUDE_IN_REPORTS_MASK deceased_consent = UserConsent(user_id=uid, organization_id=keeper.organization_id, agreement_url=keeper.agreement_url, acceptance_date=dd, options=deceased_options, status='suspended') user.update_consents(consent_list=[deceased_consent], acting_user=admin) # The updated consent may have altered the cached assessment # status - invalidate this user's data at this time. invalidate_users_QBT(user_id=user.id) db.session.commit() log.info(" Done, results:") for uc in UserConsent.query.filter(UserConsent.user_id == uid): log.info(" {} {} {} {}".format(uc, uc.acceptance_date, uc.status, uc.options))
def test_deep_tree_check_role(self): self.deepen_org_tree() org_102 = Organization.query.get(102) org_1002 = Organization.query.get(1002) org_10031 = Organization.query.get(10031) org_10032 = Organization.query.get(10032) audit = Audit(comment="testing", user_id=TEST_USER_ID, subject_id=TEST_USER_ID, context='consent') staff_top = self.add_user('Staff 102') self.promote_user(staff_top, ROLE.STAFF) staff_top.organizations.append(org_102) staff_leaf = self.add_user('Staff 10031') self.promote_user(staff_leaf, ROLE.STAFF) staff_leaf.organizations.append(org_10031) staff_mid = self.add_user('Staff 1002') self.promote_user(staff_mid, ROLE.STAFF) staff_mid.organizations.append(org_1002) patient_w = self.add_user('patient w') patient_w_id = patient_w.id self.promote_user(patient_w, ROLE.PATIENT) patient_w.organizations.append(org_10032) uc_w = UserConsent( audit=audit, agreement_url='http://fake.org', user_id=patient_w_id, organization=org_10032, options=STAFF_EDITABLE_MASK) patient_x = self.add_user('patient x') patient_x_id = patient_x.id self.promote_user(patient_x, ROLE.PATIENT) patient_x.organizations.append(org_10032) uc_x = UserConsent( audit=audit, agreement_url='http://fake.org', user_id=patient_x_id, organization=org_102, options=STAFF_EDITABLE_MASK) patient_y = self.add_user('patient y') patient_y_id = patient_y.id self.promote_user(patient_y, ROLE.PATIENT) patient_y.organizations.append(org_102) uc_y = UserConsent( audit=audit, agreement_url='http://fake.org', user_id=patient_y_id, organization=org_10031, options=STAFF_EDITABLE_MASK) patient_z = self.add_user('patient z') patient_z_id = patient_z.id self.promote_user(patient_z, ROLE.PATIENT) patient_z.organizations.append(org_10031) uc_z = UserConsent( audit=audit, agreement_url='http://fake.org', user_id=patient_z_id, organization=org_10031, options=STAFF_EDITABLE_MASK) with SessionScope(db): db.session.add(uc_w) db.session.add(uc_x) db.session.add(uc_y) db.session.add(uc_z) db.session.commit() patient_w, patient_x, patient_y, patient_z = map(db.session.merge,( patient_w, patient_x, patient_y, patient_z)) ### # Setup complete - test access ### # top level staff can view/edit all staff_top = db.session.merge(staff_top) for perm in ('view', 'edit'): for patient in ( patient_w_id, patient_x_id, patient_y_id, patient_z_id): self.assertTrue( staff_top.check_role(perm, other_id=patient)) # mid level staff can view/edit all staff_mid = db.session.merge(staff_mid) for perm in ('view', 'edit'): for patient in ( patient_w_id, patient_x_id, patient_y_id, patient_z_id): self.assertTrue( staff_mid.check_role(perm, other_id=patient)) # low level staff can view/edit only those w/ same org staff_leaf = db.session.merge(staff_leaf) for perm in ('view', 'edit'): for patient in (patient_w_id, patient_x_id): self.assertRaises( Unauthorized, staff_leaf.check_role, perm, patient) for perm in ('view', 'edit'): for patient in (patient_y_id, patient_z_id): self.assertTrue( staff_leaf.check_role(perm, other_id=patient)) # Now remove the staff editable flag from the consents, which should # preserve view but remove edit permission for uc in (uc_w, uc_x, uc_y, uc_z): uc = db.session.merge(uc) uc.staff_editable = False with SessionScope(db): db.session.commit() patient_w, patient_x, patient_y, patient_z = map(db.session.merge,( patient_w, patient_x, patient_y, patient_z)) # top level staff can view all, edit none staff_top = db.session.merge(staff_top) for patient in ( patient_w_id, patient_x_id, patient_y_id, patient_z_id): self.assertTrue( staff_top.check_role('view', other_id=patient)) self.assertRaises( Unauthorized, staff_top.check_role, 'edit', other_id=patient) # mid level staff can view all, edit none staff_mid = db.session.merge(staff_mid) for patient in ( patient_w_id, patient_x_id, patient_y_id, patient_z_id): self.assertTrue( staff_mid.check_role('view', other_id=patient)) self.assertRaises( Unauthorized, staff_mid.check_role, 'edit', other_id=patient) # low level staff can view only those w/ same org; edit none staff_leaf = db.session.merge(staff_leaf) for perm in ('view', 'edit'): for patient in (patient_w_id, patient_x_id): self.assertRaises( Unauthorized, staff_leaf.check_role, perm, patient) for patient in (patient_y_id, patient_z_id): self.assertTrue( staff_leaf.check_role('view', other_id=patient)) self.assertRaises( Unauthorized, staff_leaf.check_role, 'edit', patient)
def test_permanently_delete_user(self): # created acting user and to-be-deleted user actor = self.add_user('actor') deleted = self.add_user('deleted') deleted, actor = map(db.session.merge, (deleted, actor)) deleted_id, actor_id = deleted.id, actor.id deleted_email, actor_email = deleted.email, actor.email self.promote_user(user=actor, role_name=ROLE.ADMIN) with SessionScope(db): db.session.commit() deleted = db.session.merge(deleted) # create observation and user_observation audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) observation = Observation() coding = Coding(system='SNOMED-CT', code='372278000', display='Gleason score') cc = CodeableConcept(codings=[coding, ]) observation.codeable_concept = cc observation.value_quantity = ValueQuantity(value=2) performer = Performer(reference_txt=json.dumps( Reference.patient(TEST_USER_ID).as_fhir())) observation.performers.append(performer) enc = Encounter(status='planned', auth_method='url_authenticated', user_id=TEST_USER_ID, start_time=datetime.utcnow()) with SessionScope(db): db.session.add(observation) db.session.add(enc) db.session.commit() observation, enc = map(db.session.merge, (observation, enc)) observation_id, enc_id = observation.id, enc.id user_obs = UserObservation(user_id=deleted_id, audit=audit, observation_id=observation_id, encounter_id=enc_id) with SessionScope(db): db.session.add(user_obs) db.session.commit() user_obs = db.session.merge(user_obs) user_obs_id = user_obs.id # create user and subject audits subj_audit = Audit(user_id=TEST_USER_ID, subject_id=deleted_id) user_audit = Audit(user_id=deleted_id, subject_id=TEST_USER_ID) with SessionScope(db): db.session.add(subj_audit) db.session.add(user_audit) db.session.commit() subj_audit, user_audit = map(db.session.merge, (subj_audit, user_audit)) subj_audit_id, user_audit_id = subj_audit.id, user_audit.id # create user_consent and audit consent_org = Organization(name='test org') consent_audit = Audit(user_id=TEST_USER_ID, subject_id=TEST_USER_ID) with SessionScope(db): db.session.add(consent_org) db.session.add(consent_audit) db.session.commit() db.session.add(UserConsent( user_id=deleted_id, organization_id=consent_org.id, audit=consent_audit, agreement_url='http://example.org', options=STAFF_EDITABLE_MASK)) db.session.commit() consent_org, consent_audit = map(db.session.merge, (consent_org, consent_audit)) co_id, ca_id = consent_org.id, consent_audit.id # permanently deleted user, check deletion cascades permanently_delete_user(deleted_email, actor=actor_email) self.assertFalse(User.query.get(deleted_id)) self.assertFalse(UserObservation.query.get(user_obs_id)) self.assertTrue(Observation.query.get(observation_id)) self.assertFalse(Audit.query.get(subj_audit_id)) self.assertFalse(Audit.query.get(user_audit_id)) self.assertFalse(UserConsent.query.get(co_id)) self.assertFalse(Audit.query.get(ca_id))
def bless_with_basics( self, user=None, backdate=None, setdate=None, local_metastatic=None, make_patient=True): """Bless user with basic requirements for coredata :param user: user to bless, self.test_user by default :param backdate: timedelta value. Define to mock consents happening said period in the past. See ``associative_backdate`` for issues with 'months'. :param setdate: datetime value. Define to mock consents happening at exact time in the past :param local_metastatic: set to 'localized' or 'metastatic' for tests needing those respective orgs assigned to the user :param make_patient: add patient role unless set False """ if not user: user = db.session.merge(self.test_user) else: user = db.session.merge(user) user_id = user.id user.birthdate = datetime.utcnow() if make_patient: self.promote_user(user=user, role_name=ROLE.PATIENT.value) # Register with a clinic self.shallow_org_tree() if local_metastatic: org = Organization.query.filter( Organization.name == local_metastatic).one() else: org = Organization.query.filter( Organization.partOf_id.isnot(None)).first() assert org user = db.session.merge(user) user.organizations.append(org) # Agree to Terms of Use and sign consent audit = Audit(user_id=user_id, subject_id=user_id) tou = ToU( audit=audit, agreement_url='http://not.really.org', type='website terms of use') privacy = ToU( audit=audit, agreement_url='http://not.really.org', type='privacy policy') parent_org = OrgTree().find(org.id).top_level() options = (STAFF_EDITABLE_MASK | INCLUDE_IN_REPORTS_MASK | SEND_REMINDERS_MASK) consent = UserConsent( user_id=user_id, organization_id=parent_org, options=options, audit=audit, agreement_url='http://fake.org', acceptance_date=calc_date_params( backdate=backdate, setdate=setdate)) with SessionScope(db): db.session.add(tou) db.session.add(privacy) db.session.add(consent) db.session.commit() # Invalidate org tree cache, in case orgs are added by other # tests. W/o doing so, the new orgs aren't in the orgtree OrgTree.invalidate_cache()
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