예제 #1
0
    def test_end_to_end(self):
        dao = BiobankStoredSampleDao()
        self.assertEquals(dao.count(), 0)

        # Create 3 participants and pass their (random) IDs into sample rows.
        summary_dao = ParticipantSummaryDao()
        biobank_ids = []
        participant_ids = []
        for _ in xrange(3):
            participant = self.participant_dao.insert(Participant())
            summary_dao.insert(self.participant_summary(participant))
            participant_ids.append(participant.participantId)
            biobank_ids.append(participant.biobankId)
            self.assertEquals(
                summary_dao.get(
                    participant.participantId).numBaselineSamplesArrived, 0)
        test1, test2, test3 = random.sample(_BASELINE_TESTS, 3)
        samples_file = test_data.open_biobank_samples(*biobank_ids,
                                                      test1=test1,
                                                      test2=test2,
                                                      test3=test3)
        input_filename = 'cloud%s.csv' % self._naive_utc_to_naive_central(
            clock.CLOCK.now()).strftime(
                biobank_samples_pipeline.INPUT_CSV_TIME_FORMAT)
        self._write_cloud_csv(input_filename, samples_file.read())

        biobank_samples_pipeline.upsert_from_latest_csv()

        self.assertEquals(dao.count(), 3)
        self._check_summary(participant_ids[0], test1, '2016-11-29T12:19:32')
        self._check_summary(participant_ids[1], test2, '2016-11-29T12:38:58')
        self._check_summary(participant_ids[2], test3, '2016-11-29T12:41:26')
def _do_update_for_organization(organization_id, submission_date, person_file):
    """
  deferred task: creates EhrReceipt and updates ParticipantSummary objects from a person.csv file
  """
    updated_datetime = datetime.datetime.combine(submission_date,
                                                 datetime.datetime.min.time())

    org_dao = OrganizationDao()
    summary_dao = ParticipantSummaryDao()
    receipt_dao = EhrReceiptDao()

    org_external_id = organization_id.upper()
    org = org_dao.get_by_external_id(org_external_id)
    if org is None:
        LOG.info("Organization not found with external_id: {}".format(
            org_external_id))

    receipt = EhrReceipt(organizationId=org.organizationId,
                         receiptTime=updated_datetime)
    receipt_dao.insert(receipt)

    for participant_id in _get_participant_ids_from_person_file(person_file):
        summary = summary_dao.get(participant_id)
        if summary is None:
            LOG.info("Participant not found with participant_id: {}".format(
                participant_id))
            continue
        summary_dao.update_ehr_status(summary, updated_datetime)
        summary_dao.update(summary)
    def test_end_to_end(self):
        dao = BiobankStoredSampleDao()
        self.assertEquals(dao.count(), 0)

        # Create 3 participants and pass their (random) IDs into sample rows.
        summary_dao = ParticipantSummaryDao()
        biobank_ids = []
        participant_ids = []
        nids = 16  # equal to the number of parent rows in 'biobank_samples_1.csv'
        cids = 1  # equal to the number of child rows in 'biobank_samples_1.csv'

        for _ in xrange(nids):
            participant = self.participant_dao.insert(Participant())
            summary_dao.insert(self.participant_summary(participant))
            participant_ids.append(participant.participantId)
            biobank_ids.append(participant.biobankId)
            self.assertEquals(
                summary_dao.get(
                    participant.participantId).numBaselineSamplesArrived, 0)

        test_codes = random.sample(_BASELINE_TESTS, nids)
        samples_file = test_data.open_biobank_samples(biobank_ids=biobank_ids,
                                                      tests=test_codes)
        lines = samples_file.split('\n')[1:]  # remove field name line

        input_filename = 'cloud%s.csv' % self._naive_utc_to_naive_central(
            clock.CLOCK.now()).strftime(
                biobank_samples_pipeline.INPUT_CSV_TIME_FORMAT)
        self._write_cloud_csv(input_filename, samples_file)
        biobank_samples_pipeline.upsert_from_latest_csv()

        self.assertEquals(dao.count(), nids - cids)

        for x in range(0, nids):
            cols = lines[x].split('\t')

            if cols[10].strip():  # skip child sample
                continue

            # If status is 'In Prep', then sample confirmed timestamp should be empty
            if cols[2] == 'In Prep':
                self.assertEquals(len(cols[11]), 0)
            else:
                status = SampleStatus.RECEIVED
                ts_str = cols[11]
                # DA-814 - Participant Summary test status should be: Unset, Received or Disposed only.
                # If sample is disposed, then check disposed timestamp, otherwise check confirmed timestamp.
                # DA-871 - Only check status is disposed when reason code is a bad disposal.
                if cols[2] == 'Disposed' and get_sample_status_enum_value(
                        cols[8]) > SampleStatus.UNKNOWN:
                    status = SampleStatus.DISPOSED
                    ts_str = cols[9]

                ts = datetime.datetime.strptime(ts_str, '%Y/%m/%d %H:%M:%S')
                self._check_summary(participant_ids[x], test_codes[x], ts,
                                    status)
class ParticipantSummaryDaoTest(NdbTestBase):
    def setUp(self):
        super(ParticipantSummaryDaoTest, self).setUp(use_mysql=True)
        self.dao = ParticipantSummaryDao()
        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)
예제 #5
0
class BiobankSamplesPipelineTest(CloudStorageSqlTestBase, NdbTestBase):
    def setUp(self):
        super(BiobankSamplesPipelineTest, self).setUp(use_mysql=True)
        NdbTestBase.doSetUp(self)
        TestBase.setup_fake(self)
        config.override_setting(config.BASELINE_SAMPLE_TEST_CODES,
                                _BASELINE_TESTS)
        # Everything is stored as a list, so override bucket name as a 1-element list.
        config.override_setting(config.BIOBANK_SAMPLES_BUCKET_NAME,
                                [_FAKE_BUCKET])
        self.participant_dao = ParticipantDao()
        self.summary_dao = ParticipantSummaryDao()

    def _write_cloud_csv(self, file_name, contents_str):
        with cloudstorage_api.open('/%s/%s' % (_FAKE_BUCKET, file_name),
                                   mode='w') as cloud_file:
            cloud_file.write(contents_str.encode('utf-8'))

    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.
    """
        participantId = kwargs['participantId']
        modified = datetime.datetime(2019, 03, 25, 15, 59, 30)

        for k, default_value in (
            ('biobankOrderId', u'1'),
            ('created', clock.CLOCK.now()),
                # ('participantId', self.participant.participantId),
            ('sourceSiteId', 1),
            ('sourceUsername', u'*****@*****.**'),
            ('collectedSiteId', 1),
            ('collectedUsername', u'*****@*****.**'),
            ('processedSiteId', 1),
            ('processedUsername', u'*****@*****.**'),
            ('finalizedSiteId', 2),
            ('finalizedUsername', u'*****@*****.**'),
            ('version', 1),
            ('identifiers', [BiobankOrderIdentifier(system=u'a', value=u'c')]),
            ('samples', [
                BiobankOrderedSample(test=u'1SAL2',
                                     description=u'description',
                                     processingRequired=True)
            ]),
            ('dvOrders', [
                BiobankDVOrder(participantId=participantId,
                               modified=modified,
                               version=1)
            ])):
            if k not in kwargs:
                kwargs[k] = default_value
        return BiobankOrder(**kwargs)

    def test_dv_order_sample_update(self):
        """
    Test Biobank Direct Volunteer order
    """
        participant = self.participant_dao.insert(Participant())
        self.summary_dao.insert(self.participant_summary(participant))

        created_ts = datetime.datetime(2019, 03, 22, 18, 30, 45)
        confirmed_ts = datetime.datetime(2019, 03, 23, 12, 13, 00)

        bo = self._make_biobank_order(participantId=participant.participantId)
        BiobankOrderDao().insert(bo)

        boi = bo.identifiers[0]

        bss = BiobankStoredSample(biobankStoredSampleId=u'23523523',
                                  biobankId=participant.biobankId,
                                  test=u'1SAL2',
                                  created=created_ts,
                                  biobankOrderIdentifier=boi.value,
                                  confirmed=confirmed_ts)

        with self.participant_dao.session() as session:
            session.add(bss)

        ps = self.summary_dao.get(participant.participantId)
        self.assertIsNone(ps.sampleStatusDV1SAL2)
        self.assertIsNone(ps.sampleStatusDV1SAL2Time)

        self.summary_dao.update_from_biobank_stored_samples()
        ps = self.summary_dao.get(participant.participantId)
        self.assertEqual(ps.sampleStatus1SAL2, SampleStatus.RECEIVED)
        self.assertEqual(ps.sampleStatus1SAL2Time, confirmed_ts)

    def test_end_to_end(self):
        dao = BiobankStoredSampleDao()
        self.assertEquals(dao.count(), 0)

        # Create 3 participants and pass their (random) IDs into sample rows.
        summary_dao = ParticipantSummaryDao()
        biobank_ids = []
        participant_ids = []
        nids = 16  # equal to the number of parent rows in 'biobank_samples_1.csv'
        cids = 1  # equal to the number of child rows in 'biobank_samples_1.csv'

        for _ in xrange(nids):
            participant = self.participant_dao.insert(Participant())
            summary_dao.insert(self.participant_summary(participant))
            participant_ids.append(participant.participantId)
            biobank_ids.append(participant.biobankId)
            self.assertEquals(
                summary_dao.get(
                    participant.participantId).numBaselineSamplesArrived, 0)

        test_codes = random.sample(_BASELINE_TESTS, nids)
        samples_file = test_data.open_biobank_samples(biobank_ids=biobank_ids,
                                                      tests=test_codes)
        lines = samples_file.split('\n')[1:]  # remove field name line

        input_filename = 'cloud%s.csv' % self._naive_utc_to_naive_central(
            clock.CLOCK.now()).strftime(
                biobank_samples_pipeline.INPUT_CSV_TIME_FORMAT)
        self._write_cloud_csv(input_filename, samples_file)
        biobank_samples_pipeline.upsert_from_latest_csv()

        self.assertEquals(dao.count(), nids - cids)

        for x in range(0, nids):
            cols = lines[x].split('\t')

            if cols[10].strip():  # skip child sample
                continue

            # If status is 'In Prep', then sample confirmed timestamp should be empty
            if cols[2] == 'In Prep':
                self.assertEquals(len(cols[11]), 0)
            else:
                status = SampleStatus.RECEIVED
                ts_str = cols[11]
                # DA-814 - Participant Summary test status should be: Unset, Received or Disposed only.
                # If sample is disposed, then check disposed timestamp, otherwise check confirmed timestamp.
                # DA-871 - Only check status is disposed when reason code is a bad disposal.
                if cols[2] == 'Disposed' and get_sample_status_enum_value(
                        cols[8]) > SampleStatus.UNKNOWN:
                    status = SampleStatus.DISPOSED
                    ts_str = cols[9]

                ts = datetime.datetime.strptime(ts_str, '%Y/%m/%d %H:%M:%S')
                self._check_summary(participant_ids[x], test_codes[x], ts,
                                    status)

    def test_old_csv_not_imported(self):
        now = clock.CLOCK.now()
        too_old_time = now - datetime.timedelta(hours=25)
        input_filename = 'cloud%s.csv' % self._naive_utc_to_naive_central(
            too_old_time).strftime(
                biobank_samples_pipeline.INPUT_CSV_TIME_FORMAT)
        self._write_cloud_csv(input_filename, '')
        with self.assertRaises(biobank_samples_pipeline.DataError):
            biobank_samples_pipeline.upsert_from_latest_csv()

    def _naive_utc_to_naive_central(self, naive_utc_date):
        utc_date = pytz.utc.localize(naive_utc_date)
        central_date = utc_date.astimezone(pytz.timezone('US/Central'))
        return central_date.replace(tzinfo=None)

    def _check_summary(self, participant_id, test, date_formatted, status):
        summary = ParticipantSummaryDao().get(participant_id)
        self.assertEquals(summary.numBaselineSamplesArrived, 1)
        # DA-614 - All specific disposal statuses in biobank_stored_samples are changed to DISPOSED
        # in the participant summary.
        self.assertEquals(status, getattr(summary, 'sampleStatus' + test))
        sample_time = self._naive_utc_to_naive_central(
            getattr(summary, 'sampleStatus' + test + 'Time'))
        self.assertEquals(date_formatted, sample_time)

    def test_find_latest_csv(self):
        # The cloud storage testbed does not expose an injectable time function.
        # Creation time is stored at second granularity.
        self._write_cloud_csv('a_lex_first_created_first.csv', 'any contents')
        time.sleep(1.0)
        self._write_cloud_csv('z_lex_last_created_middle.csv', 'any contents')
        time.sleep(1.0)
        created_last = 'b_lex_middle_created_last.csv'
        self._write_cloud_csv(created_last, 'any contents')
        self._write_cloud_csv(
            '%s/created_last_in_subdir.csv' %
            biobank_samples_pipeline._REPORT_SUBDIR, 'any contents')

        latest_filename = biobank_samples_pipeline._find_latest_samples_csv(
            _FAKE_BUCKET)
        self.assertEquals(latest_filename,
                          '/%s/%s' % (_FAKE_BUCKET, created_last))

    def test_sample_from_row(self):
        samples_file = test_data.open_biobank_samples([112, 222, 333], [])
        reader = csv.DictReader(StringIO.StringIO(samples_file),
                                delimiter='\t')
        row = reader.next()
        sample = biobank_samples_pipeline._create_sample_from_row(
            row, get_biobank_id_prefix())
        self.assertIsNotNone(sample)

        cols = biobank_samples_pipeline.CsvColumns
        self.assertEquals(sample.biobankStoredSampleId, row[cols.SAMPLE_ID])
        self.assertEquals(to_client_biobank_id(sample.biobankId),
                          row[cols.EXTERNAL_PARTICIPANT_ID])
        self.assertEquals(sample.test, row[cols.TEST_CODE])
        confirmed_date = self._naive_utc_to_naive_central(sample.confirmed)
        self.assertEquals(
            confirmed_date.strftime(
                biobank_samples_pipeline._INPUT_TIMESTAMP_FORMAT),
            row[cols.CONFIRMED_DATE])
        received_date = self._naive_utc_to_naive_central(sample.created)
        self.assertEquals(
            received_date.strftime(
                biobank_samples_pipeline._INPUT_TIMESTAMP_FORMAT),
            row[cols.CREATE_DATE])

    def test_sample_from_row_wrong_prefix(self):
        samples_file = test_data.open_biobank_samples([111, 222, 333], [])
        reader = csv.DictReader(StringIO.StringIO(samples_file),
                                delimiter='\t')
        row = reader.next()
        row[biobank_samples_pipeline.CsvColumns.CONFIRMED_DATE] = '2016 11 19'
        self.assertIsNone(
            biobank_samples_pipeline._create_sample_from_row(row, 'Q'))

    def test_sample_from_row_invalid(self):
        samples_file = test_data.open_biobank_samples([111, 222, 333], [])
        reader = csv.DictReader(StringIO.StringIO(samples_file),
                                delimiter='\t')
        row = reader.next()
        row[biobank_samples_pipeline.CsvColumns.CONFIRMED_DATE] = '2016 11 19'
        with self.assertRaises(biobank_samples_pipeline.DataError):
            biobank_samples_pipeline._create_sample_from_row(
                row, get_biobank_id_prefix())

    def test_sample_from_row_old_test(self):
        samples_file = test_data.open_biobank_samples([111, 222, 333], [])
        reader = csv.DictReader(StringIO.StringIO(samples_file),
                                delimiter='\t')
        row = reader.next()
        row[biobank_samples_pipeline.CsvColumns.TEST_CODE] = '2PST8'
        sample = biobank_samples_pipeline._create_sample_from_row(
            row, get_biobank_id_prefix())
        self.assertIsNotNone(sample)
        cols = biobank_samples_pipeline.CsvColumns
        self.assertEquals(sample.biobankStoredSampleId, row[cols.SAMPLE_ID])
        self.assertEquals(sample.test, row[cols.TEST_CODE])

    def test_column_missing(self):
        with open(test_data.data_path(
                'biobank_samples_missing_field.csv')) as samples_file:
            reader = csv.DictReader(samples_file, delimiter='\t')
            with self.assertRaises(biobank_samples_pipeline.DataError):
                biobank_samples_pipeline._upsert_samples_from_csv(reader)

    def test_get_reconciliation_report_paths(self):
        dt = datetime.datetime(2016, 12, 22, 18, 30, 45)
        expected_prefix = 'reconciliation/report_2016-12-22'
        paths = biobank_samples_pipeline._get_report_paths(dt)
        self.assertEquals(len(paths), 4)
        for path in paths:
            self.assertTrue(
                path.startswith(expected_prefix),
                'Report path %r must start with %r.' % (expected_prefix, path))
            self.assertTrue(path.endswith('.csv'))
예제 #6
0
class ParticipantDaoTest(SqlTestBase):
    def setUp(self):
        super(ParticipantDaoTest, self).setUp()
        self.dao = ParticipantDao()
        self.participant_summary_dao = ParticipantSummaryDao()
        self.participant_history_dao = ParticipantHistoryDao()

    def test_get_before_insert(self):
        self.assertIsNone(self.dao.get(1))
        self.assertIsNone(self.participant_summary_dao.get(1))
        self.assertIsNone(self.participant_history_dao.get([1, 1]))

    def test_insert(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=1,
            biobankId=2,
            lastModified=time,
            signUpTime=time)
        self.assertEquals(expected_participant.asdict(), p.asdict())

        p2 = self.dao.get(1)
        self.assertEquals(p.asdict(), p2.asdict())

        # Creating a participant also creates a ParticipantHistory row, but not a ParticipantSummary row
        ps = self.participant_summary_dao.get(1)
        self.assertIsNone(ps)
        ph = self.participant_history_dao.get([1, 1])
        expected_ph = self._participant_history_with_defaults(
            participantId=1, biobankId=2, lastModified=time, signUpTime=time)
        self.assertEquals(expected_ph.asdict(), ph.asdict())

    def test_insert_with_external_id(self):
        p = Participant(externalId=3)
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            externalId=3,
            version=1,
            biobankId=2,
            lastModified=time,
            signUpTime=time)
        self.assertEquals(expected_participant.asdict(), p.asdict())

        p2 = self.dao.get(1)
        self.assertEquals(p.asdict(), p2.asdict())

        # Creating a participant also creates a ParticipantHistory row, but not a ParticipantSummary row
        ps = self.participant_summary_dao.get(1)
        self.assertIsNone(ps)
        ph = self.participant_history_dao.get([1, 1])
        expected_ph = self._participant_history_with_defaults(
            participantId=1,
            externalId=3,
            biobankId=2,
            lastModified=time,
            signUpTime=time)
        self.assertEquals(expected_ph.asdict(), ph.asdict())

    def test_insert_duplicate_participant_id_retry(self):
        p = Participant()
        with random_ids([1, 2]):
            self.dao.insert(p)
        p2 = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 3, 2, 3]):
            with FakeClock(time):
                p2 = self.dao.insert(p2)
        expected_participant = self._participant_with_defaults(
            participantId=2,
            version=1,
            biobankId=3,
            lastModified=time,
            signUpTime=time)
        self.assertEquals(expected_participant.asdict(), p2.asdict())

    def test_insert_duplicate_participant_id_give_up(self):
        p = Participant()
        with random_ids([1, 2]):
            self.dao.insert(p)
        rand_ints = []
        for i in range(0, MAX_INSERT_ATTEMPTS):
            rand_ints.append(1)
            rand_ints.append(i)
        p2 = Participant()
        with random_ids(rand_ints):
            with self.assertRaises(ServiceUnavailable):
                self.dao.insert(p2)

    def test_insert_duplicate_biobank_id_give_up(self):
        p = Participant()
        with random_ids([1, 2]):
            self.dao.insert(p)
        rand_ints = []
        for i in range(0, MAX_INSERT_ATTEMPTS):
            rand_ints.append(i + 2)
            rand_ints.append(2)
        p2 = Participant()
        with random_ids(rand_ints):
            with self.assertRaises(ServiceUnavailable):
                self.dao.insert(p2)

    def test_update_no_expected_version_no_ps(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)

        p.providerLink = make_primary_provider_link_for_name('PITT')
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            self.dao.update(p)
        # lastModified, hpoId, version is updated on p after being passed in
        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_participant.asdict(), p2.asdict())
        self.assertEquals(p.asdict(), p2.asdict())

        ps = self.participant_summary_dao.get(1)
        self.assertIsNone(ps)

        expected_ph = self._participant_history_with_defaults(
            participantId=1, biobankId=2, lastModified=time, signUpTime=time)
        # Updating the participant adds a new ParticipantHistory row.
        ph = self.participant_history_dao.get([1, 1])
        self.assertEquals(expected_ph.asdict(), ph.asdict())
        ph2 = self.participant_history_dao.get([1, 2])
        expected_ph2 = self._participant_history_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_ph2.asdict(), ph2.asdict())

    def test_update_no_expected_version_with_ps(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)
        p.providerLink = make_primary_provider_link_for_name('PITT')
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            self.dao.update(p)

        summary = self.participant_summary(p)
        self.participant_summary_dao.insert(summary)

        # lastModified, hpoId, version is updated on p after being passed in
        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_participant.asdict(), p2.asdict())
        self.assertEquals(p.asdict(), p2.asdict())

        # Updating the participant provider link also updates the HPO ID on the participant summary.
        ps = self.participant_summary_dao.get(1)
        expected_ps = self._participant_summary_with_defaults(
            participantId=1,
            biobankId=2,
            signUpTime=time,
            hpoId=PITT_HPO_ID,
            lastModified=time2,
            firstName=summary.firstName,
            lastName=summary.lastName,
            email=summary.email)
        self.assertEquals(expected_ps.asdict(), ps.asdict())

        p2_last_modified = p2.lastModified
        p2.hpoId = 2
        self.dao.update(p2)
        p2_update = self.dao.get(1)
        self.assertNotEquals(p2_last_modified, p2_update.lastModified)
        self.assertEquals(p2_update.lastModified, p2.lastModified)

        expected_ph = self._participant_history_with_defaults(
            participantId=1, biobankId=2, lastModified=time, signUpTime=time)
        # And updating the participant adds a new ParticipantHistory row.
        ph = self.participant_history_dao.get([1, 1])
        self.assertEquals(expected_ph.asdict(), ph.asdict())
        ph2 = self.participant_history_dao.get([1, 2])
        expected_ph2 = self._participant_history_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_ph2.asdict(), ph2.asdict())

    def test_update_right_expected_version(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)
        p.version = 1
        p.providerLink = make_primary_provider_link_for_name('PITT')
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            self.dao.update(p)

        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_participant.asdict(), p2.asdict())

    def test_update_withdraw(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)
        p.version = 1
        p.withdrawalStatus = WithdrawalStatus.NO_USE
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            self.dao.update(p)

        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            withdrawalStatus=WithdrawalStatus.NO_USE,
            withdrawalTime=time2)
        self.assertEquals(expected_participant.asdict(), p2.asdict())

        p.version = 2
        p.providerLink = make_primary_provider_link_for_name('PITT')
        p.withdrawalTime = None
        time3 = datetime.datetime(2016, 1, 3)
        with FakeClock(time3):
            self.dao.update(p)

        # Withdrawal time should get copied over.
        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=3,
            biobankId=2,
            lastModified=time3,
            signUpTime=time,
            withdrawalStatus=WithdrawalStatus.NO_USE,
            withdrawalTime=time2,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_participant.asdict(), p2.asdict())

    def test_update_suspend(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)
        p.version = 1
        p.suspensionStatus = SuspensionStatus.NO_CONTACT
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            self.dao.update(p)

        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=2,
            biobankId=2,
            lastModified=time2,
            signUpTime=time,
            suspensionStatus=SuspensionStatus.NO_CONTACT,
            suspensionTime=time2)
        self.assertEquals(expected_participant.asdict(), p2.asdict())

        p.version = 2
        p.providerLink = make_primary_provider_link_for_name('PITT')
        p.suspensionTime = None
        time3 = datetime.datetime(2016, 1, 3)
        with FakeClock(time3):
            self.dao.update(p)

        # Withdrawal time should get copied over.
        p2 = self.dao.get(1)
        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=3,
            biobankId=2,
            lastModified=time3,
            signUpTime=time,
            suspensionStatus=SuspensionStatus.NO_CONTACT,
            suspensionTime=time2,
            hpoId=PITT_HPO_ID,
            providerLink=p2.providerLink)
        self.assertEquals(expected_participant.asdict(), p2.asdict())

    def test_update_wrong_expected_version(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)

        p.version = 2
        p.providerLink = make_primary_provider_link_for_name('PITT')
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            with self.assertRaises(PreconditionFailed):
                self.dao.update(p)

    def test_update_withdrawn_hpo_succeeds(self):
        p = Participant(withdrawalStatus=WithdrawalStatus.NO_USE)
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)

        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=1,
            biobankId=2,
            lastModified=time,
            signUpTime=time,
            withdrawalStatus=WithdrawalStatus.NO_USE)
        self.assertEquals(expected_participant.asdict(), p.asdict())

        p2 = self.dao.get(1)
        self.assertEquals(p.asdict(), p2.asdict())

        p.version = 1
        p.providerLink = make_primary_provider_link_for_name('PITT')
        self.dao.update(p)

    def test_update_withdrawn_status_fails(self):
        p = Participant(withdrawalStatus=WithdrawalStatus.NO_USE)
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)

        expected_participant = self._participant_with_defaults(
            participantId=1,
            version=1,
            biobankId=2,
            lastModified=time,
            signUpTime=time,
            withdrawalStatus=WithdrawalStatus.NO_USE)
        self.assertEquals(expected_participant.asdict(), p.asdict())

        p2 = self.dao.get(1)
        self.assertEquals(p.asdict(), p2.asdict())

        p.version = 1
        p.withdrawalStatus = WithdrawalStatus.NOT_WITHDRAWN
        with self.assertRaises(Forbidden):
            self.dao.update(p)

    def test_update_not_exists(self):
        p = self._participant_with_defaults(participantId=1, biobankId=2)
        with self.assertRaises(NotFound):
            self.dao.update(p)

    def test_bad_hpo_insert(self):
        p = Participant(
            participantId=1,
            version=1,
            biobankId=2,
            providerLink=make_primary_provider_link_for_name('FOO'))
        with self.assertRaises(BadRequest):
            self.dao.insert(p)

    def test_bad_hpo_update(self):
        p = Participant(participantId=1, biobankId=2)
        time = datetime.datetime(2016, 1, 1)
        with FakeClock(time):
            self.dao.insert(p)

        p.providerLink = make_primary_provider_link_for_name('FOO')
        with self.assertRaises(BadRequest):
            self.dao.update(p)

    def test_pairs_unset(self):
        participant_id = 22
        self.dao.insert(Participant(participantId=participant_id, biobankId=2))
        refetched = self.dao.get(participant_id)
        self.assertEquals(refetched.hpoId, UNSET_HPO_ID)  # sanity check
        self.participant_summary_dao.insert(
            self.participant_summary(refetched))

        with self.dao.session() as session:
            self.dao.add_missing_hpo_from_site(session, participant_id,
                                               self._test_db.site_id)

        paired = self.dao.get(participant_id)
        self.assertEquals(paired.hpoId, self._test_db.hpo_id)
        self.assertEquals(
            paired.providerLink,
            make_primary_provider_link_for_id(self._test_db.hpo_id))
        self.assertEquals(
            self.participant_summary_dao.get(participant_id).hpoId,
            self._test_db.hpo_id)
        self.assertEquals(paired.organizationId, self._test_db.organization_id)
        self.assertEquals(paired.siteId, self._test_db.site_id)

    def test_overwrite_existing_pairing(self):
        participant_id = 99
        created = self.dao.insert(
            Participant(participantId=participant_id,
                        biobankId=2,
                        hpoId=self._test_db.hpo_id,
                        providerLink=make_primary_provider_link_for_id(
                            self._test_db.hpo_id)))
        self.participant_summary_dao.insert(self.participant_summary(created))
        self.assertEquals(created.hpoId, self._test_db.hpo_id)  # sanity check

        other_hpo = HPODao().insert(
            HPO(hpoId=PITT_HPO_ID + 1, name='DIFFERENT_HPO'))
        other_site = SiteDao().insert(
            Site(hpoId=other_hpo.hpoId,
                 siteName='Arbitrary Site',
                 googleGroup='*****@*****.**'))

        with self.dao.session() as session:
            self.dao.add_missing_hpo_from_site(session, participant_id,
                                               other_site.siteId)

        # Original Participant + summary is affected.
        refetched = self.dao.get(participant_id)

        self.assertEquals(refetched.hpoId, other_hpo.hpoId)
        self.assertEquals(refetched.providerLink,
                          make_primary_provider_link_for_id(other_hpo.hpoId))
        self.assertEquals(
            self.participant_summary_dao.get(participant_id).hpoId,
            other_hpo.hpoId)

    def test_pairing_at_different_levels(self):
        p = Participant()
        time = datetime.datetime(2016, 1, 1)
        with random_ids([1, 2]):
            with FakeClock(time):
                self.dao.insert(p)

        p.version = 1
        p.siteId = 1
        time2 = datetime.datetime(2016, 1, 2)
        with FakeClock(time2):
            self.dao.update(p)

        p2 = self.dao.get(1)
        ep = self._participant_with_defaults(participantId=1,
                                             version=2,
                                             biobankId=2,
                                             lastModified=time2,
                                             signUpTime=time,
                                             hpoId=PITT_HPO_ID,
                                             siteId=1,
                                             organizationId=PITT_ORG_ID,
                                             providerLink=p2.providerLink)
        self.assertEquals(ep.siteId, p2.siteId)
        # ensure that p2 get paired with expected awardee and organization from update().
        self.assertEquals(ep.hpoId, p2.hpoId)
        self.assertEquals(ep.organizationId, p2.organizationId)
예제 #7
0
class SiteDaoTest(SqlTestBase):
    def setUp(self):
        super(SiteDaoTest, self).setUp()
        self.site_dao = SiteDao()
        self.participant_dao = ParticipantDao()
        self.ps_dao = ParticipantSummaryDao()
        self.ps_history = ParticipantHistoryDao()

    def test_get_no_sites(self):
        self.assertIsNone(self.site_dao.get(9999))
        self.assertIsNone(
            self.site_dao.get_by_google_group('*****@*****.**'))

    def test_insert(self):
        site = Site(siteName='site',
                    googleGroup='*****@*****.**',
                    mayolinkClientNumber=12345,
                    hpoId=PITT_HPO_ID)
        created_site = self.site_dao.insert(site)
        new_site = self.site_dao.get(created_site.siteId)
        site.siteId = created_site.siteId
        self.assertEquals(site.asdict(), new_site.asdict())
        self.assertEquals(
            site.asdict(),
            self.site_dao.get_by_google_group(
                '*****@*****.**').asdict())

    def test_update(self):
        site = Site(siteName='site',
                    googleGroup='*****@*****.**',
                    mayolinkClientNumber=12345,
                    hpoId=PITT_HPO_ID)
        created_site = self.site_dao.insert(site)
        new_site = Site(siteId=created_site.siteId,
                        siteName='site2',
                        googleGroup='*****@*****.**',
                        mayolinkClientNumber=123456,
                        hpoId=UNSET_HPO_ID)
        self.site_dao.update(new_site)
        fetched_site = self.site_dao.get(created_site.siteId)
        self.assertEquals(new_site.asdict(), fetched_site.asdict())
        self.assertEquals(
            new_site.asdict(),
            self.site_dao.get_by_google_group(
                '*****@*****.**').asdict())
        self.assertIsNone(
            self.site_dao.get_by_google_group('*****@*****.**'))

    def test_participant_pairing_updates_on_change(self):
        TIME = datetime.datetime(2018, 1, 1)
        TIME2 = datetime.datetime(2018, 1, 2)
        provider_link = '[{"organization": {"reference": "Organization/AZ_TUCSON"}, "primary": true}]'
        site = Site(siteName='site',
                    googleGroup='*****@*****.**',
                    mayolinkClientNumber=12345,
                    hpoId=PITT_HPO_ID,
                    organizationId=PITT_ORG_ID)
        created_site = self.site_dao.insert(site)

        with FakeClock(TIME):
            p = Participant(participantId=1,
                            biobankId=2,
                            siteId=created_site.siteId)
            self.participant_dao.insert(p)
            fetch_p = self.participant_dao.get(p.participantId)
            updated_p = self.participant_dao.get(fetch_p.participantId)
            p_summary = self.ps_dao.insert(self.participant_summary(updated_p))

        with FakeClock(TIME2):
            update_site_parent = Site(siteId=created_site.siteId,
                                      siteName='site2',
                                      googleGroup='*****@*****.**',
                                      mayolinkClientNumber=123456,
                                      hpoId=AZ_HPO_ID,
                                      organizationId=AZ_ORG_ID)
            self.site_dao.update(update_site_parent)

        updated_p = self.participant_dao.get(fetch_p.participantId)
        ps = self.ps_dao.get(p_summary.participantId)
        ph = self.ps_history.get([updated_p.participantId, 1])

        self.assertEquals(update_site_parent.hpoId, updated_p.hpoId)
        self.assertEquals(update_site_parent.organizationId,
                          updated_p.organizationId)
        self.assertEquals(ps.organizationId, update_site_parent.organizationId)
        self.assertEquals(ps.hpoId, update_site_parent.hpoId)
        self.assertEquals(ps.organizationId, update_site_parent.organizationId)
        self.assertEquals(ph.organizationId, update_site_parent.organizationId)
        self.assertEquals(updated_p.providerLink, provider_link)
        self.assertEquals(ps.lastModified, TIME2)
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)
예제 #9
0
class BiobankOrderApiTest(FlaskTestBase):
    def setUp(self):
        super(BiobankOrderApiTest, self).setUp(use_mysql=True)
        self.participant = Participant(participantId=123, biobankId=555)
        self.participant_dao = ParticipantDao()
        self.participant_dao.insert(self.participant)
        self.summary_dao = ParticipantSummaryDao()
        self.bio_dao = BiobankOrderDao()
        self.path = ('Participant/%s/BiobankOrder' %
                     to_client_participant_id(self.participant.participantId))

    def test_cancel_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)
        full_order_json = load_biobank_order_json(
            self.participant.participantId, filename='biobank_order_1.json')
        _strip_fields(result)
        _strip_fields(full_order_json)
        self.assertEquals(full_order_json, result)

        biobank_order_id = result['identifier'][1]['value']
        path = self.path + '/' + biobank_order_id
        request_data = {
            "amendedReason": "Its all wrong",
            "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"
        }
        cancelled_order = self.send_patch(path,
                                          request_data=request_data,
                                          headers={'If-Match': 'W/"1"'})
        get_cancelled_order = self.send_get(path)
        get_summary = self.summary_dao.get(self.participant.participantId)

        self.assertEqual(get_summary.biospecimenSourceSiteId, None)
        self.assertEqual(get_summary.biospecimenCollectedSiteId, None)
        self.assertEqual(get_summary.biospecimenOrderTime, None)
        self.assertEqual(get_summary.biospecimenStatus, None)
        self.assertEqual(get_summary.biospecimenFinalizedSiteId, None)
        self.assertEqual(get_summary.biospecimenProcessedSiteId, None)
        self.assertEqual(get_summary.sampleOrderStatus2ED10, None)
        self.assertEqual(get_summary.sampleOrderStatus2ED10Time, None)
        self.assertEqual(get_summary.sampleStatus2ED10, None)
        self.assertEqual(get_summary.sampleStatus2ED10Time, None)
        self.assertEqual(get_summary.sampleOrderStatus1PST8, None)
        self.assertEqual(get_summary.sampleOrderStatus1PST8Time, None)
        self.assertEqual(get_summary.sampleStatus1PST8, None)
        self.assertEqual(get_summary.sampleStatus1PST8Time, None)
        self.assertEqual(get_summary.sampleOrderStatus1PS08, None)
        self.assertEqual(get_summary.sampleOrderStatus1PS08Time, None)
        self.assertEqual(get_summary.sampleStatus1PS08, None)
        self.assertEqual(get_summary.sampleStatus1PS08Time, None)
        self.assertEqual(get_summary.sampleOrderStatus2PST8, None)
        self.assertEqual(get_summary.sampleOrderStatus2PST8Time, None)
        self.assertEqual(get_summary.sampleStatus2PST8, None)
        self.assertEqual(get_summary.sampleStatus2PST8Time, None)
        self.assertEqual(get_summary.sampleOrderStatus1PXR2, None)
        self.assertEqual(get_summary.sampleOrderStatus1PXR2Time, None)
        self.assertEqual(get_summary.sampleStatus1PXR2, None)
        self.assertEqual(get_summary.sampleStatus1PXR2Time, None)
        self.assertEqual(get_summary.sampleOrderStatus1CFD9, None)
        self.assertEqual(get_summary.sampleOrderStatus1CFD9Time, None)
        self.assertEqual(get_summary.sampleStatus1CFD9, None)
        self.assertEqual(get_summary.sampleStatus1CFD9Time, None)
        self.assertEqual(get_summary.sampleOrderStatus1ED02, None)
        self.assertEqual(get_summary.sampleOrderStatus1ED02Time, None)
        self.assertEqual(get_summary.sampleStatus1ED02, None)
        self.assertEqual(get_summary.sampleStatus1ED02Time, None)
        self.assertEqual(cancelled_order, get_cancelled_order)
        self.assertEqual(get_cancelled_order['status'], 'CANCELLED')
        self.assertEqual(get_cancelled_order['amendedReason'], 'Its all wrong')
        self.assertEqual(
            get_cancelled_order['cancelledInfo']['author']['value'],
            '*****@*****.**')
        self.assertEqual(get_cancelled_order['cancelledInfo']['site']['value'],
                         'hpo-site-monroeville')

    def test_cancel_one_order_with_another_good_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename="biobank_order_1.json")
        order_json2 = load_biobank_order_json(self.participant.participantId,
                                              filename="biobank_order_2.json")
        order_json2['identifier'][0]['value'] = 'healthpro-order-id-1231234'
        order_json2['identifier'][1]['value'] = 'WEB1YLHV1234'
        result = self.send_post(self.path, order_json)
        self.send_post(self.path, order_json2)

        biobank_order_id = result["identifier"][1]["value"]
        path = self.path + "/" + biobank_order_id
        request_data = {
            "amendedReason": "Its all wrong",
            "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",
        }
        self.send_patch(path,
                        request_data=request_data,
                        headers={"If-Match": 'W/"1"'})

        get_summary = self.summary_dao.get(self.participant.participantId)

        self.assertEqual(get_summary.biospecimenSourceSiteId, 1)
        self.assertEqual(get_summary.biospecimenCollectedSiteId, 1)
        self.assertEqual(get_summary.biospecimenFinalizedSiteId, 2)

    def test_you_can_not_cancel_a_cancelled_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)

        biobank_order_id = result['identifier'][1]['value']
        path = self.path + '/' + biobank_order_id
        request_data = {
            "amendedReason": "Its all wrong",
            "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"
        }
        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"1"'})

        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"2"'},
                        expected_status=httplib.BAD_REQUEST)

    def test_you_can_not_restore_a_not_cancelled_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)

        biobank_order_id = result['identifier'][1]['value']
        path = self.path + '/' + biobank_order_id
        request_data = {
            "amendedReason": "Its all wrong",
            "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"
        }
        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"1"'},
                        expected_status=httplib.BAD_REQUEST)

    def test_restore_an_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)
        full_order_json = load_biobank_order_json(
            self.participant.participantId, filename='biobank_order_1.json')
        _strip_fields(result)
        _strip_fields(full_order_json)
        self.assertEquals(full_order_json, result)

        biobank_order_id = result['identifier'][1]['value']
        path = self.path + '/' + biobank_order_id
        request_data = {
            "amendedReason": "Its all wrong",
            "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"
        }
        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"1"'})

        request_data = {
            "amendedReason": "I didnt mean to cancel",
            "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"
        }

        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"2"'})
        restored_order = self.send_get(path)
        get_summary = self.summary_dao.get(self.participant.participantId)
        self.assertEqual(get_summary.sampleOrderStatus1SST8,
                         OrderStatus.CREATED)
        self.assertEqual(get_summary.sampleOrderStatus2ED10,
                         OrderStatus.CREATED)
        self.assertEqual(get_summary.sampleOrderStatus1SAL,
                         OrderStatus.CREATED)
        self.assertEqual(get_summary.sampleOrderStatus1UR10,
                         OrderStatus.CREATED)
        self.assertEqual(get_summary.sampleOrderStatus1CFD9,
                         OrderStatus.FINALIZED)
        self.assertEqual(get_summary.sampleOrderStatus1ED02,
                         OrderStatus.FINALIZED)
        self.assertEqual(get_summary.sampleOrderStatus2SST8,
                         OrderStatus.FINALIZED)
        self.assertEqual(get_summary.sampleOrderStatus2PST8,
                         OrderStatus.FINALIZED)
        self.assertEqual(restored_order['status'], 'UNSET')
        self.assertEqual(restored_order['restoredInfo']['author']['value'],
                         '*****@*****.**')
        self.assertEqual(restored_order['restoredInfo']['site']['value'],
                         'hpo-site-monroeville')
        self.assertEqual(restored_order['amendedReason'],
                         'I didnt mean to cancel')

    def test_amending_an_order(self):
        # pylint: disable=unused-variable
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)

        biobank_order_id = result['identifier'][1]['value']
        path = self.path + '/' + biobank_order_id
        request_data = {
            "amendedReason": "Its all better",
            "amendedInfo": {
                "author": {
                    "system": "https://www.pmi-ops.org/healthpro-username",
                    "value": "*****@*****.**"
                },
                "site": {
                    "system": "https://www.pmi-ops.org/site-id",
                    "value": "hpo-site-bannerphoenix"
                }
            }
        }

        biobank_order_identifiers = {
            "created": "2018-02-21T16:25:12",
            "createdInfo": {
                "author": {
                    "system": "https://www.pmi-ops.org/healthpro-username",
                    "value": "*****@*****.**"
                },
                "site": {
                    "system": "https://www.pmi-ops.org/site-id",
                    "value": "hpo-site-clinic-phoenix"
                }
            }
        }
        get_order = self.send_get(path)
        full_order = get_order.copy()
        full_order.update(request_data)
        full_order.update(biobank_order_identifiers)

        self.assertEqual(len(full_order['samples']), 16)
        del full_order['samples'][0]

        self.send_put(path,
                      request_data=full_order,
                      headers={'If-Match': 'W/"1"'})

        get_amended_order = self.send_get(path)
        get_summary = self.summary_dao.get(self.participant.participantId)
        self.assertEqual(get_summary.biospecimenProcessedSiteId, 1)
        self.assertEqual(get_summary.biospecimenFinalizedSiteId, 2)
        self.assertEqual(get_summary.biospecimenCollectedSiteId, 1)
        self.assertEqual(get_summary.sampleOrderStatus2PST8,
                         OrderStatus.FINALIZED)
        self.assertEqual(get_summary.sampleOrderStatus1PS08,
                         OrderStatus.FINALIZED)
        self.assertEqual(get_summary.sampleOrderStatus1PST8,
                         OrderStatus.FINALIZED)
        self.assertEqual(get_summary.sampleOrderStatus1SST8,
                         OrderStatus.CREATED)
        self.assertEqual(get_summary.sampleOrderStatus2ED10,
                         OrderStatus.CREATED)
        self.assertEqual(len(get_amended_order['samples']), 15)
        self.assertEqual(get_amended_order['meta'], {'versionId': 'W/"2"'})
        self.assertEqual(get_amended_order['amendedReason'], 'Its all better')
        self.assertEqual(get_amended_order['amendedInfo']['author']['value'],
                         '*****@*****.**')
        self.assertEqual(get_amended_order['amendedInfo']['site']['value'],
                         'hpo-site-bannerphoenix')
        self.assertEqual(get_amended_order['createdInfo']['site']['value'],
                         'hpo-site-clinic-phoenix')
        self.assertEqual(get_amended_order['createdInfo']['author']['value'],
                         '*****@*****.**')
        self.assertEqual(get_amended_order['created'], "2018-02-21T16:25:12")
        self.assertEqual(get_amended_order['status'], "AMENDED")

    def test_amend_a_restored_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)
        full_order_json = load_biobank_order_json(
            self.participant.participantId, filename='biobank_order_1.json')
        _strip_fields(result)
        _strip_fields(full_order_json)

        biobank_order_id = result['identifier'][1]['value']
        path = self.path + '/' + biobank_order_id
        request_data = {
            "amendedReason": "Its all wrong",
            "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"
        }
        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"1"'})
        self.send_get(path)
        request_data = {
            "amendedReason": "I didnt mean to cancel",
            "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"
        }

        self.send_patch(path,
                        request_data=request_data,
                        headers={'If-Match': 'W/"2"'})

        request_data = {
            "amendedReason":
            "Its all better",
            "samples": [{
                "test": "1ED10",
                "description": "EDTA 10 mL (1)",
                "processingRequired": False,
                "collected": "2016-01-04T09:45:49Z",
                "finalized": "2016-01-04T10:55:41Z"
            }, {
                "test": "1PST8",
                "description": "Plasma Separator 8 mL",
                "collected": "2016-01-04T09:45:49Z",
                "processingRequired": True,
                "processed": "2016-01-04T10:28:50Z",
                "finalized": "2016-01-04T10:55:41Z"
            }],
            "amendedInfo": {
                "author": {
                    "system": "https://www.pmi-ops.org/healthpro-username",
                    "value": "*****@*****.**"
                },
                "site": {
                    "system": "https://www.pmi-ops.org/site-id",
                    "value": "hpo-site-monroeville"
                }
            }
        }
        get_order = self.send_get(path)
        full_order = get_order.copy()
        full_order.update(request_data)
        self.send_put(path,
                      request_data=full_order,
                      headers={'If-Match': 'W/"3"'})

        get_amended_order = self.send_get(path)
        self.assertEqual(len(get_amended_order['samples']), 2)
        self.assertEqual(get_amended_order['amendedInfo']['author']['value'],
                         '*****@*****.**')
        self.assertEqual(get_amended_order['status'], 'AMENDED')
        self.assertEqual(get_amended_order.get('restoredSiteId'), None)
        self.assertEqual(get_amended_order.get('restoredUsername'), None)
        self.assertEqual(get_amended_order.get('restoredTime'), None)
        self.assertEqual(get_amended_order['meta'], {'versionId': 'W/"4"'})

    def test_insert_and_refetch(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        self.create_and_verify_created_obj(
            self.path, load_biobank_order_json(self.participant.participantId))

    def test_insert_new_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)
        full_order_json = load_biobank_order_json(
            self.participant.participantId, filename='biobank_order_1.json')
        _strip_fields(result)
        _strip_fields(full_order_json)
        self.assertEquals(full_order_json, result)

    def test_biobank_history_on_insert(self):
        with self.bio_dao.session() as session:
            self.summary_dao.insert(self.participant_summary(self.participant))
            order_json = load_biobank_order_json(
                self.participant.participantId,
                filename='biobank_order_2.json')
            result = self.send_post(self.path, order_json)
            load_biobank_order_json(self.participant.participantId,
                                    filename='biobank_order_1.json')
            order_history = session.query(BiobankOrderHistory).first()
            identifier_history = session.query(
                BiobankOrderIdentifierHistory).first()
            sample_history = session.query(BiobankOrderedSampleHistory).first()

            self.assertEqual(result['id'], order_history.biobankOrderId)
            self.assertEqual(identifier_history.biobankOrderId, result['id'])
            self.assertEqual(sample_history.biobankOrderId, result['id'])
            self.assertEqual(result['meta']['versionId'], 'W/"1"')
            self.assertEqual(order_history.version, 1)

            # Test history on updates...
            biobank_order_id = result['identifier'][1]['value']
            path = self.path + '/' + biobank_order_id
            request_data = {
                "amendedReason": "Its all better",
                "amendedInfo": {
                    "author": {
                        "system": "https://www.pmi-ops.org/healthpro-username",
                        "value": "*****@*****.**"
                    },
                    "site": {
                        "system": "https://www.pmi-ops.org/site-id",
                        "value": "hpo-site-bannerphoenix"
                    }
                }
            }

            biobank_order_identifiers = {
                "created": "2018-02-21T16:25:12",
                "createdInfo": {
                    "author": {
                        "system": "https://www.pmi-ops.org/healthpro-username",
                        "value": "*****@*****.**"
                    },
                    "site": {
                        "system": "https://www.pmi-ops.org/site-id",
                        "value": "hpo-site-clinic-phoenix"
                    }
                }
            }
            get_order = self.send_get(path)
            full_order = get_order.copy()
            full_order.update(request_data)
            full_order.update(biobank_order_identifiers)

            self.assertEqual(len(full_order['samples']), 16)
            del full_order['samples'][0]

            self.send_put(path,
                          request_data=full_order,
                          headers={'If-Match': 'W/"1"'})

            with self.bio_dao.session() as session:
                amended_order = self.send_get(path)
                second_order_history = session.query(
                    BiobankOrderHistory).filter_by(version=2).first()
                second_order_samples = session.query(
                    BiobankOrderedSampleHistory).filter_by(version=2).first()
                second_order_identifier = session.query(BiobankOrderIdentifierHistory).filter_by(version=2)\
                                                                                                 .first()
                self.assertEqual(second_order_history.biobankOrderId,
                                 amended_order['id'])
                self.assertEqual(second_order_identifier.biobankOrderId,
                                 amended_order['id'])
                self.assertEqual(second_order_samples.biobankOrderId,
                                 amended_order['id'])

                # Check that original order hasn't changed in history
                original = session.query(BiobankOrderHistory).filter_by(
                    version=1).first()
                self.assertEqual(original.asdict(), order_history.asdict())

    def test_error_no_summary(self):
        order_json = load_biobank_order_json(self.participant.participantId)
        self.send_post(self.path,
                       order_json,
                       expected_status=httplib.BAD_REQUEST)

    def test_error_missing_required_fields(self):
        order_json = load_biobank_order_json(self.participant.participantId)
        del order_json['identifier']
        self.send_post(self.path,
                       order_json,
                       expected_status=httplib.BAD_REQUEST)

    def test_no_duplicate_test_within_order(self):
        order_json = load_biobank_order_json(self.participant.participantId)
        order_json['samples'].extend(list(order_json['samples']))
        self.send_post(self.path,
                       order_json,
                       expected_status=httplib.BAD_REQUEST)

    def test_auto_pair_updates_participant_and_summary(self):
        self.summary_dao.insert(self.participant_summary(self.participant))

        # Sanity check: No HPO yet.
        p_unpaired = self.participant_dao.get(self.participant.participantId)
        self.assertEquals(p_unpaired.hpoId, UNSET_HPO_ID)
        self.assertIsNone(p_unpaired.providerLink)
        s_unpaired = self.summary_dao.get(self.participant.participantId)
        self.assertEquals(s_unpaired.hpoId, UNSET_HPO_ID)

        self.send_post(self.path,
                       load_biobank_order_json(self.participant.participantId))

        # Some HPO has been set. (ParticipantDao tests cover more detailed cases / specific values.)
        p_paired = self.participant_dao.get(self.participant.participantId)
        self.assertNotEqual(p_paired.hpoId, UNSET_HPO_ID)
        self.assertIsNotNone(p_paired.providerLink)

        s_paired = self.summary_dao.get(self.participant.participantId)

        self.assertNotEqual(s_paired.hpoId, UNSET_HPO_ID)
        self.assertEqual(s_paired.biospecimenCollectedSiteId, s_paired.siteId)
        self.assertNotEqual(s_paired.biospecimenCollectedSiteId,
                            s_paired.biospecimenFinalizedSiteId)

        self.assertNotEqual(s_paired.siteId,
                            s_paired.physicalMeasurementsCreatedSiteId)
        self.assertNotEqual(s_paired.siteId,
                            s_paired.physicalMeasurementsFinalizedSiteId)

    def test_not_pairing_at_pm_when_has_bio(self):
        self.participant_id = self.create_participant()
        _id = int(self.participant_id[1:])
        self.path = ('Participant/%s/BiobankOrder' %
                     to_client_participant_id(_id))
        pid_numeric = from_client_participant_id(self.participant_id)
        self.send_consent(self.participant_id)
        self.send_post(self.path, load_biobank_order_json(pid_numeric))
        participant_paired = self.summary_dao.get(pid_numeric)

        self.assertEqual(participant_paired.siteId,
                         participant_paired.biospecimenCollectedSiteId)
        self.path = ('Participant/%s/PhysicalMeasurements' %
                     to_client_participant_id(pid_numeric))
        self._insert_measurements(datetime.datetime.utcnow().isoformat())
        self.assertNotEqual(
            participant_paired.siteId,
            participant_paired.physicalMeasurementsFinalizedSiteId)

    def test_bio_after_cancelled_pm(self):
        self.participant_id = self.create_participant()
        self.send_consent(self.participant_id)
        measurement = load_measurement_json(self.participant_id)
        measurement2 = load_measurement_json(self.participant_id)

        # send both PM's
        pm_path = 'Participant/%s/PhysicalMeasurements' % self.participant_id
        response = self.send_post(pm_path, measurement)
        self.send_post(pm_path, measurement2)

        # cancel the 1st PM
        pm_path = pm_path + '/' + response['id']
        cancel_info = get_restore_or_cancel_info()
        self.send_patch(pm_path, cancel_info)

        # set up questionnaires to hit the calculate_max_core_sample_time in participant summary
        questionnaire_id = self.create_questionnaire('questionnaire3.json')
        questionnaire_id_1 = self.create_questionnaire(
            'all_consents_questionnaire.json')
        questionnaire_id_2 = self.create_questionnaire('questionnaire4.json')
        self._submit_consent_questionnaire_response(
            self.participant_id,
            questionnaire_id_1,
            CONSENT_PERMISSION_YES_CODE,
            time=TIME_6)

        self.submit_questionnaire_response(self.participant_id,
                                           questionnaire_id,
                                           RACE_NONE_OF_THESE_CODE, None, None,
                                           datetime.date(1978, 10, 10))

        self._submit_empty_questionnaire_response(self.participant_id,
                                                  questionnaire_id_2)

        # send a biobank order
        _id = int(self.participant_id[1:])
        self.path = ('Participant/%s/BiobankOrder' %
                     to_client_participant_id(_id))
        pid_numeric = from_client_participant_id(self.participant_id)
        self.send_post(self.path, load_biobank_order_json(pid_numeric))

        # fetch participant summary
        ps = self.send_get('ParticipantSummary?participantId=%s' % _id)

        self.assertTrue(
            ps['entry'][0]['resource']["physicalMeasurementsFinalizedTime"])
        self.assertEquals(
            ps['entry'][0]['resource']["physicalMeasurementsFinalizedSite"],
            'hpo-site-bannerphoenix')
        self.assertIsNotNone('biobankId', ps['entry'][0]['resource'])

    def _insert_measurements(self, now=None):
        measurements_1 = load_measurement_json(self.participant_id, now)
        path_1 = 'Participant/%s/PhysicalMeasurements' % self.participant_id
        self.send_post(path_1, measurements_1)

    def _submit_consent_questionnaire_response(self,
                                               participant_id,
                                               questionnaire_id,
                                               ehr_consent_answer,
                                               time=TIME_1):
        code_answers = []
        _add_code_answer(code_answers, "ehrConsent", ehr_consent_answer)
        qr = make_questionnaire_response_json(participant_id,
                                              questionnaire_id,
                                              code_answers=code_answers)
        with FakeClock(time):
            self.send_post(
                'Participant/%s/QuestionnaireResponse' % participant_id, qr)

    def _submit_empty_questionnaire_response(self,
                                             participant_id,
                                             questionnaire_id,
                                             time=TIME_1):
        qr = make_questionnaire_response_json(participant_id, questionnaire_id)
        with FakeClock(time):
            self.send_post(
                'Participant/%s/QuestionnaireResponse' % participant_id, qr)
예제 #10
0
class QuestionnaireResponseDaoTest(FlaskTestBase):
    def setUp(self):
        super(QuestionnaireResponseDaoTest, self).setUp()
        self.code_dao = CodeDao()
        self.participant_dao = ParticipantDao()
        self.questionnaire_dao = QuestionnaireDao()
        self.questionnaire_response_dao = QuestionnaireResponseDao()
        self.questionnaire_response_answer_dao = QuestionnaireResponseAnswerDao(
        )
        self.participant_summary_dao = ParticipantSummaryDao()
        self.CODE_1 = Code(codeId=1,
                           system=PPI_SYSTEM,
                           value=GENDER_IDENTITY_QUESTION_CODE,
                           display=u'c',
                           topic=u'd',
                           codeType=CodeType.QUESTION,
                           mapped=True)
        self.CODE_2 = Code(codeId=2,
                           system='a',
                           value='x',
                           display=u'y',
                           codeType=CodeType.QUESTION,
                           mapped=False)
        self.CODE_3 = Code(codeId=3,
                           system='a',
                           value='c',
                           codeType=CodeType.ANSWER,
                           mapped=True,
                           parentId=1)
        self.CODE_4 = Code(codeId=4,
                           system='a',
                           value='d',
                           codeType=CodeType.ANSWER,
                           mapped=True,
                           parentId=2)
        self.CODE_5 = Code(codeId=5,
                           system='a',
                           value='e',
                           codeType=CodeType.ANSWER,
                           mapped=False,
                           parentId=1)
        self.CODE_6 = Code(codeId=6,
                           system='a',
                           value='f',
                           codeType=CodeType.ANSWER,
                           mapped=True,
                           parentId=1)
        self.MODULE_CODE_7 = Code(codeId=7,
                                  system=PPI_SYSTEM,
                                  value=THE_BASICS_PPI_MODULE,
                                  codeType=CodeType.MODULE,
                                  mapped=True)
        self.CONCEPT_1 = QuestionnaireConcept(codeId=7)
        self.CODE_1_QUESTION_1 = QuestionnaireQuestion(linkId='a',
                                                       codeId=1,
                                                       repeats=False)
        self.CODE_2_QUESTION = QuestionnaireQuestion(linkId='d',
                                                     codeId=2,
                                                     repeats=True)
        # Same code as question 1
        self.CODE_1_QUESTION_2 = QuestionnaireQuestion(linkId='x',
                                                       codeId=1,
                                                       repeats=False)

        self.skip_code = Code(codeId=8,
                              system=PPI_SYSTEM,
                              value=PMI_SKIP_CODE,
                              mapped=True,
                              codeType=CodeType.ANSWER)

        config.override_setting(config.CONSENT_PDF_BUCKET, [_FAKE_BUCKET])

    def _setup_questionnaire(self):
        q = Questionnaire(resource=QUESTIONNAIRE_RESOURCE)
        q.concepts.append(self.CONCEPT_1)
        q.concepts.append(QuestionnaireConcept(codeId=self.consent_code_id))
        q.questions.append(self.CODE_1_QUESTION_1)
        q.questions.append(self.CODE_2_QUESTION)
        q.questions.append(self.FN_QUESTION)
        q.questions.append(self.LN_QUESTION)
        q.questions.append(self.EMAIL_QUESTION)
        q.questions.append(self.LOGIN_PHONE_NUMBER_QUESTION)
        return self.questionnaire_dao.insert(q)

    def insert_codes(self):
        self.code_dao.insert(self.CODE_1)
        self.code_dao.insert(self.CODE_2)
        self.code_dao.insert(self.CODE_3)
        self.code_dao.insert(self.CODE_4)
        self.code_dao.insert(self.CODE_5)
        self.code_dao.insert(self.CODE_6)
        self.code_dao.insert(self.MODULE_CODE_7)
        self.code_dao.insert(self.skip_code)
        self.consent_code_id = self.code_dao.insert(consent_code()).codeId
        self.first_name_code_id = self.code_dao.insert(
            first_name_code()).codeId
        self.last_name_code_id = self.code_dao.insert(last_name_code()).codeId
        self.email_code_id = self.code_dao.insert(email_code()).codeId
        self.login_phone_number_code_id = self.code_dao.insert(
            login_phone_number_code()).codeId
        self.FN_QUESTION = QuestionnaireQuestion(
            linkId='fn', codeId=self.first_name_code_id, repeats=False)
        self.LN_QUESTION = QuestionnaireQuestion(linkId='ln',
                                                 codeId=self.last_name_code_id,
                                                 repeats=False)
        self.EMAIL_QUESTION = QuestionnaireQuestion(linkId='email',
                                                    codeId=self.email_code_id,
                                                    repeats=False)
        self.LOGIN_PHONE_NUMBER_QUESTION = QuestionnaireQuestion(
            linkId='lpn',
            codeId=self.login_phone_number_code_id,
            repeats=False)
        self.first_name = self.fake.first_name()
        self.last_name = self.fake.last_name()
        self.email = self.fake.email()
        self.login_phone_number = self.fake.phone_number()
        self.FN_ANSWER = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=3,
            questionnaireResponseId=1,
            questionId=3,
            valueString=self.first_name)
        self.LN_ANSWER = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=4,
            questionnaireResponseId=1,
            questionId=4,
            valueString=self.last_name)
        self.EMAIL_ANSWER = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=5,
            questionnaireResponseId=1,
            questionId=5,
            valueString=self.email)
        self.LOGIN_PHONE_NUMBER_ANSWER = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=6,
            questionnaireResponseId=1,
            questionId=6,
            valueString=self.login_phone_number)

    def check_response(self, expected_qr):
        qr = self.questionnaire_response_dao.get_with_children(
            expected_qr.questionnaireResponseId)
        self.assertEquals(expected_qr.asdict(follow=ANSWERS),
                          qr.asdict(follow=ANSWERS))

    def _names_and_email_answers(self):
        return [self.FN_ANSWER, self.LN_ANSWER, self.EMAIL_ANSWER]

    def _names_and_login_phone_number_answers(self):
        return [self.FN_ANSWER, self.LN_ANSWER, self.LOGIN_PHONE_NUMBER_ANSWER]

    def test_get_before_insert(self):
        self.assertIsNone(self.questionnaire_response_dao.get(1))
        self.assertIsNone(self.questionnaire_response_dao.get_with_children(1))
        self.assertIsNone(self.questionnaire_response_answer_dao.get(1))

    def test_insert_questionnaire_not_found(self):
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_participant_not_found(self):
        self.insert_codes()
        q = Questionnaire(resource=QUESTIONNAIRE_RESOURCE)
        q.concepts.append(QuestionnaireConcept(codeId=self.consent_code_id))
        self.questionnaire_dao.insert(q)
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_email_answers())
        # Answers are there but the participant is not.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_participant_not_found2(self):
        self.insert_codes()
        p = Participant(participantId=1,
                        biobankId=2,
                        withdrawalStatus=WithdrawalStatus.NOT_WITHDRAWN)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=2,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_email_answers())
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_participant_withdrawn(self):
        self.insert_codes()
        p = Participant(participantId=1,
                        biobankId=2,
                        withdrawalStatus=WithdrawalStatus.NO_USE)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_email_answers())
        with self.assertRaises(Forbidden):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_not_name_answers(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(
            QuestionnaireResponseAnswer(questionnaireResponseAnswerId=2,
                                        questionnaireResponseId=1,
                                        questionId=2,
                                        valueSystem='c',
                                        valueCodeId=4))
        # Both first and last name are required.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_first_name_only(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.FN_ANSWER)
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_last_name_only(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.LN_ANSWER)
        # Both first and last name are required.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_names_only(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.FN_ANSWER)
        qr.answers.append(self.LN_ANSWER)
        # Email is required.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_email_only(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.EMAIL_ANSWER)
        # First and last name are required.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_login_phone_number_only(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.LOGIN_PHONE_NUMBER_ANSWER)
        # First and last name are required.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_both_email_and_login_phone_number_without_names(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.EMAIL_ANSWER)
        qr.answers.append(self.LOGIN_PHONE_NUMBER_ANSWER)
        # First and last name are required.
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    def test_insert_both_names_and_login_phone_number(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_login_phone_number_answers())
        time = datetime.datetime(2016, 1, 1)
        with FakeClock(time):
            qr.authored = time
            self.questionnaire_response_dao.insert(qr)

        expected_qr = QuestionnaireResponse(
            questionnaireResponseId=1,
            questionnaireId=1,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE, 1),
            created=time,
            authored=time)
        expected_qr.answers.extend(
            self._names_and_login_phone_number_answers())
        qr2 = self.questionnaire_response_dao.get(1)
        self.assertEquals(expected_qr.asdict(), qr2.asdict())
        self.check_response(expected_qr)

    def test_insert_both_names_and_email(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_email_answers())
        time = datetime.datetime(2016, 1, 1)
        with FakeClock(time):
            qr.authored = time
            self.questionnaire_response_dao.insert(qr)

        expected_qr = QuestionnaireResponse(
            questionnaireResponseId=1,
            questionnaireId=1,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE, 1),
            created=time,
            authored=time)
        expected_qr.answers.extend(self._names_and_email_answers())
        qr2 = self.questionnaire_response_dao.get(1)
        self.assertEquals(expected_qr.asdict(), qr2.asdict())
        self.check_response(expected_qr)

    def test_insert_both_names_and_email_and_login_phone_number(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.append(self.FN_ANSWER)
        qr.answers.append(self.LN_ANSWER)
        qr.answers.append(self.EMAIL_ANSWER)
        qr.answers.append(self.LOGIN_PHONE_NUMBER_ANSWER)
        time = datetime.datetime(2016, 1, 1)
        with FakeClock(time):
            qr.authored = time
            self.questionnaire_response_dao.insert(qr)

        expected_qr = QuestionnaireResponse(
            questionnaireResponseId=1,
            questionnaireId=1,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE, 1),
            created=time,
            authored=time)
        expected_qr.answers.append(self.FN_ANSWER)
        expected_qr.answers.append(self.LN_ANSWER)
        expected_qr.answers.append(self.EMAIL_ANSWER)
        expected_qr.answers.append(self.LOGIN_PHONE_NUMBER_ANSWER)
        qr2 = self.questionnaire_response_dao.get(1)
        self.assertEquals(expected_qr.asdict(), qr2.asdict())
        self.check_response(expected_qr)

    def test_insert_duplicate(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_email_answers())
        self.questionnaire_response_dao.insert(qr)
        qr2 = QuestionnaireResponse(questionnaireResponseId=1,
                                    questionnaireId=1,
                                    questionnaireVersion=1,
                                    participantId=1,
                                    resource=QUESTIONNAIRE_RESPONSE_RESOURCE_2)
        qr2.answers.append(
            QuestionnaireResponseAnswer(questionnaireResponseAnswerId=2,
                                        questionnaireResponseId=1,
                                        questionId=2,
                                        valueSystem='c',
                                        valueCodeId=4))
        with self.assertRaises(IntegrityError):
            self.questionnaire_response_dao.insert(qr2)

    def test_insert_skip_codes(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        with FakeClock(TIME):
            self.participant_dao.insert(p)
        self._setup_questionnaire()

        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)

        answer_1 = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=1,
            questionnaireResponseId=1,
            questionId=1,
            valueSystem='a',
            valueCodeId=self.skip_code.codeId)

        answer_2 = QuestionnaireResponseAnswer(questionnaireResponseAnswerId=2,
                                               questionnaireResponseId=1,
                                               questionId=2,
                                               valueSystem='c',
                                               valueCodeId=4)

        qr.answers.extend([answer_1, answer_2])
        qr.answers.extend(self._names_and_email_answers())

        with FakeClock(TIME_2):
            self.questionnaire_response_dao.insert(qr)

        expected_qr = QuestionnaireResponse(
            questionnaireResponseId=1,
            questionnaireId=1,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE, 1),
            created=TIME_2,
            authored=TIME_2)

        qr2 = self.questionnaire_response_dao.get(1)
        self.assertEquals(expected_qr.asdict(), qr2.asdict())

        expected_qr.answers.extend([answer_1, answer_2])
        expected_qr.answers.extend(self._names_and_email_answers())
        self.check_response(expected_qr)

        expected_ps = self._participant_summary_with_defaults(
            genderIdentityId=self.skip_code.codeId,
            participantId=1,
            biobankId=2,
            signUpTime=TIME,
            numCompletedBaselinePPIModules=1,
            numCompletedPPIModules=1,
            questionnaireOnTheBasics=QuestionnaireStatus.SUBMITTED,
            questionnaireOnTheBasicsTime=TIME_2,
            questionnaireOnTheBasicsAuthored=TIME_2,
            consentForStudyEnrollment=QuestionnaireStatus.SUBMITTED,
            consentForStudyEnrollmentTime=TIME_2,
            consentForStudyEnrollmentAuthored=TIME_2,
            firstName=self.first_name,
            lastName=self.last_name,
            email=self.email,
            lastModified=TIME_2,
        )
        self.assertEquals(expected_ps.asdict(),
                          self.participant_summary_dao.get(1).asdict())

    def test_from_client_json_raises_BadRequest_for_excessively_long_value_string(
            self):
        self.insert_codes()
        q_id = self.create_questionnaire('questionnaire1.json')
        p_id = self.create_participant()
        self.send_consent(p_id)

        # First check that the normal case actually writes out correctly
        string = 'a' * QuestionnaireResponseAnswer.VALUE_STRING_MAXLEN
        string_answers = [["nameOfChild", string]]
        resource = make_questionnaire_response_json(
            p_id, q_id, string_answers=string_answers)
        qr = self.questionnaire_response_dao.from_client_json(
            resource, participant_id=int(p_id[1:]))
        with self.questionnaire_response_answer_dao.session() as session:
            self.questionnaire_response_dao.insert(qr)
            all_strings_query = session.query(
                QuestionnaireResponseAnswer.valueString).all()
            all_strings = [obj.valueString for obj in all_strings_query]
            self.assertTrue(string in all_strings)

        # Now check that the incorrect case throws
        string = 'a' * (QuestionnaireResponseAnswer.VALUE_STRING_MAXLEN + 1)
        string_answers = [["nameOfChild", string]]
        resource = make_questionnaire_response_json(
            p_id, q_id, string_answers=string_answers)
        with self.assertRaises(BadRequest):
            qr = self.questionnaire_response_dao.from_client_json(
                resource, participant_id=int(p_id[1:]))

    def test_get_after_withdrawal_fails(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        qr.answers.extend(self._names_and_email_answers())
        self.questionnaire_response_dao.insert(qr)
        p.withdrawalStatus = WithdrawalStatus.NO_USE
        self.participant_dao.update(p)
        with self.assertRaises(Forbidden):
            self.questionnaire_response_dao.get(qr.questionnaireResponseId)

    def test_insert_with_answers(self):
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        with FakeClock(TIME):
            self.participant_dao.insert(p)
        self._setup_questionnaire()
        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        answer_1 = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=1,
            questionnaireResponseId=1,
            questionId=1,
            valueSystem='a',
            valueCodeId=3,
            valueDecimal=123,
            valueString=self.fake.first_name(),
            valueDate=datetime.date.today())
        answer_2 = QuestionnaireResponseAnswer(questionnaireResponseAnswerId=2,
                                               questionnaireResponseId=1,
                                               questionId=2,
                                               valueSystem='c',
                                               valueCodeId=4)
        qr.answers.append(answer_1)
        qr.answers.append(answer_2)
        names_and_email_answers = self._names_and_email_answers()
        qr.answers.extend(names_and_email_answers)
        with FakeClock(TIME_2):
            self.questionnaire_response_dao.insert(qr)

        expected_qr = QuestionnaireResponse(
            questionnaireResponseId=1,
            questionnaireId=1,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE, 1),
            created=TIME_2,
            authored=TIME_2)
        qr2 = self.questionnaire_response_dao.get(1)
        self.assertEquals(expected_qr.asdict(), qr2.asdict())

        expected_qr.answers.append(answer_1)
        expected_qr.answers.append(answer_2)
        expected_qr.answers.extend(names_and_email_answers)
        self.check_response(expected_qr)

        expected_ps = self._participant_summary_with_defaults(
            participantId=1,
            biobankId=2,
            genderIdentityId=3,
            signUpTime=TIME,
            numCompletedBaselinePPIModules=1,
            numCompletedPPIModules=1,
            questionnaireOnTheBasics=QuestionnaireStatus.SUBMITTED,
            questionnaireOnTheBasicsTime=TIME_2,
            questionnaireOnTheBasicsAuthored=TIME_2,
            consentForStudyEnrollment=QuestionnaireStatus.SUBMITTED,
            consentForStudyEnrollmentTime=TIME_2,
            consentForStudyEnrollmentAuthored=TIME_2,
            lastModified=TIME_2,
            firstName=self.first_name,
            lastName=self.last_name,
            email=self.email)
        self.assertEquals(expected_ps.asdict(),
                          self.participant_summary_dao.get(1).asdict())

    def test_insert_qr_three_times(self):
        """Adds three questionnaire responses for the same participant.

    The latter two responses are for the same questionnaire, answering a question that has the
    same concept code and system as a question found on the first (different) questionnaire.

    Verifies that new answers set endTime on answers for questions with the same concept for the
    same participant, whether on the same questionnaire or a different questionnaire,
    without affecting other answers.
    """
        self.insert_codes()
        p = Participant(participantId=1, biobankId=2)
        with FakeClock(TIME):
            self.participant_dao.insert(p)
        self._setup_questionnaire()
        q2 = Questionnaire(resource=QUESTIONNAIRE_RESOURCE_2)
        # The question on the second questionnaire has the same concept as the first question on the
        # first questionnaire; answers to it will thus set endTime for answers to the first question.
        q2.questions.append(self.CODE_1_QUESTION_2)

        self.questionnaire_dao.insert(q2)

        qr = QuestionnaireResponse(questionnaireResponseId=1,
                                   questionnaireId=1,
                                   questionnaireVersion=1,
                                   participantId=1,
                                   resource=QUESTIONNAIRE_RESPONSE_RESOURCE)
        answer_1 = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=1,
            questionnaireResponseId=1,
            questionId=1,
            valueSystem='a',
            valueCodeId=3,
            valueDecimal=123,
            valueString=self.fake.first_name(),
            valueDate=datetime.date.today())
        answer_2 = QuestionnaireResponseAnswer(questionnaireResponseAnswerId=2,
                                               questionnaireResponseId=1,
                                               questionId=2,
                                               valueSystem='c',
                                               valueCodeId=4)
        qr.answers.append(answer_1)
        qr.answers.append(answer_2)
        qr.answers.extend(self._names_and_email_answers())
        with FakeClock(TIME_2):
            self.questionnaire_response_dao.insert(qr)

        expected_ps = self._participant_summary_with_defaults(
            participantId=1,
            biobankId=2,
            genderIdentityId=3,
            signUpTime=TIME,
            numCompletedBaselinePPIModules=1,
            numCompletedPPIModules=1,
            questionnaireOnTheBasics=QuestionnaireStatus.SUBMITTED,
            questionnaireOnTheBasicsTime=TIME_2,
            questionnaireOnTheBasicsAuthored=TIME_2,
            consentForStudyEnrollment=QuestionnaireStatus.SUBMITTED,
            consentForStudyEnrollmentTime=TIME_2,
            consentForStudyEnrollmentAuthored=TIME_2,
            lastModified=TIME_2,
            firstName=self.first_name,
            lastName=self.last_name,
            email=self.email)
        self.assertEquals(expected_ps.asdict(),
                          self.participant_summary_dao.get(1).asdict())

        qr2 = QuestionnaireResponse(questionnaireResponseId=2,
                                    questionnaireId=2,
                                    questionnaireVersion=1,
                                    participantId=1,
                                    resource=QUESTIONNAIRE_RESPONSE_RESOURCE_2)
        answer_3 = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=6,
            questionnaireResponseId=2,
            questionId=7,
            valueSystem='x',
            valueCodeId=5,
            valueDecimal=123,
            valueString=self.fake.last_name(),
            valueDate=datetime.date.today())
        qr2.answers.append(answer_3)
        with FakeClock(TIME_3):
            qr2.authored = TIME_3
            self.questionnaire_response_dao.insert(qr2)

        expected_qr = QuestionnaireResponse(
            questionnaireResponseId=1,
            questionnaireId=1,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE, 1),
            created=TIME_2,
            authored=TIME_2)
        # Answer one on the original response should be marked as ended, since a question with
        # the same concept was answered. Answer two should be left alone.
        answer_1.endTime = TIME_3
        expected_qr.answers.append(answer_1)
        expected_qr.answers.append(answer_2)
        expected_qr.answers.extend(self._names_and_email_answers())
        self.check_response(expected_qr)

        # The new questionnaire response should be there, too.
        expected_qr2 = QuestionnaireResponse(
            questionnaireResponseId=2,
            questionnaireId=2,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE_2, 2),
            created=TIME_3,
            authored=TIME_3)
        expected_qr2.answers.append(answer_3)
        self.check_response(expected_qr2)

        expected_ps2 = self._participant_summary_with_defaults(
            participantId=1,
            biobankId=2,
            genderIdentityId=5,
            signUpTime=TIME,
            numCompletedBaselinePPIModules=1,
            numCompletedPPIModules=1,
            questionnaireOnTheBasics=QuestionnaireStatus.SUBMITTED,
            questionnaireOnTheBasicsTime=TIME_2,
            questionnaireOnTheBasicsAuthored=TIME_2,
            lastModified=TIME_3,
            consentForStudyEnrollment=QuestionnaireStatus.SUBMITTED,
            consentForStudyEnrollmentTime=TIME_2,
            consentForStudyEnrollmentAuthored=TIME_2,
            firstName=self.first_name,
            lastName=self.last_name,
            email=self.email)
        # The participant summary should be updated with the new gender identity, but nothing else
        # changes.
        self.assertEquals(expected_ps2.asdict(),
                          self.participant_summary_dao.get(1).asdict())

        qr3 = QuestionnaireResponse(questionnaireResponseId=3,
                                    questionnaireId=2,
                                    questionnaireVersion=1,
                                    participantId=1,
                                    resource=QUESTIONNAIRE_RESPONSE_RESOURCE_3)
        answer_4 = QuestionnaireResponseAnswer(
            questionnaireResponseAnswerId=7,
            questionnaireResponseId=3,
            questionId=7,
            valueSystem='z',
            valueCodeId=6,
            valueDecimal=456,
            valueString=self.fake.last_name(),
            valueDate=datetime.date.today())
        qr3.answers.append(answer_4)
        with FakeClock(TIME_4):
            qr3.authored = TIME_4
            self.questionnaire_response_dao.insert(qr3)

        # The first questionnaire response hasn't changed.
        self.check_response(expected_qr)

        # The second questionnaire response's answer should have had an endTime set.
        answer_3.endTime = TIME_4
        self.check_response(expected_qr2)

        # The third questionnaire response should be there.
        expected_qr3 = QuestionnaireResponse(
            questionnaireResponseId=3,
            questionnaireId=2,
            questionnaireVersion=1,
            participantId=1,
            resource=with_id(QUESTIONNAIRE_RESPONSE_RESOURCE_3, 3),
            created=TIME_4,
            authored=TIME_4)
        expected_qr3.answers.append(answer_4)
        self.check_response(expected_qr3)

        expected_ps3 = self._participant_summary_with_defaults(
            participantId=1,
            biobankId=2,
            genderIdentityId=6,
            signUpTime=TIME,
            numCompletedBaselinePPIModules=1,
            numCompletedPPIModules=1,
            questionnaireOnTheBasics=QuestionnaireStatus.SUBMITTED,
            questionnaireOnTheBasicsTime=TIME_2,
            questionnaireOnTheBasicsAuthored=TIME_2,
            consentForStudyEnrollment=QuestionnaireStatus.SUBMITTED,
            consentForStudyEnrollmentTime=TIME_2,
            consentForStudyEnrollmentAuthored=TIME_2,
            lastModified=TIME_4,
            firstName=self.first_name,
            lastName=self.last_name,
            email=self.email)
        # The participant summary should be updated with the new gender identity, but nothing else
        # changes.
        self.assertEquals(expected_ps3.asdict(),
                          self.participant_summary_dao.get(1).asdict())

    def _get_questionnaire_response_with_consents(self, *consent_paths):
        self.insert_codes()
        questionnaire = self._setup_questionnaire()
        participant = Participant(participantId=1, biobankId=2)
        self.participant_dao.insert(participant)
        resource = test_data.load_questionnaire_response_with_consents(
            questionnaire.questionnaireId, participant.participantId,
            self.FN_QUESTION.linkId, self.LN_QUESTION.linkId,
            self.EMAIL_QUESTION.linkId, consent_paths)

        # We need to remove the unused answers in the resource so we don't trigger an unused
        # link id exception.
        res_len = len(resource['group']['question']) - 1
        for idx in range(res_len, -1, -1):
            if resource['group']['question'][idx]['linkId'].isdigit() is True:
                del resource['group']['question'][idx]

        questionnaire_response = self.questionnaire_response_dao.from_client_json(
            resource, participant.participantId)

        return questionnaire_response

    @mock.patch('dao.questionnaire_response_dao._raise_if_gcloud_file_missing')
    def test_consent_pdf_valid_leading_slash(self, mock_gcloud_check):
        consent_pdf_path = '/Participant/xyz/consent.pdf'
        questionnaire_response = self._get_questionnaire_response_with_consents(
            consent_pdf_path)
        # This should pass validation (not raise exceptions).
        self.questionnaire_response_dao.insert(questionnaire_response)
        mock_gcloud_check.assert_called_with('/%s%s' %
                                             (_FAKE_BUCKET, consent_pdf_path))

    @mock.patch('dao.questionnaire_response_dao._raise_if_gcloud_file_missing')
    def test_consent_pdf_valid_no_leading_slash(self, mock_gcloud_check):
        consent_pdf_path = 'Participant/xyz/consent.pdf'
        questionnaire_response = self._get_questionnaire_response_with_consents(
            consent_pdf_path)
        # This should pass validation (not raise exceptions).
        self.questionnaire_response_dao.insert(questionnaire_response)
        mock_gcloud_check.assert_called_with('/%s/%s' %
                                             (_FAKE_BUCKET, consent_pdf_path))

    @mock.patch('dao.questionnaire_response_dao._raise_if_gcloud_file_missing')
    def test_consent_pdf_file_invalid(self, mock_gcloud_check):
        mock_gcloud_check.side_effect = BadRequest('Test should raise this.')
        qr = self._get_questionnaire_response_with_consents(
            '/nobucket/no/file.pdf')
        with self.assertRaises(BadRequest):
            self.questionnaire_response_dao.insert(qr)

    @mock.patch('dao.questionnaire_response_dao._raise_if_gcloud_file_missing')
    def test_consent_pdf_checks_multiple_extensions(self, mock_gcloud_check):
        qr = self._get_questionnaire_response_with_consents(
            '/Participant/one.pdf', '/Participant/two.pdf')
        self.questionnaire_response_dao.insert(qr)
        self.assertEquals(mock_gcloud_check.call_count, 2)
예제 #11
0
class OrganizationDaoTest(SqlTestBase):

  def setUp(self):
    super(OrganizationDaoTest, self).setUp()
    self.organization_dao = OrganizationDao()
    self.participant_dao = ParticipantDao()
    self.ps_dao = ParticipantSummaryDao()
    self.ps_history = ParticipantHistoryDao()

  def test_insert(self):
    organization = Organization(externalId='myorg', displayName='myorg_display',
                                hpoId=PITT_HPO_ID, isObsolete=1)
    created_organization = self.organization_dao.insert(organization)
    new_organization = self.organization_dao.get(created_organization.organizationId)
    organization.organizationId = created_organization.organizationId
    organization.isObsolete = new_organization.isObsolete
    self.assertEquals(organization.asdict(), new_organization.asdict())

  def test_participant_pairing_updates_onchange(self):
    provider_link = '[{"organization": {"reference": "Organization/AZ_TUCSON"}, "primary": true}]'
    TIME = datetime.datetime(2018, 1, 1)
    TIME2 = datetime.datetime(2018, 1, 2)
    insert_org = self.organization_dao.insert(
      Organization(externalId='tardis', displayName='bluebox', hpoId=PITT_HPO_ID))

    with FakeClock(TIME):
      self.participant_dao.insert(Participant(participantId=1, biobankId=2))
      participant = self.participant_dao.get(1)
      participant.organizationId = insert_org.organizationId
      self.participant_dao.update(participant)

      self.assertEquals(participant.hpoId, insert_org.hpoId)
      participant = self.participant_dao.get(1)
      p_summary = self.ps_dao.insert(self.participant_summary(participant))

    with FakeClock(TIME2):
      insert_org.hpoId = AZ_HPO_ID
      self.organization_dao.update(insert_org)

    new_org = self.organization_dao.get_by_external_id('tardis')
    ps = self.ps_dao.get(p_summary.participantId)
    ph = self.ps_history.get([participant.participantId, 2])
    participant = self.participant_dao.get(1)

    self.assertEquals(ps.lastModified, TIME2)
    self.assertEquals(ps.hpoId, new_org.hpoId)
    self.assertEquals(ph.hpoId, insert_org.hpoId)
    self.assertEquals(ph.organizationId, insert_org.organizationId)
    self.assertEquals(new_org.hpoId, participant.hpoId)
    self.assertEquals(new_org.organizationId, participant.organizationId)
    self.assertIsNone(participant.siteId)
    self.assertEquals(participant.providerLink, provider_link)

  def test_participant_different_hpo_does_not_change(self):
    insert_org = self.organization_dao.insert(
      Organization(externalId='stark_industries', displayName='ironman', hpoId=PITT_HPO_ID))

    self.participant_dao.insert(Participant(participantId=1, biobankId=2))
    participant = self.participant_dao.get(1)
    participant.hpoId = UNSET_HPO_ID
    self.participant_dao.update(participant)
    insert_org.hpoId = AZ_HPO_ID
    self.organization_dao.update(insert_org)
    new_org = self.organization_dao.get_by_external_id('stark_industries')
    participant = self.participant_dao.get(1)
    self.assertNotEqual(new_org.hpoId, participant.hpoId)
    self.assertEqual(new_org.hpoId, AZ_HPO_ID)
    self.assertEqual(participant.hpoId, UNSET_HPO_ID)
예제 #12
0
class BiobankOrderApiTest(FlaskTestBase):
    def setUp(self):
        super(BiobankOrderApiTest, self).setUp()
        self.participant = Participant(participantId=123, biobankId=555)
        self.participant_dao = ParticipantDao()
        self.participant_dao.insert(self.participant)
        self.summary_dao = ParticipantSummaryDao()
        self.path = ('Participant/%s/BiobankOrder' %
                     to_client_participant_id(self.participant.participantId))

    def test_insert_and_refetch(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        self.create_and_verify_created_obj(
            self.path, load_biobank_order_json(self.participant.participantId))

    def test_insert_new_order(self):
        self.summary_dao.insert(self.participant_summary(self.participant))
        order_json = load_biobank_order_json(self.participant.participantId,
                                             filename='biobank_order_2.json')
        result = self.send_post(self.path, order_json)
        full_order_json = load_biobank_order_json(
            self.participant.participantId, filename='biobank_order_1.json')
        _strip_fields(result)
        _strip_fields(full_order_json)
        self.assertEquals(full_order_json, result)

    def test_error_no_summary(self):
        order_json = load_biobank_order_json(self.participant.participantId)
        self.send_post(self.path,
                       order_json,
                       expected_status=httplib.BAD_REQUEST)

    def test_error_missing_required_fields(self):
        order_json = load_biobank_order_json(self.participant.participantId)
        del order_json['identifier']
        self.send_post(self.path,
                       order_json,
                       expected_status=httplib.BAD_REQUEST)

    def test_no_duplicate_test_within_order(self):
        order_json = load_biobank_order_json(self.participant.participantId)
        order_json['samples'].extend(list(order_json['samples']))
        self.send_post(self.path,
                       order_json,
                       expected_status=httplib.BAD_REQUEST)

    def test_auto_pair_updates_participant_and_summary(self):
        self.summary_dao.insert(self.participant_summary(self.participant))

        # Sanity check: No HPO yet.
        p_unpaired = self.participant_dao.get(self.participant.participantId)
        self.assertEquals(p_unpaired.hpoId, UNSET_HPO_ID)
        self.assertIsNone(p_unpaired.providerLink)
        s_unpaired = self.summary_dao.get(self.participant.participantId)
        self.assertEquals(s_unpaired.hpoId, UNSET_HPO_ID)

        self.send_post(self.path,
                       load_biobank_order_json(self.participant.participantId))

        # Some HPO has been set. (ParticipantDao tests cover more detailed cases / specific values.)
        p_paired = self.participant_dao.get(self.participant.participantId)
        self.assertNotEqual(p_paired.hpoId, UNSET_HPO_ID)
        self.assertIsNotNone(p_paired.providerLink)
        s_paired = self.summary_dao.get(self.participant.participantId)
        self.assertNotEqual(s_paired.hpoId, UNSET_HPO_ID)
예제 #13
0
class ParticipantSummaryDaoTest(NdbTestBase):
    def setUp(self):
        super(ParticipantSummaryDaoTest, self).setUp()
        self.dao = ParticipantSummaryDao()
        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 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']
        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))
        self.assertEquals(
            self.dao.get(
                p_baseline_samples.participantId).numBaselineSamplesArrived, 0)

        sample_dao = BiobankStoredSampleDao()

        def add_sample(participant, test_code, sample_id):
            sample_dao.insert(
                BiobankStoredSample(biobankStoredSampleId=sample_id,
                                    biobankId=participant.biobankId,
                                    test=test_code))

        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')

        self.dao.update_from_biobank_stored_samples()
        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)

    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))
예제 #14
0
class BiobankOrderApiTest(FlaskTestBase):
  def setUp(self):
    super(BiobankOrderApiTest, self).setUp()
    self.participant = Participant(participantId=123, biobankId=555)
    self.participant_dao = ParticipantDao()
    self.participant_dao.insert(self.participant)
    self.summary_dao = ParticipantSummaryDao()
    self.path = (
        'Participant/%s/BiobankOrder' % to_client_participant_id(self.participant.participantId))

  def test_insert_and_refetch(self):
    self.summary_dao.insert(self.participant_summary(self.participant))
    self.create_and_verify_created_obj(
        self.path, load_biobank_order_json(self.participant.participantId))

  def test_insert_new_order(self):
    self.summary_dao.insert(self.participant_summary(self.participant))
    order_json = load_biobank_order_json(self.participant.participantId,
                                         filename='biobank_order_2.json')
    result = self.send_post(self.path, order_json)
    full_order_json = load_biobank_order_json(self.participant.participantId,
                                              filename='biobank_order_1.json')
    _strip_fields(result)
    _strip_fields(full_order_json)
    self.assertEquals(full_order_json, result)

  def test_error_no_summary(self):
    order_json = load_biobank_order_json(self.participant.participantId)
    self.send_post(self.path, order_json, expected_status=httplib.BAD_REQUEST)

  def test_error_missing_required_fields(self):
    order_json = load_biobank_order_json(self.participant.participantId)
    del order_json['identifier']
    self.send_post(self.path, order_json, expected_status=httplib.BAD_REQUEST)

  def test_no_duplicate_test_within_order(self):
    order_json = load_biobank_order_json(self.participant.participantId)
    order_json['samples'].extend(list(order_json['samples']))
    self.send_post(self.path, order_json, expected_status=httplib.BAD_REQUEST)

  def test_auto_pair_updates_participant_and_summary(self):
    self.summary_dao.insert(self.participant_summary(self.participant))

    # Sanity check: No HPO yet.
    p_unpaired = self.participant_dao.get(self.participant.participantId)
    self.assertEquals(p_unpaired.hpoId, UNSET_HPO_ID)
    self.assertIsNone(p_unpaired.providerLink)
    s_unpaired = self.summary_dao.get(self.participant.participantId)
    self.assertEquals(s_unpaired.hpoId, UNSET_HPO_ID)

    self.send_post(self.path, load_biobank_order_json(self.participant.participantId))

    # Some HPO has been set. (ParticipantDao tests cover more detailed cases / specific values.)
    p_paired = self.participant_dao.get(self.participant.participantId)
    self.assertNotEqual(p_paired.hpoId, UNSET_HPO_ID)
    self.assertIsNotNone(p_paired.providerLink)

    s_paired = self.summary_dao.get(self.participant.participantId)

    self.assertNotEqual(s_paired.hpoId, UNSET_HPO_ID)
    self.assertEqual(s_paired.biospecimenCollectedSiteId, s_paired.siteId)
    self.assertNotEqual(s_paired.biospecimenCollectedSiteId, s_paired.biospecimenFinalizedSiteId)

    self.assertNotEqual(s_paired.siteId, s_paired.physicalMeasurementsCreatedSiteId )
    self.assertNotEqual(s_paired.siteId, s_paired.physicalMeasurementsFinalizedSiteId )

  def test_not_pairing_at_pm_when_has_bio(self):
    self.participant_id = self.create_participant()
    _id = int(self.participant_id[1:])
    self.path = (
      'Participant/%s/BiobankOrder' % to_client_participant_id(_id))
    pid_numeric = from_client_participant_id(self.participant_id)
    self.send_consent(self.participant_id)
    self.send_post(self.path, load_biobank_order_json(pid_numeric))
    participant_paired = self.summary_dao.get(pid_numeric)

    self.assertEqual(participant_paired.siteId, participant_paired.biospecimenCollectedSiteId)
    self.path = (
      'Participant/%s/PhysicalMeasurements' % to_client_participant_id(pid_numeric))
    self._insert_measurements(datetime.datetime.utcnow().isoformat())
    self.assertNotEqual(participant_paired.siteId,
                        participant_paired.physicalMeasurementsFinalizedSiteId)

  def _insert_measurements(self, now=None):
    measurements_1 = load_measurement_json(self.participant_id, now)
    path_1 = 'Participant/%s/PhysicalMeasurements' % self.participant_id
    self.send_post(path_1, measurements_1)
class UpdateEhrStatusUpdatesTestCase(SqlTestBase):
    def setUp(self, **kwargs):
        super(UpdateEhrStatusUpdatesTestCase, self).setUp(use_mysql=True,
                                                          **kwargs)
        self.hpo_dao = HPODao()
        self.org_dao = OrganizationDao()
        self.participant_dao = ParticipantDao()
        self.summary_dao = ParticipantSummaryDao()
        self.ehr_receipt_dao = EhrReceiptDao()

        self.hpo_foo = self._make_hpo(int_id=10, string_id='hpo_foo')
        self.hpo_bar = self._make_hpo(int_id=11, string_id='hpo_bar')

        self.org_foo_a = self._make_org(hpo=self.hpo_foo,
                                        int_id=10,
                                        external_id='FOO_A')
        self.org_foo_b = self._make_org(hpo=self.hpo_foo,
                                        int_id=11,
                                        external_id='FOO_B')
        self.org_bar_a = self._make_org(hpo=self.hpo_bar,
                                        int_id=12,
                                        external_id='BAR_A')

        self.participants = [
            self._make_participant(hpo=self.hpo_foo,
                                   org=self.org_foo_a,
                                   int_id=11),
            self._make_participant(hpo=self.hpo_foo,
                                   org=self.org_foo_b,
                                   int_id=12),
            self._make_participant(hpo=self.hpo_bar,
                                   org=self.org_bar_a,
                                   int_id=13),
            self._make_participant(hpo=self.hpo_bar,
                                   org=self.org_bar_a,
                                   int_id=14),
        ]

    def _make_hpo(self, int_id, string_id):
        hpo = HPO(hpoId=int_id, name=string_id)
        self.hpo_dao.insert(hpo)
        return hpo

    def _make_org(self, hpo, int_id, external_id):
        org = Organization(organizationId=int_id,
                           externalId=external_id,
                           displayName='SOME ORG',
                           hpoId=hpo.hpoId)
        self.org_dao.insert(org)
        return org

    def _make_participant(self, hpo, org, int_id):
        participant = self._participant_with_defaults(participantId=int_id,
                                                      biobankId=int_id)
        participant.hpoId = hpo.hpoId
        participant.organizationId = org.organizationId
        self.participant_dao.insert(participant)
        summary = self.participant_summary(participant)
        self.summary_dao.insert(summary)
        return participant, summary

    # Mock BigQuery result types
    EhrUpdatePidRow = collections.namedtuple('EhrUpdatePidRow', [
        'person_id',
    ])
    TableCountsRow = collections.namedtuple('TableCountsRow', [
        'org_id',
        'person_upload_time',
    ])

    @mock.patch('offline.update_ehr_status.update_organizations_from_job')
    @mock.patch(
        'offline.update_ehr_status.update_participant_summaries_from_job')
    @mock.patch('offline.update_ehr_status.make_update_organizations_job')
    @mock.patch(
        'offline.update_ehr_status.make_update_participant_summaries_job')
    def test_skips_when_no_job(self, mock_summary_job, mock_organization_job,
                               mock_update_summaries,
                               mock_update_organizations):
        mock_summary_job.return_value = None
        mock_organization_job.return_value = None

        with FakeClock(datetime.datetime(2019, 1, 1)):
            offline.update_ehr_status.update_ehr_status()

        self.assertFalse(mock_update_summaries.called)
        self.assertFalse(mock_update_organizations.called)

    @mock.patch('offline.update_ehr_status.make_update_organizations_job')
    @mock.patch(
        'offline.update_ehr_status.make_update_participant_summaries_job')
    def test_updates_participant_summaries(self, mock_summary_job,
                                           mock_organization_job):
        mock_summary_job.return_value.__iter__.return_value = [[
            self.EhrUpdatePidRow(11),
        ]]
        mock_organization_job.return_value.__iter__.return_value = []
        with FakeClock(datetime.datetime(2019, 1, 1)):
            offline.update_ehr_status.update_ehr_status()

        mock_summary_job.return_value.__iter__.return_value = [[
            self.EhrUpdatePidRow(11),
            self.EhrUpdatePidRow(12),
        ]]
        mock_organization_job.return_value.__iter__.return_value = []
        with FakeClock(datetime.datetime(2019, 1, 2)):
            offline.update_ehr_status.update_ehr_status()

        summary = self.summary_dao.get(11)
        self.assertEqual(summary.ehrStatus, EhrStatus.PRESENT)
        self.assertEqual(summary.ehrReceiptTime, datetime.datetime(2019, 1, 1))
        self.assertEqual(summary.ehrUpdateTime, datetime.datetime(2019, 1, 2))

        summary = self.summary_dao.get(12)
        self.assertEqual(summary.ehrStatus, EhrStatus.PRESENT)
        self.assertEqual(summary.ehrReceiptTime, datetime.datetime(2019, 1, 2))
        self.assertEqual(summary.ehrUpdateTime, datetime.datetime(2019, 1, 2))

    @mock.patch('offline.update_ehr_status.make_update_organizations_job')
    @mock.patch(
        'offline.update_ehr_status.make_update_participant_summaries_job')
    def test_creates_receipts(self, mock_summary_job, mock_organization_job):
        mock_summary_job.return_value.__iter__.return_value = []
        mock_organization_job.return_value.__iter__.return_value = [
            [
                self.TableCountsRow(org_id='FOO_A',
                                    person_upload_time=datetime.datetime(
                                        2019, 1, 1).replace(tzinfo=pytz.UTC)),
            ],
        ]
        with FakeClock(datetime.datetime(2019, 1, 1)):
            offline.update_ehr_status.update_ehr_status()

        foo_a_receipts = self.ehr_receipt_dao.get_by_organization_id(
            self.org_foo_a.organizationId)
        self.assertEqual(len(foo_a_receipts), 1)
        self.assertEqual(foo_a_receipts[0].receiptTime,
                         datetime.datetime(2019, 1, 1))

        foo_b_receipts = self.ehr_receipt_dao.get_by_organization_id(
            self.org_foo_b.organizationId)
        self.assertEqual(len(foo_b_receipts), 0)

        mock_summary_job.return_value.__iter__.return_value = []
        mock_organization_job.return_value.__iter__.return_value = [
            [
                self.TableCountsRow(org_id='FOO_A',
                                    person_upload_time=datetime.datetime(
                                        2019, 1, 1).replace(tzinfo=pytz.UTC)),
                self.TableCountsRow(org_id='FOO_A',
                                    person_upload_time=datetime.datetime(
                                        2019, 1, 2).replace(tzinfo=pytz.UTC)),
                self.TableCountsRow(org_id='FOO_B',
                                    person_upload_time=datetime.datetime(
                                        2019, 1, 2).replace(tzinfo=pytz.UTC)),
            ],
        ]
        with FakeClock(datetime.datetime(2019, 1, 2)):
            offline.update_ehr_status.update_ehr_status()

        foo_a_receipts = self.ehr_receipt_dao.get_by_organization_id(
            self.org_foo_a.organizationId)
        self.assertEqual(len(foo_a_receipts), 2)
        self.assertEqual(foo_a_receipts[0].receiptTime,
                         datetime.datetime(2019, 1, 1))
        self.assertEqual(foo_a_receipts[1].receiptTime,
                         datetime.datetime(2019, 1, 2))

        foo_b_receipts = self.ehr_receipt_dao.get_by_organization_id(
            self.org_foo_b.organizationId)
        self.assertEqual(len(foo_b_receipts), 1)
        self.assertEqual(foo_b_receipts[0].receiptTime,
                         datetime.datetime(2019, 1, 2))

    @mock.patch('offline.update_ehr_status.make_update_organizations_job')
    @mock.patch(
        'offline.update_ehr_status.make_update_participant_summaries_job')
    def test_ignores_bad_data(self, mock_summary_job, mock_organization_job):
        invalid_participant_id = -1
        mock_summary_job.return_value.__iter__.return_value = [[
            self.EhrUpdatePidRow(invalid_participant_id),
        ]]
        mock_organization_job.return_value.__iter__.return_value = [
            [
                self.TableCountsRow(
                    org_id='FOO_A',
                    person_upload_time="an invalid date string"),
                self.TableCountsRow(org_id='AN_ORG_THAT_DOESNT_EXIST',
                                    person_upload_time=datetime.datetime(
                                        2019, 1, 1).replace(tzinfo=pytz.UTC)),
                self.TableCountsRow(org_id='AN_ORG_THAT_DOESNT_EXIST',
                                    person_upload_time=None),
            ],
        ]
        with FakeClock(datetime.datetime(2019, 1, 1)):
            offline.update_ehr_status.update_ehr_status()

        foo_a_receipts = self.ehr_receipt_dao.get_all()
        self.assertEqual(len(foo_a_receipts), 0)