def _update_participant_summary(self, session, obj): participant_id = obj.participantId if participant_id is None: raise BadRequest('participantId is required') participant_summary_dao = ParticipantSummaryDao() participant = ParticipantDao().get_for_update(session, participant_id) if not participant: raise BadRequest( "Can't submit physical measurements for unknown participant %s" % participant_id) participant_summary = participant.participantSummary if not participant_summary: raise BadRequest( "Can't submit physical measurements for participant %s without consent" % participant_id) raise_if_withdrawn(participant_summary) participant_summary.physicalMeasurementsTime = obj.created participant_summary.physicalMeasurementsFinalizedTime = obj.finalized participant_summary.physicalMeasurementsCreatedSiteId = obj.createdSiteId participant_summary.physicalMeasurementsFinalizedSiteId = obj.finalizedSiteId participant_summary.lastModified = clock.CLOCK.now() if participant_summary.physicalMeasurementsStatus != PhysicalMeasurementsStatus.COMPLETED: participant_summary.physicalMeasurementsStatus = PhysicalMeasurementsStatus.COMPLETED participant_summary_dao.update_enrollment_status( participant_summary) session.merge(participant_summary) return participant_summary
def _update_participant_summary(self, session, obj, is_amendment=False): participant_id = obj.participantId if participant_id is None: raise BadRequest('participantId is required') participant_summary_dao = ParticipantSummaryDao() participant = ParticipantDao().get_for_update(session, participant_id) if not participant: raise BadRequest( "Can't submit physical measurements for unknown participant %s" % participant_id) participant_summary = participant.participantSummary if not participant_summary: raise BadRequest( "Can't submit physical measurements for participant %s without consent" % participant_id) raise_if_withdrawn(participant_summary) participant_summary.lastModified = clock.CLOCK.now() is_distinct_visit = participant_summary_dao.calculate_distinct_visits( participant_id, obj.finalized, obj.physicalMeasurementsId) if obj.status and obj.status == PhysicalMeasurementsStatus.CANCELLED and is_distinct_visit \ and not is_amendment: participant_summary.numberDistinctVisits -= 1 # These fields set on measurement that is cancelled and doesn't have a previous good measurement if obj.status and obj.status == PhysicalMeasurementsStatus.CANCELLED and not \ self.has_uncancelled_pm(session, participant): participant_summary.physicalMeasurementsStatus = PhysicalMeasurementsStatus.CANCELLED participant_summary.physicalMeasurementsTime = None participant_summary.physicalMeasurementsFinalizedSiteId = None # These fields set on any measurement not cancelled elif obj.status != PhysicalMeasurementsStatus.CANCELLED: # new PM or if a PM was restored, it is complete again. participant_summary.physicalMeasurementsStatus = PhysicalMeasurementsStatus.COMPLETED participant_summary.physicalMeasurementsTime = obj.created participant_summary.physicalMeasurementsFinalizedTime = obj.finalized participant_summary.physicalMeasurementsCreatedSiteId = obj.createdSiteId participant_summary.physicalMeasurementsFinalizedSiteId = obj.finalizedSiteId if is_distinct_visit and not is_amendment: participant_summary.numberDistinctVisits += 1 elif obj.status and obj.status == PhysicalMeasurementsStatus.CANCELLED and \ self.has_uncancelled_pm(session, participant): get_latest_pm = self.get_latest_pm(session, participant) participant_summary.physicalMeasurementsFinalizedTime = get_latest_pm.finalized participant_summary.physicalMeasurementsTime = get_latest_pm.created participant_summary.physicalMeasurementsCreatedSiteId = get_latest_pm.createdSiteId participant_summary.physicalMeasurementsFinalizedSiteId = get_latest_pm.finalizedSiteId participant_summary_dao.update_enrollment_status(participant_summary) session.merge(participant_summary) return participant_summary
def _update_participant_summary(self, session, obj): participant_summary_dao = ParticipantSummaryDao() participant_summary = participant_summary_dao.get_for_update( session, obj.participantId) if not participant_summary: raise BadRequest( "Can't submit biospecimens for participant %s without consent" % obj.participantId) raise_if_withdrawn(participant_summary) self._set_participant_summary_fields(obj, participant_summary) participant_summary_dao.update_enrollment_status(participant_summary)
def _update_participant_summary(self, session, obj): """ called on insert""" participant_summary_dao = ParticipantSummaryDao() participant_summary = participant_summary_dao.get_for_update( session, obj.participantId) if not participant_summary: raise BadRequest( "Can't submit biospecimens for participant %s without consent" % obj.participantId) raise_if_withdrawn(participant_summary) self._set_participant_summary_fields(obj, participant_summary) participant_summary_dao.update_enrollment_status(participant_summary) finalized_time = self.get_random_sample_finalized_time(obj) is_distinct_visit = ParticipantSummaryDao().calculate_distinct_visits( participant_summary.participantId, finalized_time, obj.biobankOrderId) if is_distinct_visit: participant_summary.numberDistinctVisits += 1
def _update_participant_summary(self, session, created, participant_id): if participant_id is None: raise BadRequest('participantId is required') participant_summary_dao = ParticipantSummaryDao() participant = ParticipantDao().get_for_update(session, participant_id) if not participant: raise BadRequest("Can't submit physical measurements for unknown participant %s" % participant_id) participant_summary = participant.participantSummary if not participant_summary: raise BadRequest("Can't submit physical measurements for participant %s without consent" % participant_id) raise_if_withdrawn(participant_summary) if (not participant_summary.physicalMeasurementsStatus or participant_summary.physicalMeasurementsStatus == PhysicalMeasurementsStatus.UNSET): participant_summary.physicalMeasurementsStatus = PhysicalMeasurementsStatus.COMPLETED if not participant_summary.physicalMeasurementsTime: participant_summary.physicalMeasurementsTime = created participant_summary_dao.update_enrollment_status(participant_summary) session.merge(participant_summary)
def _update_participant_summary(self, session, obj): participant_summary_dao = ParticipantSummaryDao() participant_summary = participant_summary_dao.get_for_update( session, obj.participantId) if not participant_summary: raise BadRequest( "Can't submit biospecimens for participant %s without consent" % obj.participantId) raise_if_withdrawn(participant_summary) participant_summary.biospecimenStatus = OrderStatus.FINALIZED participant_summary.biospecimenOrderTime = obj.created participant_summary.biospecimenSourceSiteId = obj.sourceSiteId participant_summary.biospecimenCollectedSiteId = obj.collectedSiteId participant_summary.biospecimenProcessedSiteId = obj.processedSiteId participant_summary.biospecimenFinalizedSiteId = obj.finalizedSiteId participant_summary.lastModified = clock.CLOCK.now() for sample in obj.samples: status_field = 'sampleOrderStatus' + sample.test status, time = self._get_order_status_and_time(sample, obj) setattr(participant_summary, status_field, status) setattr(participant_summary, status_field + 'Time', time)
def _validate_model(self, session, obj): if obj.participantId is None: raise BadRequest('participantId is required') participant_summary = ParticipantSummaryDao().get_with_session( session, obj.participantId) if not participant_summary: raise BadRequest( "Can't submit order for participant %s without consent" % obj.participantId) raise_if_withdrawn(participant_summary) for sample in obj.samples: self._validate_order_sample(sample) # TODO(mwf) FHIR validation for identifiers? # Verify that no identifier is in use by another order. for identifier in obj.identifiers: for existing in (session.query(BiobankOrderIdentifier).filter_by( system=identifier.system).filter_by( value=identifier.value).filter( BiobankOrderIdentifier.biobankOrderId != obj.biobankOrderId)): raise BadRequest( 'Identifier %s is already in use by order %s' % (identifier, existing.biobankOrderId))
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)