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
Example #2
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)
Example #3
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)