class BiobankOrderDaoTest(SqlTestBase): _A_TEST = BIOBANK_TESTS[0] _B_TEST = BIOBANK_TESTS[1] TIME_1 = datetime.datetime(2018, 9, 20, 5, 49, 11) TIME_2 = datetime.datetime(2018, 9, 21, 8, 49, 37) def setUp(self): super(BiobankOrderDaoTest, self).setUp(use_mysql=True) self.participant = Participant(participantId=123, biobankId=555) ParticipantDao().insert(self.participant) self.dao = BiobankOrderDao() def _make_biobank_order(self, **kwargs): """Makes a new BiobankOrder (same values every time) with valid/complete defaults. Kwargs pass through to BiobankOrder constructor, overriding defaults. """ for k, default_value in ( ('biobankOrderId', '1'), ('created', clock.CLOCK.now()), ('participantId', self.participant.participantId), ('sourceSiteId', 1), ('sourceUsername', '*****@*****.**'), ('collectedSiteId', 1), ('collectedUsername', '*****@*****.**'), ('processedSiteId', 1), ('processedUsername', '*****@*****.**'), ('finalizedSiteId', 2), ('finalizedUsername', '*****@*****.**'), ('identifiers', [BiobankOrderIdentifier(system='a', value='c')]), ('samples', [BiobankOrderedSample( biobankOrderId='1', test=self._A_TEST, finalized=self.TIME_2, description='description', processingRequired=True)])): if k not in kwargs: kwargs[k] = default_value return BiobankOrder(**kwargs) @staticmethod def _get_cancel_patch(): return { "amendedReason": 'I messed something up :( ', "cancelledInfo": { "author": { "system": "https://www.pmi-ops.org/healthpro-username", "value": "*****@*****.**" }, "site": { "system": "https://www.pmi-ops.org/site-id", "value": "hpo-site-monroeville" } }, "status": "cancelled" } @staticmethod def _get_restore_patch(): return { "amendedReason": 'I didn"t mess something up :( ', "restoredInfo": { "author": { "system": "https://www.pmi-ops.org/healthpro-username", "value": "*****@*****.**" }, "site": { "system": "https://www.pmi-ops.org/site-id", "value": "hpo-site-monroeville" } }, "status": "restored" } @staticmethod def _get_amended_info(order): amendment = dict(amendedReason='I had to change something', amendedInfo={ "author": { "system": "https://www.pmi-ops.org/healthpro-username", "value": "*****@*****.**" }, "site": { "system": "https://www.pmi-ops.org/site-id", "value": "hpo-site-monroeville" } }) order.amendedReason = amendment['amendedReason'] order.amendedInfo = amendment['amendedInfo'] return order def test_bad_participant(self): with self.assertRaises(BadRequest): self.dao.insert(self._make_biobank_order(participantId=999)) def test_from_json(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_json = load_biobank_order_json(self.participant.participantId) order = BiobankOrderDao().from_client_json(order_json, participant_id=self.participant.participantId) self.assertEquals(1, order.sourceSiteId) self.assertEquals('*****@*****.**', order.sourceUsername) self.assertEquals(1, order.collectedSiteId) self.assertEquals('*****@*****.**', order.collectedUsername) self.assertEquals(1, order.processedSiteId) self.assertEquals('*****@*****.**', order.processedUsername) self.assertEquals(2, order.finalizedSiteId) self.assertEquals('*****@*****.**', order.finalizedUsername) # testing finalized_time samples_finalized_time = None for sample in order_json['samples']: samples_finalized_time = parse_date(sample['finalized']) break self.assertEquals(samples_finalized_time, order.finalizedTime) def test_to_json(self): order = self._make_biobank_order() order_json = self.dao.to_client_json(order) expected_order_json = load_biobank_order_json(self.participant.participantId) for key in ('createdInfo', 'collectedInfo', 'processedInfo', 'finalizedInfo'): self.assertEquals(expected_order_json[key], order_json.get(key)) def test_duplicate_insert_ok(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order(created=self.TIME_1)) order_2 = self.dao.insert(self._make_biobank_order(created=self.TIME_1)) self.assertEquals(order_1.asdict(), order_2.asdict()) def test_same_id_different_identifier_not_ok(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) self.dao.insert(self._make_biobank_order( identifiers=[BiobankOrderIdentifier(system='a', value='b')])) with self.assertRaises(Conflict): self.dao.insert(self._make_biobank_order( identifiers=[BiobankOrderIdentifier(system='a', value='c')])) def test_reject_used_identifier(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) self.dao.insert(self._make_biobank_order( biobankOrderId='1', identifiers=[BiobankOrderIdentifier(system='a', value='b')])) with self.assertRaises(BadRequest): self.dao.insert(self._make_biobank_order( biobankOrderId='2', identifiers=[BiobankOrderIdentifier(system='a', value='b')])) def test_order_for_withdrawn_participant_fails(self): self.participant.withdrawalStatus = WithdrawalStatus.NO_USE ParticipantDao().update(self.participant) ParticipantSummaryDao().insert(self.participant_summary(self.participant)) with self.assertRaises(Forbidden): self.dao.insert(self._make_biobank_order(participantId=self.participant.participantId)) def test_get_for_withdrawn_participant_fails(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) self.dao.insert(self._make_biobank_order( biobankOrderId='1', participantId=self.participant.participantId)) self.participant.version += 1 self.participant.withdrawalStatus = WithdrawalStatus.NO_USE ParticipantDao().update(self.participant) with self.assertRaises(Forbidden): self.dao.get(1) def test_store_invalid_test(self): with self.assertRaises(BadRequest): self.dao.insert(self._make_biobank_order( samples=[BiobankOrderedSample( test='InvalidTestName', processingRequired=True, description=u'tested it')])) def test_cancelling_an_order(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) cancelled_request = self._get_cancel_patch() self.assertEqual(order_1.version, 1) self.assertEqual(order_1.orderStatus, None) updated_order = self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) self.assertEqual(updated_order.version, 2) self.assertEqual(updated_order.cancelledUsername, '*****@*****.**') self.assertEqual(updated_order.orderStatus, BiobankOrderStatus.CANCELLED) self.assertEqual(updated_order.amendedReason, cancelled_request['amendedReason']) ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.biospecimenFinalizedSiteId, None) def test_cancelled_order_removes_from_participant_summary(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) samples = [BiobankOrderedSample( test=self._B_TEST, processingRequired=True, description=u'new sample')] biobank_order_id = 2 with clock.FakeClock(self.TIME_1): order_1 = self.dao.insert(self._make_biobank_order()) with clock.FakeClock(self.TIME_2): self.dao.insert(self._make_biobank_order(samples=samples, biobankOrderId=biobank_order_id, identifiers=[ BiobankOrderIdentifier(system='z', value='x')])) cancelled_request = self._get_cancel_patch() ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.sampleOrderStatus1ED10, OrderStatus.FINALIZED) self.assertEqual(ps_dao.sampleOrderStatus1ED10Time, self.TIME_2) self.assertEqual(ps_dao.sampleOrderStatus2ED10, OrderStatus.CREATED) self.assertEqual(ps_dao.sampleOrderStatus2ED10Time, self.TIME_2) self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.sampleOrderStatus1ED10, None) self.assertEqual(ps_dao.sampleOrderStatus1ED10Time, None) # should not remove the other order self.assertEqual(ps_dao.sampleOrderStatus2ED10, OrderStatus.CREATED) self.assertEqual(ps_dao.sampleOrderStatus2ED10Time, self.TIME_2) self.assertEqual(ps_dao.biospecimenCollectedSiteId, 1) self.assertEqual(ps_dao.biospecimenFinalizedSiteId, 2) self.assertEqual(ps_dao.biospecimenProcessedSiteId, 1) self.assertEqual(ps_dao.biospecimenStatus, OrderStatus.FINALIZED) def test_restoring_an_order_gets_to_participant_summary(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) cancelled_request = self._get_cancel_patch() cancelled_order = self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) restore_request = self._get_restore_patch() self.dao.update_with_patch(order_1.biobankOrderId, restore_request, cancelled_order.version) ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.sampleOrderStatus1ED10, OrderStatus.FINALIZED) self.assertEqual(ps_dao.biospecimenStatus, OrderStatus.FINALIZED) self.assertEqual(ps_dao.biospecimenSourceSiteId, 1) self.assertEqual(ps_dao.biospecimenCollectedSiteId, 1) self.assertEqual(ps_dao.biospecimenFinalizedSiteId, 2) self.assertEqual(ps_dao.biospecimenProcessedSiteId, 1) def test_amending_order_participant_summary(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) amended_info = self._get_amended_info(order_1) amended_info.sourceSiteId = 2 samples = [BiobankOrderedSample( test=self._B_TEST, processingRequired=True, description=u'new sample')] amended_info.samples = samples with self.dao.session() as session: self.dao._do_update(session, amended_info, order_1) amended_order = self.dao.get(1) self.assertEqual(amended_order.version, 2) ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.sampleOrderStatus2ED10, OrderStatus.CREATED) self.assertEqual(ps_dao.sampleOrderStatus1ED10, OrderStatus.FINALIZED) self.assertEqual(ps_dao.numberDistinctVisits, 1) def test_cancelling_an_order_missing_reason(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) cancelled_request = self._get_cancel_patch() del cancelled_request['amendedReason'] with self.assertRaises(BadRequest): self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) def test_cancelling_an_order_missing_info(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) # missing cancelled info cancelled_request = self._get_cancel_patch() del cancelled_request['cancelledInfo'] with self.assertRaises(BadRequest): self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) # missing site cancelled_request = self._get_cancel_patch() del cancelled_request['cancelledInfo']['site'] with self.assertRaises(BadRequest): self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) # missing author cancelled_request = self._get_cancel_patch() del cancelled_request['cancelledInfo']['author'] with self.assertRaises(BadRequest): self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) # missing status cancelled_request = self._get_cancel_patch() del cancelled_request['status'] with self.assertRaises(BadRequest): self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) def test_restoring_an_order(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) cancelled_request = self._get_cancel_patch() cancelled_order = self.dao.update_with_patch(order_1.biobankOrderId, cancelled_request, order_1.version) ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.numberDistinctVisits, 0) restore_request = self._get_restore_patch() restored_order = self.dao.update_with_patch(order_1.biobankOrderId, restore_request, cancelled_order.version) self.assertEqual(restored_order.version, 3) self.assertEqual(restored_order.restoredUsername, '*****@*****.**') self.assertEqual(restored_order.orderStatus, BiobankOrderStatus.UNSET) self.assertEqual(restored_order.amendedReason, restore_request['amendedReason']) ps_dao = ParticipantSummaryDao().get(self.participant.participantId) self.assertEqual(ps_dao.numberDistinctVisits, 1) def test_amending_an_order(self): ParticipantSummaryDao().insert(self.participant_summary(self.participant)) order_1 = self.dao.insert(self._make_biobank_order()) amended_info = self._get_amended_info(order_1) amended_info.sourceSiteId = 2 with self.dao.session() as session: self.dao._do_update(session, order_1, amended_info) amended_order = self.dao.get(1) self.assertEqual(amended_order.version, 2) self.assertEqual(amended_order.orderStatus, BiobankOrderStatus.AMENDED) self.assertEqual(amended_order.amendedReason, 'I had to change something') self.assertEqual(amended_order.amendedSiteId, 1) self.assertEqual(amended_order.amendedUsername, '*****@*****.**')
class ParticipantSummaryDaoTest(NdbTestBase): def setUp(self): super(ParticipantSummaryDaoTest, self).setUp(use_mysql=True) self.dao = ParticipantSummaryDao() self.order_dao = BiobankOrderDao() self.measurement_dao = PhysicalMeasurementsDao() self.participant_dao = ParticipantDao() self.no_filter_query = Query([], None, 2, None) self.one_filter_query = Query([FieldFilter("participantId", Operator.EQUALS, 1)], None, 2, None) self.two_filter_query = Query([FieldFilter("participantId", Operator.EQUALS, 1), FieldFilter("hpoId", Operator.EQUALS, PITT_HPO_ID)], None, 2, None) self.ascending_biobank_id_query = Query([], OrderBy("biobankId", True), 2, None) self.descending_biobank_id_query = Query([], OrderBy("biobankId", False), 2, None) self.enrollment_status_order_query = Query([], OrderBy("enrollmentStatus", True), 2, None) self.hpo_id_order_query = Query([], OrderBy("hpoId", True), 2, None) self.first_name_order_query = Query([], OrderBy("firstName", True), 2, None) def assert_no_results(self, query): results = self.dao.query(query) self.assertEquals([], results.items) self.assertIsNone(results.pagination_token) def assert_results(self, query, items, pagination_token=None): results = self.dao.query(query) self.assertListAsDictEquals(items, results.items) self.assertEquals(pagination_token, results.pagination_token, "Pagination tokens don't match; decoded = %s, %s" % (_decode_token(pagination_token), _decode_token(results.pagination_token))) def test_query_with_total(self): num_participants = 5 query = Query([], None, 10, None, include_total=True) results = self.dao.query(query) self.assertEqual(results.total, 0) for i in range(num_participants): participant = Participant(participantId=i, biobankId=i) self._insert(participant) results = self.dao.query(query) self.assertEqual(results.total, num_participants) def testQuery_noSummaries(self): self.assert_no_results(self.no_filter_query) self.assert_no_results(self.one_filter_query) self.assert_no_results(self.two_filter_query) self.assert_no_results(self.ascending_biobank_id_query) self.assert_no_results(self.descending_biobank_id_query) def _insert(self, participant, first_name=None, last_name=None): self.participant_dao.insert(participant) summary = self.participant_summary(participant) if first_name: summary.firstName = first_name if last_name: summary.lastName = last_name self.dao.insert(summary) return participant def testQuery_oneSummary(self): participant = Participant(participantId=1, biobankId=2) self._insert(participant) summary = self.dao.get(1) self.assert_results(self.no_filter_query, [summary]) self.assert_results(self.one_filter_query, [summary]) self.assert_no_results(self.two_filter_query) self.assert_results(self.ascending_biobank_id_query, [summary]) self.assert_results(self.descending_biobank_id_query, [summary]) def testUnicodeNameRoundTrip(self): name = self.fake.first_name() with self.assertRaises(UnicodeEncodeError): str(name) # sanity check that the name contains non-ASCII participant = self._insert(Participant(participantId=1, biobankId=2)) summary = self.dao.get(participant.participantId) summary.firstName = name self.dao.update(summary) fetched_summary = self.dao.get(participant.participantId) self.assertEquals(name, fetched_summary.firstName) def testQuery_twoSummaries(self): participant_1 = Participant(participantId=1, biobankId=2) self._insert(participant_1, 'Alice', 'Smith') participant_2 = Participant(participantId=2, biobankId=1) self._insert(participant_2, 'Zed', 'Zebra') ps_1 = self.dao.get(1) ps_2 = self.dao.get(2) self.assert_results(self.no_filter_query, [ps_1, ps_2]) self.assert_results(self.one_filter_query, [ps_1]) self.assert_no_results(self.two_filter_query) self.assert_results(self.ascending_biobank_id_query, [ps_2, ps_1]) self.assert_results(self.descending_biobank_id_query, [ps_1, ps_2]) def testQuery_threeSummaries_paginate(self): participant_1 = Participant(participantId=1, biobankId=4) self._insert(participant_1, 'Alice', 'Aardvark') participant_2 = Participant(participantId=2, biobankId=1) self._insert(participant_2, 'Bob', 'Builder') participant_3 = Participant(participantId=3, biobankId=3) self._insert(participant_3, 'Chad', 'Caterpillar') ps_1 = self.dao.get(1) ps_2 = self.dao.get(2) ps_3 = self.dao.get(3) self.assert_results(self.no_filter_query, [ps_1, ps_2], _make_pagination_token(['Builder', 'Bob', None, 2])) self.assert_results(self.one_filter_query, [ps_1]) self.assert_no_results(self.two_filter_query) self.assert_results(self.ascending_biobank_id_query, [ps_2, ps_3], _make_pagination_token([3, 'Caterpillar', 'Chad', None, 3])) self.assert_results(self.descending_biobank_id_query, [ps_1, ps_3], _make_pagination_token([3, 'Caterpillar', 'Chad', None, 3])) self.assert_results(_with_token(self.no_filter_query, _make_pagination_token(['Builder', 'Bob', None, 2])), [ps_3]) self.assert_results(_with_token(self.ascending_biobank_id_query, _make_pagination_token([3, 'Caterpillar', 'Chad', None, 3])), [ps_1]) self.assert_results(_with_token(self.descending_biobank_id_query, _make_pagination_token([3, 'Caterpillar', 'Chad', None, 3])), [ps_2]) def testQuery_fourFullSummaries_paginate(self): participant_1 = Participant(participantId=1, biobankId=4) self._insert(participant_1, 'Bob', 'Jones') participant_2 = Participant(participantId=2, biobankId=1) self._insert(participant_2, 'Bob', 'Jones') participant_3 = Participant(participantId=3, biobankId=3) self._insert(participant_3, 'Bob', 'Jones') participant_4 = Participant(participantId=4, biobankId=2) self._insert(participant_4, 'Bob', 'Jones') ps_1 = self.dao.get(1) ps_2 = self.dao.get(2) ps_3 = self.dao.get(3) ps_4 = self.dao.get(4) ps_1.lastName = 'Jones' ps_1.firstName = 'Bob' ps_1.dateOfBirth = datetime.date(1978, 10, 9) ps_1.hpoId = PITT_HPO_ID self.dao.update(ps_1) ps_2.lastName = 'Aardvark' ps_2.firstName = 'Bob' ps_2.dateOfBirth = datetime.date(1978, 10, 10) ps_2.enrollmentStatus = EnrollmentStatus.MEMBER self.dao.update(ps_2) ps_3.lastName = 'Jones' ps_3.firstName = 'Bob' ps_3.dateOfBirth = datetime.date(1978, 10, 10) ps_3.hpoId = PITT_HPO_ID ps_3.enrollmentStatus = EnrollmentStatus.MEMBER self.dao.update(ps_3) ps_4.lastName = 'Jones' ps_4.enrollmentStatus = EnrollmentStatus.FULL_PARTICIPANT self.dao.update(ps_4) self.assert_results(self.no_filter_query, [ps_2, ps_4], _make_pagination_token(['Jones', 'Bob', None, 4])) self.assert_results(self.one_filter_query, [ps_1]) self.assert_results(self.two_filter_query, [ps_1]) self.assert_results(self.ascending_biobank_id_query, [ps_2, ps_4], _make_pagination_token([2, 'Jones', 'Bob', None, 4])) self.assert_results(self.descending_biobank_id_query, [ps_1, ps_3], _make_pagination_token([3, 'Jones', 'Bob', datetime.date(1978, 10, 10), 3])) self.assert_results(self.hpo_id_order_query, [ps_2, ps_4], _make_pagination_token([0, 'Jones', 'Bob', None, 4])) self.assert_results(self.enrollment_status_order_query, [ps_1, ps_2], _make_pagination_token(['MEMBER', 'Aardvark', 'Bob', datetime.date(1978, 10, 10), 2])) self.assert_results(_with_token(self.no_filter_query, _make_pagination_token(['Jones', 'Bob', None, 4])), [ps_1, ps_3]) self.assert_results(_with_token(self.ascending_biobank_id_query, _make_pagination_token([2, 'Jones', 'Bob', None, 4])), [ps_3, ps_1]) self.assert_results(_with_token(self.descending_biobank_id_query, _make_pagination_token([3, 'Jones', 'Bob', datetime.date(1978, 10, 10), 3])), [ps_4, ps_2]) self.assert_results(_with_token(self.hpo_id_order_query, _make_pagination_token([0, 'Jones', 'Bob', None, 4])), [ps_1, ps_3]) self.assert_results(_with_token(self.enrollment_status_order_query, _make_pagination_token(['MEMBER', 'Aardvark', 'Bob', datetime.date(1978, 10, 10), 2])), [ps_3, ps_4]) def test_update_from_samples(self): # baseline_tests = ['BASELINE1', 'BASELINE2'] baseline_tests = ["1PST8", "2PST8"] config.override_setting(config.BASELINE_SAMPLE_TEST_CODES, baseline_tests) self.dao.update_from_biobank_stored_samples() # safe noop p_baseline_samples = self._insert(Participant(participantId=1, biobankId=11)) p_mixed_samples = self._insert(Participant(participantId=2, biobankId=22)) p_no_samples = self._insert(Participant(participantId=3, biobankId=33)) p_unconfirmed = self._insert(Participant(participantId=4, biobankId=44)) self.assertEquals(self.dao.get(p_baseline_samples.participantId).numBaselineSamplesArrived, 0) def get_p_baseline_last_modified(): return self.dao.get(p_baseline_samples.participantId).lastModified p_baseline_last_modified1 = get_p_baseline_last_modified() sample_dao = BiobankStoredSampleDao() def add_sample(participant, test_code, sample_id): TIME = datetime.datetime(2018, 3, 2) sample_dao.insert(BiobankStoredSample( biobankStoredSampleId=sample_id, biobankId=participant.biobankId, biobankOrderIdentifier='KIT', test=test_code, confirmed=TIME)) add_sample(p_baseline_samples, baseline_tests[0], '11111') add_sample(p_baseline_samples, baseline_tests[1], '22223') add_sample(p_mixed_samples, baseline_tests[0], '11112') add_sample(p_mixed_samples, 'NOT1', '44441') # add unconfirmed sample sample_dao.insert(BiobankStoredSample(biobankStoredSampleId=55555, biobankId=p_unconfirmed.biobankId, biobankOrderIdentifier='KIT', test=baseline_tests[1], confirmed=None)) # sleep 1 sec to make lastModified different time.sleep(1) self.dao.update_from_biobank_stored_samples() p_baseline_last_modified2 = get_p_baseline_last_modified() self.assertNotEquals(p_baseline_last_modified2, p_baseline_last_modified1) self.assertEquals(self.dao.get(p_baseline_samples.participantId).numBaselineSamplesArrived, 2) self.assertEquals(self.dao.get(p_mixed_samples.participantId).numBaselineSamplesArrived, 1) self.assertEquals(self.dao.get(p_no_samples.participantId).numBaselineSamplesArrived, 0) self.assertEquals(self.dao.get(p_unconfirmed.participantId).numBaselineSamplesArrived, 0) M_baseline_samples = self._insert(Participant(participantId=9, biobankId=99)) add_sample(M_baseline_samples, baseline_tests[0], '999') M_first_update = self.dao.get(M_baseline_samples.participantId) # sleep 1 sec to make lastModified different time.sleep(1) self.dao.update_from_biobank_stored_samples() add_sample(M_baseline_samples, baseline_tests[1], '9999') M_second_update = self.dao.get(M_baseline_samples.participantId) # sleep 1 sec to make lastModified different time.sleep(1) self.dao.update_from_biobank_stored_samples() self.assertNotEqual(M_first_update.lastModified, M_second_update.lastModified) self.assertEquals(get_p_baseline_last_modified(), p_baseline_last_modified2) def test_update_from_samples_changed_tests(self): baseline_tests = ["1PST8", "2PST8"] config.override_setting(config.BASELINE_SAMPLE_TEST_CODES, baseline_tests) self.dao.update_from_biobank_stored_samples() # safe noop participant = self._insert(Participant(participantId=1, biobankId=11)) self.assertEquals(self.dao.get(participant.participantId).numBaselineSamplesArrived, 0) sample_dao = BiobankStoredSampleDao() def add_sample(test_code, sample_id): TIME = datetime.datetime(2018, 3, 2) sample_dao.insert(BiobankStoredSample( biobankStoredSampleId=sample_id, biobankId=participant.biobankId, biobankOrderIdentifier='KIT', test=test_code, confirmed=TIME)) add_sample(baseline_tests[0], '11111') add_sample(baseline_tests[1], '22223') self.dao.update_from_biobank_stored_samples() summary = self.dao.get(participant.participantId) init_last_modified = summary.lastModified self.assertEquals(summary.numBaselineSamplesArrived, 2) # sleep 1 sec to make lastModified different time.sleep(1) # Simulate removal of one of the baseline tests from config.json. baseline_tests.pop() config.override_setting(config.BASELINE_SAMPLE_TEST_CODES, baseline_tests) self.dao.update_from_biobank_stored_samples() summary = self.dao.get(participant.participantId) self.assertEquals(summary.numBaselineSamplesArrived, 1) self.assertNotEqual(init_last_modified, summary.lastModified) def test_only_update_dna_sample(self): dna_tests = ["1ED10", "1SAL2"] config.override_setting(config.DNA_SAMPLE_TEST_CODES, dna_tests) self.dao.update_from_biobank_stored_samples() # safe noop p_dna_samples = self._insert(Participant(participantId=1, biobankId=11)) self.assertEquals(self.dao.get(p_dna_samples.participantId).samplesToIsolateDNA, None) self.assertEquals( self.dao.get(p_dna_samples.participantId).enrollmentStatusCoreStoredSampleTime, None) self.assertEquals( self.dao.get(p_dna_samples.participantId).enrollmentStatusCoreOrderedSampleTime, None) sample_dao = BiobankStoredSampleDao() def add_sample(participant, test_code, sample_id, confirmed_time): sample_dao.insert(BiobankStoredSample( biobankStoredSampleId=sample_id, biobankId=participant.biobankId, biobankOrderIdentifier='KIT', test=test_code, confirmed=confirmed_time)) confirmed_time_0 = datetime.datetime(2018, 3, 1) add_sample(p_dna_samples, dna_tests[0], '11111', confirmed_time_0) self.dao.update_from_biobank_stored_samples() self.assertEquals(self.dao.get(p_dna_samples.participantId).samplesToIsolateDNA, SampleStatus.RECEIVED) # only update dna sample will not update enrollmentStatusCoreStoredSampleTime self.assertEquals( self.dao.get(p_dna_samples.participantId).enrollmentStatusCoreStoredSampleTime, None) self.assertEquals( self.dao.get(p_dna_samples.participantId).enrollmentStatusCoreOrderedSampleTime, None) def test_calculate_enrollment_status(self): self.assertEquals(EnrollmentStatus.FULL_PARTICIPANT, self.dao.calculate_enrollment_status(True, NUM_BASELINE_PPI_MODULES, PhysicalMeasurementsStatus.COMPLETED, SampleStatus.RECEIVED)) self.assertEquals(EnrollmentStatus.MEMBER, self.dao.calculate_enrollment_status(True, NUM_BASELINE_PPI_MODULES - 1, PhysicalMeasurementsStatus.COMPLETED, SampleStatus.RECEIVED)) self.assertEquals(EnrollmentStatus.MEMBER, self.dao.calculate_enrollment_status(True, NUM_BASELINE_PPI_MODULES, PhysicalMeasurementsStatus.UNSET, SampleStatus.RECEIVED)) self.assertEquals(EnrollmentStatus.MEMBER, self.dao.calculate_enrollment_status(True, NUM_BASELINE_PPI_MODULES, PhysicalMeasurementsStatus.COMPLETED, SampleStatus.UNSET)) self.assertEquals(EnrollmentStatus.INTERESTED, self.dao.calculate_enrollment_status(False, NUM_BASELINE_PPI_MODULES, PhysicalMeasurementsStatus.COMPLETED, SampleStatus.RECEIVED)) def testUpdateEnrollmentStatus(self): ehr_consent_time = datetime.datetime(2018, 3, 1) summary = ParticipantSummary( participantId=1, biobankId=2, consentForStudyEnrollment=QuestionnaireStatus.SUBMITTED, consentForElectronicHealthRecords=QuestionnaireStatus.SUBMITTED, consentForElectronicHealthRecordsTime=ehr_consent_time, enrollmentStatus=EnrollmentStatus.INTERESTED) self.dao.update_enrollment_status(summary) self.assertEquals(EnrollmentStatus.MEMBER, summary.enrollmentStatus) self.assertEquals(ehr_consent_time, summary.enrollmentStatusMemberTime) def testUpdateEnrollmentStatusLastModified(self): """DA-631: enrollment_status update should update last_modified.""" participant = self._insert(Participant(participantId=6, biobankId=66)) # collect current modified and enrollment status summary = self.dao.get(participant.participantId) test_dt = datetime.datetime(2018, 11, 1) def reset_summary(): # change summary so enrollment status will be changed from INTERESTED to MEMBER. summary.enrollmentStatus = EnrollmentStatus.INTERESTED summary.lastModified = test_dt summary.consentForStudyEnrollment = QuestionnaireStatus.SUBMITTED summary.consentForElectronicHealthRecords = QuestionnaireStatus.SUBMITTED summary.physicalMeasurementsStatus = PhysicalMeasurementsStatus.COMPLETED summary.samplesToIsolateDNA = SampleStatus.RECEIVED self.dao.update(summary) ## Test Step 1: Validate update_from_biobank_stored_samples() changes lastModified. reset_summary() # Update and reload summary record self.dao.update_from_biobank_stored_samples(participant_id=participant.participantId) summary = self.dao.get(participant.participantId) # Test that status has changed and lastModified is also different self.assertEquals(EnrollmentStatus.MEMBER, summary.enrollmentStatus) self.assertNotEqual(test_dt, summary.lastModified) ## Test Step 2: Validate that update_enrollment_status() changes the lastModified property. reset_summary() summary = self.dao.get(participant.participantId) self.assertEqual(test_dt, summary.lastModified) # update_enrollment_status() does not touch the db, it only modifies object properties. self.dao.update_enrollment_status(summary) self.assertEquals(EnrollmentStatus.MEMBER, summary.enrollmentStatus) self.assertNotEqual(test_dt, summary.lastModified) def testNumberDistinctVisitsCounts(self): self.participant = self._insert(Participant(participantId=7, biobankId=77)) # insert biobank order order = self.order_dao.insert(self._make_biobank_order()) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) cancel_request = cancel_biobank_order() # cancel biobank order self.order_dao.update_with_patch(order.biobankOrderId, cancel_request, order.version) summary = self.dao.get(self.participant.participantId) # distinct count should be 0 self.assertEquals(summary.numberDistinctVisits, 0) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_1.isoformat())) # insert physical measurement measurement = self.measurement_dao.insert(self._make_physical_measurements()) summary = self.dao.get(self.participant.participantId) # count should be 1 self.assertEquals(summary.numberDistinctVisits, 1) # cancel the measurement cancel_measurement = get_restore_or_cancel_info() with self.measurement_dao.session() as session: self.measurement_dao.update_with_patch(measurement.physicalMeasurementsId, session, cancel_measurement) summary = self.dao.get(self.participant.participantId) # count should be 0 self.assertEquals(summary.numberDistinctVisits, 0) with clock.FakeClock(TIME_1): self.order_dao.insert(self._make_biobank_order(biobankOrderId='2', identifiers=[ BiobankOrderIdentifier(system='b', value='d')], samples=[BiobankOrderedSample( biobankOrderId='2', test=BIOBANK_TESTS[0], description='description', processingRequired=True)])) with clock.FakeClock(TIME_2): self.measurement_dao.insert(self._make_physical_measurements( physicalMeasurementsId=2)) summary = self.dao.get(self.participant.participantId) # A PM on another day should add a new distinct count. self.assertEquals(summary.numberDistinctVisits, 2) with clock.FakeClock(TIME_3): self.order_dao.insert(self._make_biobank_order(biobankOrderId='3', identifiers=[ BiobankOrderIdentifier(system='s', value='s')], samples=[BiobankOrderedSample( biobankOrderId ='3', finalized=TIME_3, test=BIOBANK_TESTS[1], description='another description', processingRequired=False)])) # a physical measurement on same day as biobank order does not add distinct visit. self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=6)) # another biobank order on the same day should also not add a distinct visit self.order_dao.insert(self._make_biobank_order(biobankOrderId='7', identifiers=[ BiobankOrderIdentifier(system='x', value='x')], samples=[BiobankOrderedSample( biobankOrderId ='7', finalized=TIME_3, test=BIOBANK_TESTS[1], description='another description', processingRequired=False)])) summary = self.dao.get(self.participant.participantId) # 1 from each of TIME_1 TIME_2 TIME_3 self.assertEquals(summary.numberDistinctVisits, 3) def test_qa_scenarios_for_pmb_visits(self): """ PDR at https://docs.google.com/document/d/1sL54f-I91RvhjIprrdbwD8TlR9Jq91MX2ELf1EtJdxc/edit#heading=h.bqo8kt3igsrw<Paste> """ self.participant = self._insert(Participant(participantId=6, biobankId=66)) # test scenario 1 with clock.FakeClock(TIME_4): self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_4.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=666, participantId=self.participant.participantId, finalized=TIME_4)) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) with clock.FakeClock(TIME_5): self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_5.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=669, finalized=TIME_5)) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 2) # test scenario 2 with clock.FakeClock(TIME_6): self.participant = self._insert(Participant(participantId=9, biobankId=13)) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_6.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=8, finalized=TIME_6)) self.order_dao.insert(self._make_biobank_order(biobankOrderId='2', identifiers=[ BiobankOrderIdentifier(system='b', value='d')], samples=[BiobankOrderedSample( biobankOrderId='2', finalized=TIME_7, test=BIOBANK_TESTS[0], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) # distinct count should be 2 self.assertEquals(summary.numberDistinctVisits, 2) # test scenario 3 with clock.FakeClock(TIME_6): self.participant = self._insert(Participant(participantId=66, biobankId=42)) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_6.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=12, createdSiteId=2, finalized=TIME_6)) self.order_dao.insert(self._make_biobank_order(biobankOrderId='18', finalizedSiteId=1, identifiers=[ BiobankOrderIdentifier(system='x', value='y')], samples=[BiobankOrderedSample( biobankOrderId='18', finalized=TIME_6, test=BIOBANK_TESTS[0], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) # distinct count should be 1 self.assertEquals(summary.numberDistinctVisits, 1) # test scenario 4 with clock.FakeClock(TIME_8): self.participant = self._insert(Participant(participantId=6613, biobankId=142)) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_8.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=129, finalized=TIME_8)) order = self.order_dao.insert(self._make_biobank_order(biobankOrderId='999', identifiers=[ BiobankOrderIdentifier(system='s', value='s')], samples=[BiobankOrderedSample( biobankOrderId='999', finalized=TIME_8, test=BIOBANK_TESTS[1], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) # distinct count should be 1 self.assertEquals(summary.numberDistinctVisits, 1) # change finalized time, recalculating count with self.order_dao.session() as session: existing_order = copy.deepcopy(order) order.samples[0].finalized = TIME_9 self.order_dao._do_update(session, order, existing_order) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) # change test, should not change count. with self.order_dao.session() as session: existing_order = copy.deepcopy(order) order.samples[0].test = BIOBANK_TESTS[0] self.order_dao._do_update(session, order, existing_order) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) # test scenario 5 with clock.FakeClock(TIME_12): self.participant = self._insert(Participant(participantId=3000, biobankId=2019)) self.order_dao.insert(self._make_biobank_order(biobankOrderId='700', identifiers=[ BiobankOrderIdentifier(system='n', value='s')], samples=[BiobankOrderedSample( biobankOrderId='700', finalized=TIME_10, test=BIOBANK_TESTS[1], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) # distinct count should be 1 self.assertEquals(summary.numberDistinctVisits, 1) other_order = self.order_dao.insert(self._make_biobank_order(biobankOrderId='701', identifiers=[ BiobankOrderIdentifier(system='n', value='t')], samples=[BiobankOrderedSample( biobankOrderId='701', finalized=TIME_11, test=BIOBANK_TESTS[1], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) # distinct count should be 2 self.assertEquals(summary.numberDistinctVisits, 2) order = self.order_dao.insert(self._make_biobank_order(biobankOrderId='702', identifiers=[ BiobankOrderIdentifier(system='n', value='u')], samples=[BiobankOrderedSample( biobankOrderId='702', finalized=TIME_12, test=BIOBANK_TESTS[1], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) # distinct count should be 3 self.assertEquals(summary.numberDistinctVisits, 3) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_12.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=120, finalized=TIME_12)) summary = self.dao.get(self.participant.participantId) # distinct count should be 3 self.assertEquals(summary.numberDistinctVisits, 3) cancel_request = cancel_biobank_order() # cancel biobank order with PM on same day self.order_dao.update_with_patch(order.biobankOrderId, cancel_request, order.version) summary = self.dao.get(self.participant.participantId) # distinct count should be 3 (the PM on same day still counts) self.assertEquals(summary.numberDistinctVisits, 3) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_1.isoformat())) self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=150, finalized=TIME_1)) summary = self.dao.get(self.participant.participantId) # distinct count should be 4 self.assertEquals(summary.numberDistinctVisits, 4) # cancel order with pm on different day self.order_dao.update_with_patch(other_order.biobankOrderId, cancel_request, order.version) summary = self.dao.get(self.participant.participantId) # distinct count should be 3 self.assertEquals(summary.numberDistinctVisits, 3) def test_pm_restore_cancel_biobank_restore_cancel(self): self.participant = self._insert(Participant(participantId=9, biobankId=13)) self.measurement_json = json.dumps(load_measurement_json(self.participant.participantId, TIME_4.isoformat())) measurement = self.measurement_dao.insert(self._make_physical_measurements(physicalMeasurementsId=669, finalized=TIME_4)) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) with clock.FakeClock(TIME_5): order = self.order_dao.insert(self._make_biobank_order(biobankOrderId='2', identifiers=[ BiobankOrderIdentifier(system='b', value='d')], samples=[BiobankOrderedSample( biobankOrderId='2', finalized=TIME_5, test=BIOBANK_TESTS[0], description='description', processingRequired=True)])) with clock.FakeClock(TIME_7): summary = self.dao.get(self.participant.participantId) # distinct count should be 2 self.assertEquals(summary.numberDistinctVisits, 2) # cancel the measurement cancel_measurement = get_restore_or_cancel_info() with self.measurement_dao.session() as session: self.measurement_dao.update_with_patch(measurement.physicalMeasurementsId, session, cancel_measurement) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) with clock.FakeClock(TIME_7): restore_measurement = get_restore_or_cancel_info(status='restored') with self.measurement_dao.session() as session: self.measurement_dao.update_with_patch(measurement.physicalMeasurementsId, session, restore_measurement) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 2) cancel_request = cancel_biobank_order() order = self.order_dao.update_with_patch(order.biobankOrderId, cancel_request, order.version) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) restore_order = get_restore_or_cancel_info(status='restored') restore_order['amendedReason'] = 'some reason' self.order_dao.update_with_patch(order.biobankOrderId, restore_order, order.version) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 2) def test_amending_biobank_order_distinct_visit_count(self): self.participant = self._insert(Participant(participantId=9, biobankId=13)) with clock.FakeClock(TIME_5): order = self.order_dao.insert(self._make_biobank_order(biobankOrderId='2', identifiers=[ BiobankOrderIdentifier(system='b', value='d')], samples=[BiobankOrderedSample( biobankOrderId='2', finalized=TIME_5, test=BIOBANK_TESTS[0], description='description', processingRequired=True)])) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) with clock.FakeClock(TIME_7): amend_order = self._get_amended_info(order) with self.order_dao.session() as session: self.order_dao._do_update(session, amend_order, order) # Shouldn't change on a simple amendment (unless finalized time on samples change) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 1) with clock.FakeClock(TIME_7_5): cancel_request = cancel_biobank_order() order = self.order_dao.update_with_patch(order.biobankOrderId, cancel_request, order.version) # A cancelled order (even after amending) should reduce count (unless some other valid order on same day) summary = self.dao.get(self.participant.participantId) self.assertEquals(summary.numberDistinctVisits, 0) @staticmethod def _get_amended_info(order): amendment = dict(amendedReason='I had to change something', amendedInfo={ "author": { "system": "https://www.pmi-ops.org/healthpro-username", "value": "*****@*****.**" }, "site": { "system": "https://www.pmi-ops.org/site-id", "value": "hpo-site-monroeville" } }) order.amendedReason = amendment['amendedReason'] order.amendedInfo = amendment['amendedInfo'] return order def _make_biobank_order(self, **kwargs): """Makes a new BiobankOrder (same values every time) with valid/complete defaults. Kwargs pass through to BiobankOrder constructor, overriding defaults. """ for k, default_value in ( ('biobankOrderId', '1'), ('created', clock.CLOCK.now()), ('participantId', self.participant.participantId), ('sourceSiteId', 1), ('sourceUsername', '*****@*****.**'), ('collectedSiteId', 1), ('collectedUsername', '*****@*****.**'), ('processedSiteId', 1), ('processedUsername', '*****@*****.**'), ('finalizedSiteId', 2), ('finalizedUsername', '*****@*****.**'), ('identifiers', [BiobankOrderIdentifier(system='a', value='c')]), ('samples', [BiobankOrderedSample( biobankOrderId='1', test=BIOBANK_TESTS[0], description='description', finalized=TIME_1, processingRequired=True)])): if k not in kwargs: kwargs[k] = default_value return BiobankOrder(**kwargs) def _make_physical_measurements(self, **kwargs): """Makes a new PhysicalMeasurements (same values every time) with valid/complete defaults. Kwargs pass through to PM constructor, overriding defaults. """ for k, default_value in ( ('physicalMeasurementsId', 1), ('participantId', self.participant.participantId), ('resource', self.measurement_json), ('createdSiteId', 1), ('finalized', TIME_3), ('finalizedSiteId', 2)): if k not in kwargs: kwargs[k] = default_value return PhysicalMeasurements(**kwargs)