def test_identifier_case_property_deleted(self, get_patient_mock, case_blocks_mock): """ CommCare should drop the case's patient ID when an OpenMRS patient is voided """ requests = None info = CaseTriggerInfo( domain=self.case.domain, case_id=self.case.case_id, type=self.case.type, name=self.case.name, extra_fields={ "external_id": self.case.external_id, }, ) openmrs_config = OpenmrsConfig.wrap({ "case_config": { "patient_finder": {}, "patient_identifiers": { "uuid": { "case_property": "external_id" }, }, "match_on_ids": ["uuid"], "person_properties": {}, "person_preferred_address": {}, "person_preferred_name": {}, }, "form_configs": [], }) voided_patient = self.get_json("voided_patient") get_patient_mock.return_value = voided_patient patient = get_patient(requests, DOMAIN, info, openmrs_config) self.assertEqual(info.extra_fields, {"external_id": None}) case_block_re = strip_xml(f""" <case case_id="123456" » date_modified="{DATETIME_PATTERN}" » xmlns="http://commcarehq.org/case/transaction/v2"> <update> <external_id /> </update> </case> """) case_block = case_blocks_mock.call_args[0][0][0] self.assertIsNotNone(re.match(case_block_re, case_block)) self.assertIsNone(patient)
def sync_openmrs_patient(requests, info, openmrs_config, problem_log): patient = get_patient(requests, info, openmrs_config, problem_log) person_uuid = patient['person']['uuid'] update_person_properties(requests, info, openmrs_config, person_uuid) name_uuid = patient['person']['preferredName']['uuid'] update_person_name(requests, info, openmrs_config, person_uuid, name_uuid) address_uuid = patient['person']['preferredAddress']['uuid'] if patient[ 'person']['preferredAddress'] else None if address_uuid: update_person_address(requests, info, openmrs_config, person_uuid, address_uuid) else: create_person_address(requests, info, openmrs_config, person_uuid) sync_person_attributes(requests, info, openmrs_config, person_uuid, patient['person']['attributes']) return person_uuid
def sync_openmrs_patient(requests, info, openmrs_config, problem_log): patient = get_patient(requests, info, openmrs_config, problem_log) if patient is None: raise ValueError('CommCare patient was not found in OpenMRS') person_uuid = patient['person']['uuid'] update_person_properties(requests, info, openmrs_config, person_uuid) name_uuid = patient['person']['preferredName']['uuid'] update_person_name(requests, info, openmrs_config, person_uuid, name_uuid) address_uuid = patient['person']['preferredAddress']['uuid'] if patient[ 'person']['preferredAddress'] else None if address_uuid: update_person_address(requests, info, openmrs_config, person_uuid, address_uuid) else: create_person_address(requests, info, openmrs_config, person_uuid) sync_person_attributes(requests, info, openmrs_config, person_uuid, patient['person']['attributes']) return person_uuid
def sync_openmrs_patient(requests, info, form_json, form_question_values, openmrs_config): patient = get_patient(requests, info, openmrs_config) if patient is None: raise ValueError('CommCare patient was not found in OpenMRS') person_uuid = patient['person']['uuid'] logger.debug('OpenMRS patient found: ', person_uuid) update_person_properties(requests, info, openmrs_config, person_uuid) name_uuid = patient['person']['preferredName']['uuid'] update_person_name(requests, info, openmrs_config, person_uuid, name_uuid) address_uuid = patient['person']['preferredAddress']['uuid'] if patient['person']['preferredAddress'] else None if address_uuid: update_person_address(requests, info, openmrs_config, person_uuid, address_uuid) else: create_person_address(requests, info, openmrs_config, person_uuid) sync_person_attributes(requests, info, openmrs_config, person_uuid, patient['person']['attributes']) create_visits(requests, info, form_json, form_question_values, openmrs_config, person_uuid) return OpenmrsResponse(200, 'OK')
def send_openmrs_data(requests, domain, form_json, openmrs_config, case_trigger_infos): """ Updates an OpenMRS patient and (optionally) creates visits. This involves several requests to the `OpenMRS REST Web Services`_. If any of those requests fail, we want to roll back previous changes to avoid inconsistencies in OpenMRS. To do this we define a workflow of tasks we want to do. Each workflow task has a rollback task. If a task fails, all previous tasks are rolled back in reverse order. :return: A response-like object that can be used by Repeater.handle_response(), RepeatRecord.handle_success() and RepeatRecord.handle_failure() .. _OpenMRS REST Web Services: https://wiki.openmrs.org/display/docs/REST+Web+Services+API+For+Clients """ warnings = [] errors = [] for info in case_trigger_infos: assert isinstance(info, CaseTriggerInfo) try: patient = get_patient(requests, domain, info, openmrs_config) except (RequestException, HTTPError) as err: errors.append( _("Unable to create an OpenMRS patient for case " f"{info.case_id!r}: {err}")) continue if patient is None: warnings.append( f"CommCare case '{info.case_id}' was not matched to a " f"patient in OpenMRS instance '{requests.base_url}'.") continue # case_trigger_infos are info about all of the cases # created/updated by the form. Execute a separate workflow to # update each patient. workflow = [ # Update name first. If the current name in OpenMRS fails # validation, other API requests will be rejected. UpdatePersonNameTask(requests, info, openmrs_config, patient['person']), # Update identifiers second. If a current identifier fails # validation, other API requests will be rejected. SyncPatientIdentifiersTask(requests, info, openmrs_config, patient), # Now we should be able to update the rest. UpdatePersonPropertiesTask(requests, info, openmrs_config, patient['person']), SyncPersonAttributesTask(requests, info, openmrs_config, patient['person']['uuid'], patient['person']['attributes']), ] if patient['person']['preferredAddress']: workflow.append( UpdatePersonAddressTask(requests, info, openmrs_config, patient['person'])) else: workflow.append( CreatePersonAddressTask(requests, info, openmrs_config, patient['person'])) workflow.append( CreateVisitsEncountersObsTask(requests, domain, info, form_json, openmrs_config, patient['person']['uuid']), ) errors.extend(execute_workflow(workflow)) if errors: requests.notify_error( f'Errors encountered sending OpenMRS data: {errors}') # If the form included multiple patients, some workflows may # have succeeded, but don't say everything was OK if any # workflows failed. (Of course most forms will only involve one # case, so one workflow.) return OpenmrsResponse( 400, 'Bad Request', "Errors: " + pformat_json([str(e) for e in errors])) if warnings: return OpenmrsResponse( 201, "Accepted", "Warnings: " + pformat_json([str(e) for e in warnings])) return OpenmrsResponse(200, "OK")
def send_openmrs_data(requests, domain, form_json, openmrs_config, case_trigger_infos, form_question_values): """ Updates an OpenMRS patient and (optionally) creates visits. This involves several requests to the `OpenMRS REST Web Services`_. If any of those requests fail, we want to roll back previous changes to avoid inconsistencies in OpenMRS. To do this we define a workflow of tasks we want to do. Each workflow task has a rollback task. If a task fails, all previous tasks are rolled back in reverse order. :return: A response-like object that can be used by Repeater.handle_response .. _OpenMRS REST Web Services: https://wiki.openmrs.org/display/docs/REST+Web+Services+API+For+Clients """ errors = [] for info in case_trigger_infos: assert isinstance(info, CaseTriggerInfo) patient = get_patient(requests, domain, info, openmrs_config) if patient is None: errors.append('Warning: CommCare case "{}" was not found in OpenMRS'.format(info.case_id)) continue # case_trigger_infos are info about all of the cases # created/updated by the form. Execute a separate workflow to # update each patient. workflow = [ # Update name first. If the current name in OpenMRS fails # validation, other API requests will be rejected. UpdatePersonNameTask(requests, info, openmrs_config, patient['person']), # Update identifiers second. If a current identifier fails # validation, other API requests will be rejected. SyncPatientIdentifiersTask(requests, info, openmrs_config, patient), # Now we should be able to update the rest. UpdatePersonPropertiesTask(requests, info, openmrs_config, patient['person']), SyncPersonAttributesTask( requests, info, openmrs_config, patient['person']['uuid'], patient['person']['attributes'] ), ] if patient['person']['preferredAddress']: workflow.append( UpdatePersonAddressTask(requests, info, openmrs_config, patient['person']) ) else: workflow.append( CreatePersonAddressTask(requests, info, openmrs_config, patient['person']) ) workflow.append( CreateVisitsEncountersObsTask( requests, domain, info, form_json, form_question_values, openmrs_config, patient['person']['uuid'] ), ) errors.extend( execute_workflow(workflow) ) if errors: logger.error('Errors encountered sending OpenMRS data: %s', errors) # If the form included multiple patients, some workflows may # have succeeded, but don't say everything was OK if any # workflows failed. (Of course most forms will only involve one # case, so one workflow.) return OpenmrsResponse(400, 'Bad Request', pformat_json([str(e) for e in errors])) else: return OpenmrsResponse(200, 'OK', '')