def _update_participant_summary(self, session, questionnaire_response, code_ids, questions, questionnaire_history, resource_json): """Updates the participant summary based on questions answered and modules completed in the questionnaire response. If no participant summary exists already, only a response to the study enrollment consent questionnaire can be submitted, and it must include first and last name and e-mail address. """ # Block on other threads modifying the participant or participant summary. participant = ParticipantDao().get_for_update( session, questionnaire_response.participantId) if participant is None: raise BadRequest('Participant with ID %d is not found.' % questionnaire_response.participantId) participant_summary = participant.participantSummary code_ids.extend( [concept.codeId for concept in questionnaire_history.concepts]) code_dao = CodeDao() something_changed = False # If no participant summary exists, make sure this is the study enrollment consent. if not participant_summary: consent_code = code_dao.get_code( PPI_SYSTEM, CONSENT_FOR_STUDY_ENROLLMENT_MODULE) if not consent_code: raise BadRequest( 'No study enrollment consent code found; import codebook.') if not consent_code.codeId in code_ids: raise BadRequest( "Can't submit order for participant %s without consent" % questionnaire_response.participantId) raise_if_withdrawn(participant) participant_summary = ParticipantDao.create_summary_for_participant( participant) something_changed = True else: raise_if_withdrawn(participant_summary) # Fetch the codes for all questions and concepts codes = code_dao.get_with_ids(code_ids) code_map = { code.codeId: code for code in codes if code.system == PPI_SYSTEM } question_map = { question.questionnaireQuestionId: question for question in questions } race_code_ids = [] ehr_consent = False dvehr_consent = QuestionnaireStatus.SUBMITTED_NO_CONSENT # Set summary fields for answers that have questions with codes found in QUESTION_CODE_TO_FIELD for answer in questionnaire_response.answers: question = question_map.get(answer.questionId) if question: code = code_map.get(question.codeId) if code: summary_field = QUESTION_CODE_TO_FIELD.get(code.value) if summary_field: if something_changed: self._update_field(participant_summary, summary_field[0], summary_field[1], answer) else: something_changed = self._update_field( participant_summary, summary_field[0], summary_field[1], answer) elif code.value == RACE_QUESTION_CODE: race_code_ids.append(answer.valueCodeId) elif code.value == DVEHR_SHARING_QUESTION_CODE: code = code_dao.get(answer.valueCodeId) if code and code.value == DVEHRSHARING_CONSENT_CODE_YES: dvehr_consent = QuestionnaireStatus.SUBMITTED elif code and code.value == DVEHRSHARING_CONSENT_CODE_NOT_SURE: dvehr_consent = QuestionnaireStatus.SUBMITTED_NOT_SURE elif code.value == EHR_CONSENT_QUESTION_CODE: code = code_dao.get(answer.valueCodeId) if code and code.value == CONSENT_PERMISSION_YES_CODE: ehr_consent = True elif code.value == CABOR_SIGNATURE_QUESTION_CODE: if answer.valueUri or answer.valueString: # TODO: validate the URI? [DA-326] if not participant_summary.consentForCABoR: participant_summary.consentForCABoR = True participant_summary.consentForCABoRTime = questionnaire_response.created something_changed = True # If race was provided in the response in one or more answers, set the new value. if race_code_ids: race_codes = [code_dao.get(code_id) for code_id in race_code_ids] race = get_race(race_codes) if race != participant_summary.race: participant_summary.race = race something_changed = True # Set summary fields to SUBMITTED for questionnaire concepts that are found in # QUESTIONNAIRE_MODULE_CODE_TO_FIELD module_changed = False for concept in questionnaire_history.concepts: code = code_map.get(concept.codeId) if code: summary_field = QUESTIONNAIRE_MODULE_CODE_TO_FIELD.get( code.value) if summary_field: new_status = QuestionnaireStatus.SUBMITTED if code.value == CONSENT_FOR_ELECTRONIC_HEALTH_RECORDS_MODULE and not ehr_consent: new_status = QuestionnaireStatus.SUBMITTED_NO_CONSENT elif code.value == CONSENT_FOR_DVEHR_MODULE: new_status = dvehr_consent elif code.value == CONSENT_FOR_STUDY_ENROLLMENT_MODULE: # set language of consent to participant summary for extension in resource_json.get('extension', []): if extension.get('url') == _LANGUAGE_EXTENSION and \ extension.get('valueCode') in LANGUAGE_OF_CONSENT: if participant_summary.primaryLanguage != extension.get( 'valueCode'): participant_summary.primaryLanguage = extension.get( 'valueCode') something_changed = True break elif extension.get('url') == _LANGUAGE_EXTENSION and \ extension.get('valueCode') not in LANGUAGE_OF_CONSENT: logging.warn( 'consent language %s not recognized.' % extension.get('valueCode')) if getattr(participant_summary, summary_field) != new_status: setattr(participant_summary, summary_field, new_status) setattr(participant_summary, summary_field + 'Time', questionnaire_response.created) something_changed = True module_changed = True if module_changed: participant_summary.numCompletedBaselinePPIModules = \ count_completed_baseline_ppi_modules(participant_summary) participant_summary.numCompletedPPIModules = \ count_completed_ppi_modules(participant_summary) if something_changed: first_last = (participant_summary.firstName, participant_summary.lastName) email_phone = (participant_summary.email, participant_summary.loginPhoneNumber) if not all(first_last): raise BadRequest( 'First name (%s), and last name (%s) required for consenting.' % tuple([ 'present' if part else 'missing' for part in first_last ])) if not any(email_phone): raise BadRequest( 'Email address (%s), or phone number (%s) required for consenting.' % tuple([ 'present' if part else 'missing' for part in email_phone ])) ParticipantSummaryDao().update_enrollment_status( participant_summary) participant_summary.lastModified = clock.CLOCK.now() session.merge(participant_summary) # switch account to test account if the phone number is start with 444 # this is a requirement from PTSC if participant_summary.loginPhoneNumber is not None and \ participant_summary.loginPhoneNumber.startswith(TEST_LOGIN_PHONE_NUMBER_PREFIX): ParticipantDao().switch_to_test_account( session, participant_summary.participantId)
def _update_participant_summary( self, session, questionnaire_response, code_ids, questions, questionnaire_history): """Updates the participant summary based on questions answered and modules completed in the questionnaire response. If no participant summary exists already, only a response to the study enrollment consent questionnaire can be submitted, and it must include first and last name and e-mail address. """ # Block on other threads modifying the participant or participant summary. participant = ParticipantDao().get_for_update(session, questionnaire_response.participantId) if participant is None: raise BadRequest('Participant with ID %d is not found.' % questionnaire_response.participantId) participant_summary = participant.participantSummary code_ids.extend([concept.codeId for concept in questionnaire_history.concepts]) code_dao = CodeDao() something_changed = False # If no participant summary exists, make sure this is the study enrollment consent. if not participant_summary: consent_code = code_dao.get_code(PPI_SYSTEM, CONSENT_FOR_STUDY_ENROLLMENT_MODULE) if not consent_code: raise BadRequest('No study enrollment consent code found; import codebook.') if not consent_code.codeId in code_ids: raise BadRequest("Can't submit order for participant %s without consent" % questionnaire_response.participantId) raise_if_withdrawn(participant) participant_summary = ParticipantDao.create_summary_for_participant(participant) something_changed = True else: raise_if_withdrawn(participant_summary) # Fetch the codes for all questions and concepts codes = code_dao.get_with_ids(code_ids) code_map = {code.codeId: code for code in codes if code.system == PPI_SYSTEM} question_map = {question.questionnaireQuestionId: question for question in questions} race_code_ids = [] ehr_consent = False # Set summary fields for answers that have questions with codes found in QUESTION_CODE_TO_FIELD for answer in questionnaire_response.answers: question = question_map.get(answer.questionId) if question: code = code_map.get(question.codeId) if code: summary_field = QUESTION_CODE_TO_FIELD.get(code.value) if summary_field: something_changed = self._update_field(participant_summary, summary_field[0], summary_field[1], answer) elif code.value == RACE_QUESTION_CODE: race_code_ids.append(answer.valueCodeId) elif code.value == EHR_CONSENT_QUESTION_CODE: code = code_dao.get(answer.valueCodeId) if code and code.value == CONSENT_PERMISSION_YES_CODE: ehr_consent = True elif code.value == CABOR_SIGNATURE_QUESTION_CODE: if answer.valueUri or answer.valueString: # TODO: validate the URI? [DA-326] if not participant_summary.consentForCABoR: participant_summary.consentForCABoR = True participant_summary.consentForCABoRTime = questionnaire_response.created something_changed = True # If race was provided in the response in one or more answers, set the new value. if race_code_ids: race_codes = [code_dao.get(code_id) for code_id in race_code_ids] race = get_race(race_codes) if race != participant_summary.race: participant_summary.race = race something_changed = True # Set summary fields to SUBMITTED for questionnaire concepts that are found in # QUESTIONNAIRE_MODULE_CODE_TO_FIELD module_changed = False for concept in questionnaire_history.concepts: code = code_map.get(concept.codeId) if code: summary_field = QUESTIONNAIRE_MODULE_CODE_TO_FIELD.get(code.value) if summary_field: new_status = QuestionnaireStatus.SUBMITTED if code.value == CONSENT_FOR_ELECTRONIC_HEALTH_RECORDS_MODULE and not ehr_consent: new_status = QuestionnaireStatus.SUBMITTED_NO_CONSENT if getattr(participant_summary, summary_field) != new_status: setattr(participant_summary, summary_field, new_status) setattr(participant_summary, summary_field + 'Time', questionnaire_response.created) something_changed = True module_changed = True if module_changed: participant_summary.numCompletedBaselinePPIModules = \ count_completed_baseline_ppi_modules(participant_summary) participant_summary.numCompletedPPIModules = \ count_completed_ppi_modules(participant_summary) if something_changed: first_last_email = ( participant_summary.firstName, participant_summary.lastName, participant_summary.email) if not all(first_last_email): raise BadRequest( 'First name (%s), last name (%s), and email address (%s) required for consenting.' % tuple(['present' if part else 'missing' for part in first_last_email])) ParticipantSummaryDao().update_enrollment_status(participant_summary) participant_summary.lastModified = clock.CLOCK.now() session.merge(participant_summary)