def send_dhis2_entities(requests, repeater, case_trigger_infos): """ Send request to register / update tracked entities """ errors = [] for info in case_trigger_infos: assert isinstance(info, CaseTriggerInfo) case_config = get_case_config_for_case_type( info.type, repeater.dhis2_entity_config) if not case_config: # This payload includes a case of a case type that does not correspond to a tracked entity type continue try: tracked_entity, etag = get_tracked_entity_and_etag( requests, info, case_config) if tracked_entity: update_tracked_entity_instance(requests, tracked_entity, etag, info, case_config) else: register_tracked_entity_instance(requests, info, case_config) except (Dhis2Exception, HTTPError) as err: errors.append(str(err)) if errors: errors_str = f"Errors sending to {repeater}: " + pformat_json( [str(e) for e in errors]) requests.notify_error(errors_str) return RepeaterResponse(400, 'Bad Request', errors_str) return RepeaterResponse(200, "OK")
def send_request(self, repeat_record, payload): """ Generates FHIR resources from ``payload``, and sends them as a FHIR transaction bundle. If there are patients that need to be registered, that is done first. Returns an HTTP response-like object. If the payload has nothing to send, returns True. """ requests = self.connection_settings.get_requests( repeat_record.payload_id) infos, resource_types = self.get_infos_resource_types( payload, self.fhir_version, ) try: resources = get_info_resource_list(infos, resource_types) resources = register_patients( requests, resources, self.patient_registration_enabled, self.patient_search_enabled, self._id, ) response = send_resources( requests, resources, self.fhir_version, self._id, ) except Exception as err: requests.notify_exception(str(err)) return RepeaterResponse(400, 'Bad Request', pformat_json(str(err))) return response
def send_request(self, repeat_record, payload): value_source_configs: Iterable[JsonDict] = chain( self.openmrs_config.case_config.patient_identifiers.values(), self.openmrs_config.case_config.person_properties.values(), self.openmrs_config.case_config.person_preferred_name.values(), self.openmrs_config.case_config.person_preferred_address.values(), self.openmrs_config.case_config.person_attributes.values(), ) case_trigger_infos = get_relevant_case_updates_from_form_json( self.domain, payload, case_types=self.white_listed_case_types, extra_fields=[ conf["case_property"] for conf in value_source_configs if "case_property" in conf ], form_question_values=get_form_question_values(payload), ) requests = get_requests(self, repeat_record.payload_id) try: response = send_openmrs_data( requests, self.domain, payload, self.openmrs_config, case_trigger_infos, ) except Exception as err: requests.notify_exception(str(err)) return RepeaterResponse(400, 'Bad Request', pformat_json(str(err))) return response
def _check_id(patient): if 'id' not in patient: response = RepeaterResponse(status_code=500, reason='Bad response') raise HTTPError( 'Remote service returned a patient missing an "id" property', response=response, )
def send_dhis2_entities(requests, repeater, case_trigger_infos): """ Send request to register / update tracked entities """ errors = [] info_config_pairs = _get_info_config_pairs(repeater, case_trigger_infos) for info, case_config in info_config_pairs: try: tracked_entity, etag = get_tracked_entity_and_etag( requests, info, case_config) if tracked_entity: update_tracked_entity_instance(requests, tracked_entity, etag, info, case_config) else: register_tracked_entity_instance(requests, info, case_config) except (Dhis2Exception, HTTPError) as err: errors.append(str(err)) # Create relationships after handling tracked entity instances, to # ensure that both the instances in the relationship have been created. for info, case_config in info_config_pairs: if not case_config.relationships_to_export: continue try: create_relationships( requests, info, case_config, repeater.dhis2_entity_config, ) except (Dhis2Exception, HTTPError) as err: errors.append(str(err)) if errors: errors_str = f"Errors sending to {repeater}: " + pformat_json( [str(e) for e in errors]) requests.notify_error(errors_str) return RepeaterResponse(400, 'Bad Request', errors_str) return RepeaterResponse(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 RepeaterResponse( 400, 'Bad Request', "Errors: " + pformat_json([str(e) for e in errors])) if warnings: return RepeaterResponse( 201, "Accepted", "Warnings: " + pformat_json([str(e) for e in warnings])) return RepeaterResponse(200, "OK")
def test_get_response(self, status_code, text, expected_response): response = ReferCaseRepeater.get_response( RepeaterResponse(status_code, responses[status_code], text)) self.assertEqual(response.status_code, expected_response.status_code) self.assertEqual(response.reason, expected_response.reason) self.assertEqual(response.retry, expected_response.retry)
<message nature="{ResponseNature.PROCESSING_FAILURE}">success</message> </OpenRosaResponse> """ V2_ERROR = f""" <OpenRosaResponse xmlns="http://openrosa.org/http/response"> <message nature="{ResponseNature.SUBMIT_ERROR}">success</message> </OpenRosaResponse> """ class TestReferCaseRepeater(SimpleTestCase): pass @generate_cases([ (201, SUCCESS, RepeaterResponse(201, 'Created', '')), (201, V2_ERROR, RepeaterResponse(422, ResponseNature.SUBMIT_ERROR, '', False)), (422, V3_ERROR, RepeaterResponse(422, ResponseNature.PROCESSING_FAILURE, '', False)), (422, V3_RETRY_ERROR, RepeaterResponse(422, ResponseNature.POST_PROCESSING_FAILURE, '', True)), ], TestReferCaseRepeater) def test_get_response(self, status_code, text, expected_response): response = ReferCaseRepeater.get_response( RepeaterResponse(status_code, responses[status_code], text)) self.assertEqual(response.status_code, expected_response.status_code) self.assertEqual(response.reason, expected_response.reason) self.assertEqual(response.retry, expected_response.retry)
def test_get_response(self, status_code, text, expected_response): response = get_repeater_response_from_submission_response( RepeaterResponse(status_code, responses[status_code], text)) self.assertEqual(response.status_code, expected_response.status_code) self.assertEqual(response.reason, expected_response.reason) self.assertEqual(response.retry, expected_response.retry)