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_with_session(self, session, questionnaire_response): questionnaire_history = ( QuestionnaireHistoryDao(). get_with_children_with_session(session, [questionnaire_response.questionnaireId, questionnaire_response.questionnaireVersion])) if not questionnaire_history: raise BadRequest('Questionnaire with ID %s, version %s is not found' % (questionnaire_response.questionnaireId, questionnaire_response.questionnaireVersion)) q_question_ids = set([ question.questionnaireQuestionId for question in questionnaire_history.questions]) for answer in questionnaire_response.answers: if answer.questionId not in q_question_ids: raise BadRequest('Questionnaire response contains question ID %s not in questionnaire.' % answer.questionId) questionnaire_response.created = clock.CLOCK.now() # Put the ID into the resource. resource_json = json.loads(questionnaire_response.resource) resource_json['id'] = str(questionnaire_response.questionnaireResponseId) questionnaire_response.resource = json.dumps(resource_json) question_ids = [answer.questionId for answer in questionnaire_response.answers] questions = QuestionnaireQuestionDao().get_all_with_session(session, question_ids) code_ids = [question.codeId for question in questions] current_answers = (QuestionnaireResponseAnswerDao(). get_current_answers_for_concepts(session, questionnaire_response.participantId, code_ids)) # IMPORTANT: update the participant summary first to grab an exclusive lock on the participant # row. If you insetad do this after the insert of the questionnaire response, MySQL will get a # shared lock on the participant row due the foreign key, and potentially deadlock later trying # to get the exclusive lock if another thread is updating the participant. See DA-269. # (We need to lock both participant and participant summary because the summary row may not # exist yet.) self._update_participant_summary( session, questionnaire_response, code_ids, questions, questionnaire_history) super(QuestionnaireResponseDao, self).insert_with_session(session, questionnaire_response) # Mark existing answers for the questions in this response given previously by this participant # as ended. for answer in current_answers: answer.endTime = questionnaire_response.created session.merge(answer) return questionnaire_response
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)
def insert_with_session(self, session, questionnaire_response): # Look for a questionnaire that matches any of the questionnaire history records. questionnaire_history = ( QuestionnaireHistoryDao().get_with_children_with_session( session, [ questionnaire_response.questionnaireId, questionnaire_response.questionnaireVersion ])) if not questionnaire_history: raise BadRequest( 'Questionnaire with ID %s, version %s is not found' % (questionnaire_response.questionnaireId, questionnaire_response.questionnaireVersion)) # Get the questions from the questionnaire history record. q_question_ids = set([ question.questionnaireQuestionId for question in questionnaire_history.questions ]) for answer in questionnaire_response.answers: if answer.questionId not in q_question_ids: raise BadRequest( 'Questionnaire response contains question ID %s not in questionnaire.' % answer.questionId) questionnaire_response.created = clock.CLOCK.now() if not questionnaire_response.authored: questionnaire_response.authored = questionnaire_response.created # Put the ID into the resource. resource_json = json.loads(questionnaire_response.resource) resource_json['id'] = str( questionnaire_response.questionnaireResponseId) questionnaire_response.resource = json.dumps(resource_json) # Gather the question ids and records that match the questions in the response question_ids = [ answer.questionId for answer in questionnaire_response.answers ] questions = QuestionnaireQuestionDao().get_all_with_session( session, question_ids) # DA-623: raise error when response link ids do not match our question link ids. # Gather the valid link ids for this question link_ids = [question.linkId for question in questions] # look through the response and verify each link id is valid for each question. self._validate_link_ids_from_resource_json_group( resource_json, link_ids) code_ids = [question.codeId for question in questions] current_answers = ( QuestionnaireResponseAnswerDao().get_current_answers_for_concepts( session, questionnaire_response.participantId, code_ids)) # IMPORTANT: update the participant summary first to grab an exclusive lock on the participant # row. If you insetad do this after the insert of the questionnaire response, MySQL will get a # shared lock on the participant row due the foreign key, and potentially deadlock later trying # to get the exclusive lock if another thread is updating the participant. See DA-269. # (We need to lock both participant and participant summary because the summary row may not # exist yet.) with self.session() as new_session: self._update_participant_summary(new_session, questionnaire_response, code_ids, questions, questionnaire_history, resource_json) super(QuestionnaireResponseDao, self).insert_with_session(session, questionnaire_response) # Mark existing answers for the questions in this response given previously by this participant # as ended. for answer in current_answers: answer.endTime = questionnaire_response.created session.merge(answer) return questionnaire_response