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")
Beispiel #2
0
    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
Beispiel #3
0
 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
Beispiel #4
0
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,
        )
Beispiel #5
0
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")
Beispiel #6
0
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")
Beispiel #7
0
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)
Beispiel #8
0
        <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)
Beispiel #9
0
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)