def _get_validation_result(email, codes_to_answers):
    result = _ValidationResult()

    summaries = ParticipantSummaryDao().get_by_email(email)
    if not summaries:
        result.add_error('No ParticipantSummary found for %r.' % email)
        return result
    if len(summaries) > 1:
        result.add_error('%d ParticipantSummary values found for %r.' %
                         (len(summaries), email))
        return result
    participant_id = summaries[0].participantId

    code_dao = CodeDao()
    qra_dao = QuestionnaireResponseAnswerDao()
    with qra_dao.session() as session:
        for code_string, answer_string in codes_to_answers.iteritems():
            result.tests_count += 1

            question_code = code_dao.get_code(PPI_SYSTEM, code_string)
            if not question_code:
                result.add_error(
                    'Could not find question code %r, skipping answer %r.' %
                    (code_string, answer_string))
                continue
            if question_code.codeType != CodeType.QUESTION:
                result.add_error(
                    'Code %r type is %s, not QUESTION; skipping.' %
                    (code_string, question_code.codeType))
                continue

            qras = qra_dao.get_current_answers_for_concepts(
                session, participant_id, [question_code.codeId])
            qra_values = set()
            for qra in qras:
                try:
                    qra_values.add(
                        _get_value_for_qra(qra, question_code, code_dao,
                                           session))
                except ValueError as e:
                    result.add_error(e.message)
                    continue

            if answer_string:
                expected_values = set(
                    _boolean_to_lower(v.strip())
                    for v in answer_string.split('|'))
            else:
                expected_values = set()
            if expected_values != qra_values:
                result.add_error(
                    '%s: Expected %s, found %s.' %
                    (question_code.value, _format_values(expected_values),
                     _format_values(qra_values)))
    return result
예제 #2
0
    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])
예제 #3
0
    def test_insert(self):
        participant_id = self.create_participant()
        questionnaire_id = self.create_questionnaire('questionnaire1.json')
        with open(data_path('questionnaire_response3.json')) as fd:
            resource = json.load(fd)

        # Sending response with the dummy participant id in the file is an error
        self.send_post(_questionnaire_response_url('{participant_id}'),
                       resource,
                       expected_status=httplib.NOT_FOUND)

        # Fixing participant id but not the questionnaire id is also an error
        resource['subject']['reference'] = \
            resource['subject']['reference'].format(participant_id=participant_id)
        self.send_post(_questionnaire_response_url(participant_id),
                       resource,
                       expected_status=httplib.BAD_REQUEST)

        # Fix the reference
        resource['questionnaire']['reference'] = \
            resource['questionnaire']['reference'].format(questionnaire_id=questionnaire_id)

        # Sending the response before the consent is an error.
        self.send_post(_questionnaire_response_url(participant_id),
                       resource,
                       expected_status=httplib.BAD_REQUEST)

        # After consent, the post succeeds
        self.send_consent(participant_id)
        response = self.send_post(_questionnaire_response_url(participant_id),
                                  resource)
        resource['id'] = response['id']
        # The resource gets rewritten to include the version
        resource['questionnaire'][
            'reference'] = 'Questionnaire/%s/_history/1' % questionnaire_id
        self.assertJsonResponseMatches(resource, response)

        # Do a get to fetch the questionnaire
        get_response = self.send_get(
            _questionnaire_response_url(participant_id) + "/" + response['id'])
        self.assertJsonResponseMatches(resource, get_response)

        code_dao = CodeDao()

        # Ensure we didn't create codes in the extra system
        self.assertIsNone(code_dao.get_code(PPI_EXTRA_SYSTEM, 'IgnoreThis'))

        name_of_child = code_dao.get_code("sys", "nameOfChild")
        birth_weight = code_dao.get_code("sys", "birthWeight")
        birth_length = code_dao.get_code("sys", "birthLength")
        vitamin_k_dose_1 = code_dao.get_code("sys", "vitaminKDose1")
        vitamin_k_dose_2 = code_dao.get_code("sys", "vitaminKDose2")
        hep_b_given = code_dao.get_code("sys", "hepBgiven")
        abnormalities_at_birth = code_dao.get_code("sys",
                                                   "abnormalitiesAtBirth")
        answer_dao = QuestionnaireResponseAnswerDao()
        with answer_dao.session() as session:
            code_ids = [
                code.codeId for code in [
                    name_of_child, birth_weight, birth_length,
                    vitamin_k_dose_1, vitamin_k_dose_2, hep_b_given,
                    abnormalities_at_birth
                ]
            ]
            current_answers = answer_dao.get_current_answers_for_concepts(session,\
                from_client_participant_id(participant_id), code_ids)
        self.assertEquals(7, len(current_answers))
        questionnaire = QuestionnaireDao().get_with_children(questionnaire_id)
        question_id_to_answer = {
            answer.questionId: answer
            for answer in current_answers
        }
        code_id_to_answer = {
            question.codeId:
            question_id_to_answer.get(question.questionnaireQuestionId)
            for question in questionnaire.questions
        }
        self.assertEquals("Cathy Jones",
                          code_id_to_answer[name_of_child.codeId].valueString)
        self.assertEquals(3.25,
                          code_id_to_answer[birth_weight.codeId].valueDecimal)
        self.assertEquals(44.3,
                          code_id_to_answer[birth_length.codeId].valueDecimal)
        self.assertEquals(44,
                          code_id_to_answer[birth_length.codeId].valueInteger)
        self.assertEquals(True,
                          code_id_to_answer[hep_b_given.codeId].valueBoolean)
        self.assertEquals(
            0, code_id_to_answer[abnormalities_at_birth.codeId].valueInteger)
        self.assertEquals(datetime.date(1972, 11, 30),
                          code_id_to_answer[vitamin_k_dose_1.codeId].valueDate)
        self.assertEquals(
            datetime.datetime(1972, 11, 30, 12, 34, 42),
            code_id_to_answer[vitamin_k_dose_2.codeId].valueDateTime)
예제 #4
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)