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)
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_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_replace_history(self): fake_clock = clock.FakeClock(datetime.datetime.utcnow()) orig_config = self.send_get('Config') # Replace some data in the current config. test_key = 'testing_config_key' new_config_1 = copy.deepcopy(orig_config) new_config_1[test_key] = ['initially', 'injected', 'values'] with fake_clock: self.send_request('PUT', 'Config', request_data=new_config_1) # Make sure the replacements show up when re-fetching the config. with fake_clock: response = self.send_get('Config') self.assertEquals(new_config_1, response) fake_clock.advance() between_updates = fake_clock.now fake_clock.advance() # Make sure another replacement takes effect. new_config_2 = copy.deepcopy(orig_config) new_config_2[test_key] = ['afterwards', 'replaced', 'values'] with fake_clock: self.send_request('PUT', 'Config', new_config_2) response = self.send_get('Config') self.assertEquals(new_config_2, response) # Make sure we get the the first replacement config when we query by time. with fake_clock: response = self.send_get('Config?date={}'.format( between_updates.isoformat())) self.assertEquals(new_config_1, response)
def test_ny_zip_code(self): participant_a = self.make_participant() self.make_summary(participant_a, zipCode=None) participant_b = self.make_participant() self.make_summary(participant_b, zipCode='') participant_c = self.make_participant() self.make_summary(participant_c, zipCode='12345') genomic_set = self.make_genomic_set() member_a = self.make_genomic_member(genomic_set, participant_a) member_b = self.make_genomic_member(genomic_set, participant_b) member_c = self.make_genomic_member(genomic_set, participant_c) with clock.FakeClock(datetime.datetime(2019, 1, 1)): validate_and_update_genomic_set_by_id(genomic_set.id) current_member_a = self.genomic_member_dao.get(member_a.id) current_member_b = self.genomic_member_dao.get(member_b.id) current_member_c = self.genomic_member_dao.get(member_c.id) self.assertEqual(current_member_a.validationStatus, GenomicSetMemberStatus.INVALID) self.assertIn(GenomicValidationFlag.INVALID_NY_ZIPCODE, current_member_a.validationFlags) self.assertEqual(current_member_b.validationStatus, GenomicSetMemberStatus.INVALID) self.assertIn(GenomicValidationFlag.INVALID_NY_ZIPCODE, current_member_a.validationFlags) self.assertEqual(current_member_c.validationStatus, GenomicSetMemberStatus.VALID) current_set = self.genomic_set_dao.get(genomic_set.id) self.assertEqual(current_set.genomicSetStatus, GenomicSetStatus.INVALID)
def test_age(self): now = datetime.datetime(2019, 1, 1) valid_date_of_birth = datetime.datetime(now.year - 18, now.month, now.day) invalid_date_of_birth = datetime.datetime(now.year - 17, now.month, now.day) participant_a = self.make_participant() self.make_summary(participant_a, dateOfBirth=valid_date_of_birth) participant_b = self.make_participant() self.make_summary(participant_b, dateOfBirth=invalid_date_of_birth) genomic_set = self.make_genomic_set() member_a = self.make_genomic_member(genomic_set, participant_a) member_b = self.make_genomic_member(genomic_set, participant_b) with clock.FakeClock(datetime.datetime(2019, 1, 1)): validate_and_update_genomic_set_by_id(genomic_set.id) current_member_a = self.genomic_member_dao.get(member_a.id) current_member_b = self.genomic_member_dao.get(member_b.id) self.assertEqual(current_member_a.validationStatus, GenomicSetMemberStatus.VALID) self.assertEqual(current_member_b.validationStatus, GenomicSetMemberStatus.INVALID) self.assertIn(GenomicValidationFlag.INVALID_AGE, current_member_b.validationFlags) current_set = self.genomic_set_dao.get(genomic_set.id) self.assertEqual(current_set.genomicSetStatus, GenomicSetStatus.INVALID)
def test_date_header(self): response = lambda: None # Dummy object; functions can have arbitrary attrs set on them. setattr(response, 'headers', {}) with clock.FakeClock(datetime.datetime(1994, 11, 6, 8, 49, 37)): app_util.add_headers(response) self.assertEquals(response.headers['Date'], 'Sun, 06 Nov 1994 08:49:37 GMT')
def test_valid_does_update_validated_time(self): participant = self.make_participant() self.make_summary(participant) genomic_set = self.make_genomic_set() member = self.make_genomic_member(genomic_set, participant) now = datetime.datetime(2019, 1, 1) with clock.FakeClock(now): validate_and_update_genomic_set_by_id(genomic_set.id) current_member = self.genomic_member_dao.get(member.id) self.assertEqual(current_member.validatedTime, now) current_set = self.genomic_set_dao.get(genomic_set.id) self.assertEqual(current_set.validatedTime, now)
def setUp(self): super(BigQuerySyncDaoTest, self).setUp(use_mysql=True, with_consent_codes=True) self.dao = ParticipantDao() with self.dao.session() as session: self.site = session.query(Site).filter( Site.googleGroup == 'hpo-site-monroeville').first() self.hpo = session.query(HPO).filter(HPO.name == 'PITT').first() with clock.FakeClock(self.TIME_1): self.participant = Participant(participantId=123, biobankId=555) self.participant.hpoId = self.hpo.hpoId self.participant.siteId = self.site.siteId self.dao.insert(self.participant) ps = ParticipantSummary( participantId=123, biobankId=555, firstName='john', lastName='doe', withdrawalStatus=WithdrawalStatus.NOT_WITHDRAWN, suspensionStatus=SuspensionStatus.NOT_SUSPENDED) ps.hpoId = self.hpo.hpoId ps.siteId = self.site.siteId self.summary = ParticipantSummaryDao().insert(ps) self.pm_json = json.dumps( load_measurement_json(self.participant.participantId, self.TIME_1.isoformat())) self.pm = PhysicalMeasurementsDao().insert( self._make_physical_measurements()) with clock.FakeClock(self.TIME_2): self.dao = BiobankOrderDao() self.bio_order = BiobankOrderDao().insert( self._make_biobank_order( participantId=self.participant.participantId))
def test_transaction(self): participant = self.make_participant() self.make_summary(participant) genomic_set = self.make_genomic_set() member = self.make_genomic_member(genomic_set, participant) with mock.patch('genomic.validation.GenomicSetDao.update_with_session' ) as mocked_set_update: mocked_set_update.side_effect = Exception('baz') with clock.FakeClock(datetime.datetime(2019, 1, 1)): with self.assertRaises(Exception): validate_and_update_genomic_set_by_id(genomic_set.id) current_member = self.genomic_member_dao.get(member.id) self.assertEqual(current_member.validationStatus, None) current_set = self.genomic_set_dao.get(genomic_set.id) self.assertEqual(current_set.genomicSetStatus, None)
def run(self): """ Main program process :param args: program arguments :return: Exit code value """ # load participant spreadsheet from bucket or local file. csv_data = self._local_csv_data( self.args.src_csv) or self._gdoc_csv_data(self.args.src_csv) if not csv_data: _logger.error( 'unable to fetch participant source spreadsheet [{0}].'.format( self.args.src_csv)) return 1 _logger.info('processing source data.') count = 0 # Loop through each column and generate data. for column in range(0, len(csv_data[0]) - 1): p_data = self._convert_csv_column_to_dict(csv_data, column) hpo = self._get_dict_data_by_key(p_data, '_HPO') pm = self._get_dict_data_by_key(p_data, '_PM') site_id = self._get_dict_data_by_key(p_data, '_HPOSite') bio_orders = self._get_dict_data_by_key(p_data, '_BIOOrder') bio_orders_mayo = self._get_dict_data_by_key( p_data, '_BIOOrderMayo') ppi_modules = self._get_dict_data_by_key(p_data, '_PPIModule') # choose a random starting date, timestamps of all other activities feed off this value. start_dt = self._random_date() # # Create a new participant # count += 1 _logger.info('participant [{0}].'.format(count)) with clock.FakeClock(start_dt): p_obj, hpo_site = self.create_participant(site_id=site_id, hpo_id=hpo) if not p_obj or 'participantId' not in p_obj.__dict__: _logger.error('failed to create participant.') continue _logger.info(' created [{0}].'.format(p_obj.participantId)) # # process any questionnaire modules # if ppi_modules: # submit the first module pretty close to the start date. Assumes the first # module is ConsentPII. mod_dt = self._increment_date(start_dt, minute_range=60) modules = ppi_modules.split('|') for module in modules: with clock.FakeClock(mod_dt): mod_obj = self.submit_module_response( module, p_obj.participantId, p_data.items()) if mod_obj: _logger.info( ' module: [{0}]: submitted.'.format(module)) else: _logger.info( ' module: [{0}]: failed.'.format(module)) # # see if we need to submit physical measurements. # if module == 'ConsentPII' and pm and pm.lower() == 'yes': mod_dt = self._random_date( mod_dt, datetime.timedelta(minutes=90)) with clock.FakeClock(mod_dt): pm_obj = self.submit_physical_measurements( p_obj.participantId, hpo_site) if pm_obj: _logger.info(' pm: submitted.') else: _logger.info(' pm: failed.') # choose a new random date between mod_dt and mod_dt + 15 days. mod_dt = self._random_date(mod_dt, datetime.timedelta(days=15)) # # process biobank samples # if bio_orders: sample_dt = self._increment_date(start_dt, day_range=10) samples = bio_orders.split('|') for sample in samples: with clock.FakeClock(sample_dt): bio_obj = self.submit_biobank_order( p_obj.participantId, sample, hpo_site) if bio_obj: _logger.info( ' biobank order: [{0}] submitted.'.format( sample)) else: _logger.info( ' biobank order: [{0}] failed.'.format( sample)) sample_dt = self._random_date(sample_dt, datetime.timedelta(days=30)) # # process biobank samples that also need to be sent to Mayolink. # if bio_orders_mayo: sample_dt = self._increment_date(start_dt, day_range=10) samples = bio_orders_mayo.split('|') for sample in samples: with clock.FakeClock(sample_dt): bio_obj = self.submit_biobank_order( p_obj.participantId, sample, hpo_site, to_mayo=True) if bio_obj: _logger.info( ' biobank order w/mayo: {0} submitted.'. format(sample)) else: _logger.info( ' biobank order w/mayo: {0} failed.'.format( sample)) sample_dt = self._random_date(sample_dt, datetime.timedelta(days=30)) # TODO: Add code for sending orders to mayo here, in a new ticket. return 0
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 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)