Esempio n. 1
0
 def setup_codes(values, code_type):
     code_dao = CodeDao()
     for value in values:
         code_dao.insert(
             Code(system=PPI_SYSTEM,
                  value=value,
                  codeType=code_type,
                  mapped=True))
class SchemaTest(SqlTestBase):
    def setUp(self):
        super(SchemaTest, self).setUp(use_mysql=True)

        self.code_dao = CodeDao()

    def _setup_codes(self):

        self.consent_code_id = self.code_dao.insert(consent_code()).codeId

        self.setup_codes([RACE_QUESTION_CODE], CodeType.QUESTION)
        self.setup_codes([RACE_AIAN_CODE, RACE_WHITE_CODE], CodeType.ANSWER)

    def test_fn_get_code_id_from_key(self):

        self._setup_codes()

        engine = self.database.get_engine()
        result = engine.execute(
            "select fn_get_code_id_from_key('WhatRaceEthnicity_AIAN')"
        ).fetchone()
        self.assertEquals(3, result[0])

        result = engine.execute(
            "select fn_get_code_id_from_key('WhatRaceEthnicity_White')"
        ).fetchone()
        self.assertEquals(4, result[0])
class QuestionnaireDaoTest(SqlTestBase):
    def setUp(self):
        super(QuestionnaireDaoTest, self).setUp(with_data=False)
        self.dao = QuestionnaireDao()
        self.questionnaire_history_dao = QuestionnaireHistoryDao()
        self.questionnaire_concept_dao = QuestionnaireConceptDao()
        self.questionnaire_question_dao = QuestionnaireQuestionDao()
        self.code_dao = CodeDao()
        self.CODE_1 = Code(codeId=1,
                           system='a',
                           value='b',
                           display=u'c',
                           topic=u'd',
                           codeType=CodeType.MODULE,
                           mapped=True)
        self.CODE_2 = Code(codeId=2,
                           system='a',
                           value='x',
                           display=u'y',
                           codeType=CodeType.MODULE,
                           mapped=False)
        self.CODE_3 = Code(codeId=3,
                           system='a',
                           value='z',
                           display=u'y',
                           codeType=CodeType.MODULE,
                           mapped=False)
        self.CODE_4 = Code(codeId=4,
                           system='a',
                           value='c',
                           codeType=CodeType.QUESTION,
                           mapped=True,
                           parentId=1)
        self.CODE_5 = Code(codeId=5,
                           system='a',
                           value='d',
                           codeType=CodeType.QUESTION,
                           mapped=True,
                           parentId=2)
        self.CODE_6 = Code(codeId=6,
                           system='a',
                           value='e',
                           codeType=CodeType.QUESTION,
                           mapped=True,
                           parentId=2)
        self.CONCEPT_1 = QuestionnaireConcept(codeId=1)
        self.CONCEPT_2 = QuestionnaireConcept(codeId=2)
        self.QUESTION_1 = QuestionnaireQuestion(linkId='a',
                                                codeId=4,
                                                repeats=False)
        self.QUESTION_2 = QuestionnaireQuestion(linkId='d',
                                                codeId=5,
                                                repeats=True)
        self.insert_codes()

    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)

    def test_get_before_insert(self):
        self.assertIsNone(self.dao.get(1))
        self.assertIsNone(self.dao.get_with_children(1))
        self.assertIsNone(
            self.dao.get_latest_questionnaire_with_concept(self.CODE_1.codeId))
        self.assertIsNone(self.questionnaire_history_dao.get([1, 1]))
        self.assertIsNone(
            self.questionnaire_history_dao.get_with_children([1, 1]))
        self.assertIsNone(self.questionnaire_concept_dao.get(1))
        self.assertIsNone(self.questionnaire_question_dao.get(1))

    def check_history(self):
        expected_history = QuestionnaireHistory(questionnaireId=1,
                                                version=1,
                                                created=TIME,
                                                lastModified=TIME,
                                                resource=RESOURCE_1_WITH_ID)
        questionnaire_history = self.questionnaire_history_dao.get([1, 1])
        self.assertEquals(expected_history.asdict(),
                          questionnaire_history.asdict())

        questionnaire_history = self.questionnaire_history_dao.get_with_children(
            [1, 1])
        expected_history.concepts.append(EXPECTED_CONCEPT_1)
        expected_history.concepts.append(EXPECTED_CONCEPT_2)
        expected_history.questions.append(EXPECTED_QUESTION_1)
        expected_history.questions.append(EXPECTED_QUESTION_2)

        self.assertEquals(EXPECTED_CONCEPT_1.asdict(),
                          self.questionnaire_concept_dao.get(1).asdict())
        self.assertEquals(EXPECTED_CONCEPT_2.asdict(),
                          self.questionnaire_concept_dao.get(2).asdict())
        self.assertEquals(EXPECTED_QUESTION_1.asdict(),
                          self.questionnaire_question_dao.get(1).asdict())
        self.assertEquals(EXPECTED_QUESTION_2.asdict(),
                          self.questionnaire_question_dao.get(2).asdict())

    def test_insert(self):
        q = Questionnaire(resource=RESOURCE_1)
        q.concepts.append(self.CONCEPT_1)
        q.concepts.append(self.CONCEPT_2)
        q.questions.append(self.QUESTION_1)
        q.questions.append(self.QUESTION_2)

        with FakeClock(TIME):
            self.dao.insert(q)

        # Creating a questionnaire creates a history entry with children
        self.check_history()

        expected_questionnaire = Questionnaire(questionnaireId=1,
                                               version=1,
                                               created=TIME,
                                               lastModified=TIME,
                                               resource=RESOURCE_1_WITH_ID)
        questionnaire = self.dao.get(1)
        self.assertEquals(expected_questionnaire.asdict(),
                          questionnaire.asdict())

        expected_questionnaire.concepts.append(EXPECTED_CONCEPT_1)
        expected_questionnaire.concepts.append(EXPECTED_CONCEPT_2)
        expected_questionnaire.questions.append(EXPECTED_QUESTION_1)
        expected_questionnaire.questions.append(EXPECTED_QUESTION_2)

        questionnaire = self.dao.get_with_children(1)

        self.assertEquals(
            sort_lists(expected_questionnaire.asdict_with_children()),
            sort_lists(questionnaire.asdict_with_children()))
        self.assertEquals(
            questionnaire.asdict(),
            self.dao.get_latest_questionnaire_with_concept(
                self.CODE_1.codeId).asdict())

    def test_insert_duplicate(self):
        q = Questionnaire(questionnaireId=1, resource=RESOURCE_1)
        self.dao.insert(q)
        try:
            self.dao.insert(q)
            self.fail("IntegrityError expected")
        except IntegrityError:
            pass

    def test_update_right_expected_version(self):
        q = Questionnaire(resource=RESOURCE_1)
        with FakeClock(TIME):
            self.dao.insert(q)

        q = Questionnaire(questionnaireId=1, version=1, resource=RESOURCE_2)
        with FakeClock(TIME_2):
            self.dao.update(q)

        expected_questionnaire = Questionnaire(questionnaireId=1,
                                               version=2,
                                               created=TIME,
                                               lastModified=TIME_2,
                                               resource=RESOURCE_2_WITH_ID)
        questionnaire = self.dao.get(1)
        self.assertEquals(expected_questionnaire.asdict(),
                          questionnaire.asdict())

    def test_update_wrong_expected_version(self):
        q = Questionnaire(resource=RESOURCE_1)
        with FakeClock(TIME):
            self.dao.insert(q)

        q = Questionnaire(questionnaireId=1, version=2, resource=RESOURCE_2)
        with FakeClock(TIME_2):
            try:
                self.dao.update(q)
                self.fail("PreconditionFailed expected")
            except PreconditionFailed:
                pass

    def test_update_not_exists(self):
        q = Questionnaire(questionnaireId=1, resource=RESOURCE_1)
        try:
            self.dao.update(q)
            self.fail("NotFound expected")
        except NotFound:
            pass

    def test_insert_multiple_questionnaires_same_concept(self):
        q = Questionnaire(resource=RESOURCE_1)
        q.concepts.append(self.CONCEPT_1)
        q.concepts.append(self.CONCEPT_2)
        with FakeClock(TIME):
            self.dao.insert(q)

        q2 = Questionnaire(resource=RESOURCE_2)
        q2.concepts.append(self.CONCEPT_1)
        with FakeClock(TIME_2):
            self.dao.insert(q2)

        self.assertEquals(
            2,
            self.dao.get_latest_questionnaire_with_concept(
                self.CODE_1.codeId).questionnaireId)
        self.assertEquals(
            1,
            self.dao.get_latest_questionnaire_with_concept(
                self.CODE_2.codeId).questionnaireId)
Esempio n. 4
0
class SchemaTest(SqlTestBase):
    def setUp(self):
        super(SchemaTest, self).setUp(use_mysql=True)

        self.code_dao = CodeDao()

    def _setup_codes(self):

        self.consent_code_id = self.code_dao.insert(consent_code()).codeId

        self.setup_codes([RACE_QUESTION_CODE], CodeType.QUESTION)
        self.setup_codes([RACE_AIAN_CODE, RACE_WHITE_CODE], CodeType.ANSWER)

    def test_fn_get_code_id_from_key(self):

        self._setup_codes()

        engine = self.database.get_engine()
        result = engine.execute(
            "select fn_get_code_id_from_key('WhatRaceEthnicity_AIAN')"
        ).fetchone()
        self.assertEquals(3, result[0])

        result = engine.execute(
            "select fn_get_code_id_from_key('WhatRaceEthnicity_White')"
        ).fetchone()
        self.assertEquals(4, result[0])

    def test_fn_get_code_module(self):

        self._setup_codes()

        # Test each level of a Topic
        #  ConsentPII              := level 1
        #  ConsentPII_PIIAddress   := level 2
        #  StateOfResidence        := level 3
        #  SOR_AK                  := level 4

        engine = self.database.get_engine()
        result = engine.execute(
            "select fn_get_code_module('ConsentPII')").fetchone()
        self.assertEquals('ConsentPII', result[0])

        result = engine.execute(
            "select fn_get_code_module('ConsentPII_PIIAddress')").fetchone()
        self.assertEquals('ConsentPII', result[0])

        result = engine.execute(
            "select fn_get_code_module('StateOfResidence')").fetchone()
        self.assertEquals('ConsentPII', result[0])

        result = engine.execute(
            "select fn_get_code_module('SOR_AK')").fetchone()
        self.assertEquals('ConsentPII', result[0])

        result = engine.execute(
            "select fn_get_code_module_id('ConsentPII')").fetchone()
        self.assertIsNotNone(result)
        code_id = result[0]
        self.assertGreater(code_id, 0)

        result = engine.execute(
            "select fn_get_code_module_id('ConsentPII_PIIAddress')").fetchone(
            )
        self.assertEquals(code_id, result[0])

        result = engine.execute(
            "select fn_get_code_module_id('StateOfResidence')").fetchone()
        self.assertEquals(code_id, result[0])

        result = engine.execute(
            "select fn_get_code_module_id('SOR_AK')").fetchone()
        self.assertEquals(code_id, result[0])
Esempio n. 5
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)
class CodeDaoTest(SqlTestBase):
    def setUp(self):
        super(CodeDaoTest, self).setUp()
        self.code_book_dao = CodeBookDao()
        self.code_dao = CodeDao()
        self.code_history_dao = CodeHistoryDao()

    def test_get_before_insert(self):
        self.assertIsNone(self.code_book_dao.get(1))
        self.assertIsNone(self.code_dao.get(1))
        self.assertIsNone(self.code_history_dao.get(1))

    def test_insert_without_codebook_or_parent(self):
        code = Code(system="a",
                    value="b",
                    display=u"c",
                    topic=u"d",
                    codeType=CodeType.MODULE,
                    mapped=True)
        with FakeClock(TIME):
            self.code_dao.insert(code)

        expected_code = Code(codeId=1,
                             system="a",
                             value="b",
                             display=u"c",
                             topic=u"d",
                             codeType=CodeType.MODULE,
                             mapped=True,
                             created=TIME)
        self.assertEquals(expected_code.asdict(),
                          self.code_dao.get(1).asdict())

        expected_code_history = CodeHistory(codeHistoryId=1,
                                            codeId=1,
                                            system="a",
                                            value="b",
                                            display=u"c",
                                            topic=u"d",
                                            codeType=CodeType.MODULE,
                                            mapped=True,
                                            created=TIME)
        self.assertEquals(expected_code_history.asdict(),
                          self.code_history_dao.get(1).asdict())

    def test_insert_with_codebook_and_parent(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="a")
        with FakeClock(TIME):
            self.code_book_dao.insert(code_book_1)
        expected_code_book = CodeBook(codeBookId=1,
                                      latest=True,
                                      created=TIME,
                                      name="pmi",
                                      version="v1",
                                      system="a")
        self.assertEquals(expected_code_book.asdict(),
                          self.code_book_dao.get(1).asdict())

        code_1 = Code(codeBookId=1,
                      system="a",
                      value="b",
                      display=u"c",
                      topic=u"d",
                      codeType=CodeType.MODULE,
                      mapped=True)
        with FakeClock(TIME_2):
            self.code_dao.insert(code_1)

        expected_code = Code(codeBookId=1,
                             codeId=1,
                             system="a",
                             value="b",
                             display=u"c",
                             topic=u"d",
                             codeType=CodeType.MODULE,
                             mapped=True,
                             created=TIME_2)
        self.assertEquals(expected_code.asdict(),
                          self.code_dao.get(1).asdict())

        expected_code_history = CodeHistory(codeBookId=1,
                                            codeHistoryId=1,
                                            codeId=1,
                                            system="a",
                                            value=u"b",
                                            display=u"c",
                                            topic=u"d",
                                            codeType=CodeType.MODULE,
                                            mapped=True,
                                            created=TIME_2)
        self.assertEquals(expected_code_history.asdict(),
                          self.code_history_dao.get(1).asdict())

        code_2 = Code(codeBookId=1,
                      system="x",
                      value="y",
                      display=u"z",
                      topic=u"q",
                      codeType=CodeType.QUESTION,
                      mapped=False,
                      parentId=1)
        with FakeClock(TIME_3):
            self.code_dao.insert(code_2)

        expected_code_2 = Code(codeBookId=1,
                               codeId=2,
                               system="x",
                               value="y",
                               display=u"z",
                               topic=u"q",
                               codeType=CodeType.QUESTION,
                               mapped=False,
                               created=TIME_3,
                               parentId=1)
        self.assertEquals(expected_code_2.asdict(),
                          self.code_dao.get(2).asdict())

    def test_insert_second_codebook_same_system(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="a")
        with FakeClock(TIME):
            self.code_book_dao.insert(code_book_1)

        code_book_2 = CodeBook(name="pmi", version="v2", system="a")
        with FakeClock(TIME_2):
            self.code_book_dao.insert(code_book_2)

        expected_code_book = CodeBook(codeBookId=1,
                                      latest=False,
                                      created=TIME,
                                      name="pmi",
                                      version="v1",
                                      system="a")
        self.assertEquals(expected_code_book.asdict(),
                          self.code_book_dao.get(1).asdict())

        expected_code_book_2 = CodeBook(codeBookId=2,
                                        latest=True,
                                        created=TIME_2,
                                        name="pmi",
                                        version="v2",
                                        system="a")
        self.assertEquals(expected_code_book_2.asdict(),
                          self.code_book_dao.get(2).asdict())

    def test_insert_second_codebook_different_system(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="a")
        with FakeClock(TIME):
            self.code_book_dao.insert(code_book_1)

        code_book_2 = CodeBook(name="pmi", version="v2", system="b")
        with FakeClock(TIME_2):
            self.code_book_dao.insert(code_book_2)

        expected_code_book = CodeBook(codeBookId=1,
                                      latest=True,
                                      created=TIME,
                                      name="pmi",
                                      version="v1",
                                      system="a")
        self.assertEquals(expected_code_book.asdict(),
                          self.code_book_dao.get(1).asdict())

        expected_code_book_2 = CodeBook(codeBookId=2,
                                        latest=True,
                                        created=TIME_2,
                                        name="pmi",
                                        version="v2",
                                        system="b")
        self.assertEquals(expected_code_book_2.asdict(),
                          self.code_book_dao.get(2).asdict())

    def test_insert_second_codebook_same_system_same_version(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="a")
        self.code_book_dao.insert(code_book_1)

        code_book_2 = CodeBook(name="pmi", version="v1", system="a")
        with self.assertRaises(BadRequest):
            self.code_book_dao.insert(code_book_2)

    def test_update_codes_no_codebook_id(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="c")
        with FakeClock(TIME):
            self.code_book_dao.insert(code_book_1)
        code_1 = Code(codeBookId=1,
                      system="a",
                      value="b",
                      display=u"c",
                      topic=u"d",
                      codeType=CodeType.MODULE,
                      mapped=True)
        with FakeClock(TIME_2):
            self.code_dao.insert(code_1)

        new_code_1 = Code(codeId=1,
                          system="x",
                          value="b",
                          display=u"c",
                          topic=u"d",
                          codeType=CodeType.MODULE,
                          mapped=True)
        with self.assertRaises(BadRequest):
            self.code_dao.update(new_code_1)

    def test_update_codes_same_codebook_id(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="c")
        with FakeClock(TIME):
            self.code_book_dao.insert(code_book_1)
        code_1 = Code(codeBookId=1,
                      system="a",
                      value="b",
                      display=u"c",
                      topic=u"d",
                      codeType=CodeType.MODULE,
                      mapped=True)
        with FakeClock(TIME_2):
            self.code_dao.insert(code_1)

        new_code_1 = Code(codeBookId=1,
                          codeId=1,
                          system="x",
                          value="b",
                          display=u"c",
                          topic=u"d",
                          codeType=CodeType.MODULE,
                          mapped=True)
        with self.assertRaises(BadRequest):
            self.code_dao.update(new_code_1)

    def test_update_codes_new_codebook_id(self):
        code_book_1 = CodeBook(name="pmi", version="v1", system="a")
        with FakeClock(TIME):
            self.code_book_dao.insert(code_book_1)
        code_1 = Code(codeBookId=1,
                      system="a",
                      value="b",
                      display=u"c",
                      topic=u"d",
                      codeType=CodeType.MODULE,
                      mapped=True)
        with FakeClock(TIME_2):
            self.code_dao.insert(code_1)

        code_book_2 = CodeBook(name="pmi", version="v2", system="a")
        with FakeClock(TIME_3):
            self.code_book_dao.insert(code_book_2)

        new_code_1 = Code(codeBookId=2,
                          codeId=1,
                          system="x",
                          value="b",
                          display=u"c",
                          topic=u"d",
                          codeType=CodeType.MODULE,
                          mapped=True)
        with FakeClock(TIME_4):
            self.code_dao.update(new_code_1)

        expected_code = Code(codeBookId=2,
                             codeId=1,
                             system="x",
                             value="b",
                             display=u"c",
                             topic=u"d",
                             codeType=CodeType.MODULE,
                             mapped=True,
                             created=TIME_2)
        self.assertEquals(expected_code.asdict(),
                          self.code_dao.get(1).asdict())

        expected_code_history = CodeHistory(codeBookId=1,
                                            codeHistoryId=1,
                                            codeId=1,
                                            system="a",
                                            value="b",
                                            display=u"c",
                                            topic=u"d",
                                            codeType=CodeType.MODULE,
                                            mapped=True,
                                            created=TIME_2)
        self.assertEquals(expected_code_history.asdict(),
                          self.code_history_dao.get(1).asdict())

        expected_code_history_2 = CodeHistory(codeHistoryId=2,
                                              codeBookId=2,
                                              codeId=1,
                                              system="x",
                                              value="b",
                                              display=u"c",
                                              topic=u"d",
                                              codeType=CodeType.MODULE,
                                              mapped=True,
                                              created=TIME_2)
        self.assertEquals(expected_code_history_2.asdict(),
                          self.code_history_dao.get(2).asdict())

    def test_import_codebook(self):
        answer_1 = _make_concept(u"t1", "Answer", "c1", u"d1")
        answer_2 = _make_concept(u"t2", "Answer", "c2", u"d2")
        answer_3 = _make_concept(u"t2", "Answer", "c3", u"d3")
        question_1 = _make_concept(u"t1", "Question", "q1", u"d4", [answer_1])
        question_2 = _make_concept(u"t2", "Question", "q2", u"d5",
                                   [answer_2, answer_3])
        topic_1 = _make_concept(u"t1", "Topic", "t1", u"d6", [question_1])
        module_1 = _make_concept(u"mt1", "Module Name", "m1", u"d7", [topic_1])
        module_2 = _make_concept(u"mt2", "Module Name", "m2", u"d8",
                                 [question_2])
        system = 'http://blah/foo'
        codebook = {
            'name': 'pmi',
            'version': 'v1',
            'url': system,
            'concept': [module_1, module_2]
        }
        with FakeClock(TIME):
            self.code_book_dao.import_codebook(codebook)

        expectedCodeBook = CodeBook(codeBookId=1,
                                    latest=True,
                                    created=TIME,
                                    name="pmi",
                                    version="v1",
                                    system=system)
        self.assertEquals(expectedCodeBook.asdict(),
                          self.code_book_dao.get(1).asdict())

        expectedModule1 = Code(codeBookId=1,
                               codeId=1,
                               system=system,
                               value="m1",
                               shortValue="m1",
                               display=u"d7",
                               topic=u"mt1",
                               codeType=CodeType.MODULE,
                               mapped=True,
                               created=TIME)
        self.assertEquals(expectedModule1.asdict(),
                          self.code_dao.get(1).asdict())

        expectedModuleHistory1 = CodeHistory(codeHistoryId=1,
                                             codeBookId=1,
                                             codeId=1,
                                             system=system,
                                             value="m1",
                                             shortValue="m1",
                                             display=u"d7",
                                             topic=u"mt1",
                                             codeType=CodeType.MODULE,
                                             mapped=True,
                                             created=TIME)
        self.assertEquals(expectedModuleHistory1.asdict(),
                          self.code_history_dao.get(1).asdict())

        expectedTopic1 = Code(codeBookId=1,
                              codeId=2,
                              system=system,
                              value="t1",
                              shortValue="t1",
                              display=u"d6",
                              topic=u"t1",
                              codeType=CodeType.TOPIC,
                              mapped=True,
                              created=TIME,
                              parentId=1)
        self.assertEquals(expectedTopic1.asdict(),
                          self.code_dao.get(2).asdict())

        expectedQuestion1 = Code(codeBookId=1,
                                 codeId=3,
                                 system=system,
                                 value="q1",
                                 shortValue="q1",
                                 display=u"d4",
                                 topic=u"t1",
                                 codeType=CodeType.QUESTION,
                                 mapped=True,
                                 created=TIME,
                                 parentId=2)
        self.assertEquals(expectedQuestion1.asdict(),
                          self.code_dao.get(3).asdict())

        expectedAnswer1 = Code(codeBookId=1,
                               codeId=4,
                               system=system,
                               value="c1",
                               shortValue="c1",
                               display=u"d1",
                               topic=u"t1",
                               codeType=CodeType.ANSWER,
                               mapped=True,
                               created=TIME,
                               parentId=3)
        self.assertEquals(expectedAnswer1.asdict(),
                          self.code_dao.get(4).asdict())