Esempio n. 1
0
def generate_identifier(requests, identifier_type):
    """
    Calls the idgen module's generateIdentifier endpoint

    Identifier source ID is determined from `identifier_type`. If
    `identifier_type` doesn't have an identifier source, return None.
    If the identifier source doesn't return an identifier, return None.
    If anything goes wrong ... return None.

    Partners in Health have written basic auth support for the idgen
    module, but it is not yet widely used. Until then, requests must use
    a session that has been authenticated with the HTML login page.
    """
    identifier = None
    source_id = None

    # Create a new Requests session to log in using an HTML login page.
    # See `authenticate_session()` for details.
    with Requests(
            domain_name=requests.domain_name,
            base_url=requests.base_url,
            verify=requests.verify,
            auth_manager=requests.auth_manager,
            notify_addresses=requests.notify_addresses,
            payload_id=requests.payload_id,
            logger=requests.logger,
    ) as requests_session:
        authenticate_session(requests_session)
        try:
            source_id = get_identifier_source_id(requests_session,
                                                 identifier_type)
        except OpenmrsHtmlUiChanged as err:
            requests.notify_exception('Unexpected OpenMRS HTML UI',
                                      details=str(err))
        if source_id:
            # Example request: http://www.example.com/openmrs/module/idgen/generateIdentifier.form?source=1
            response = requests_session.get(
                '/module/idgen/generateIdentifier.form',
                params={'source': source_id})
            try:
                if not (200 <= response.status_code < 300
                        and response.content):
                    raise OpenmrsException()
                try:
                    # Example response: {"identifiers": ["CHR203007"]}
                    identifier = response.json()['identifiers'][0]
                except (ValueError, IndexError, KeyError):
                    raise OpenmrsException()
            except OpenmrsException:
                requests.notify_exception(
                    'OpenMRS idgen module returned an unexpected response',
                    details=
                    (f'OpenMRS idgen module at "{response.url}" '
                     f'returned an unexpected response {response.status_code}: \r\n'
                     f'{response.content}'))
    return identifier
def generate_identifier(requests, identifier_type):
    """
    Calls the idgen module's generateIdentifier endpoint

    Identifier source ID is determined from `identifier_type`. If
    `identifier_type` doesn't have an identifier source, return None.
    If the identifier source doesn't return an identifier, return None.
    If anything goes wrong ... return None.

    The idgen module is not a REST API. It does not use API
    authentication. The user has to be logged in using the HTML login
    page, and the resulting authenticated session used for sending
    requests.
    """
    identifier = None
    source_id = None
    with Requests(domain_name=requests.domain_name,
                  base_url=requests.base_url,
                  username=requests.username,
                  password=requests.password,
                  verify=requests.verify) as requests_session:
        authenticate_session(requests_session)
        try:
            source_id = get_identifier_source_id(requests_session,
                                                 identifier_type)
        except OpenmrsHtmlUiChanged as err:
            notify_exception(
                request=None,
                message='Unexpected OpenMRS HTML UI',
                details=str(err),
            )
        if source_id:
            # Example request: http://www.example.com/openmrs/module/idgen/generateIdentifier.form?source=1
            response = requests_session.get(
                '/module/idgen/generateIdentifier.form',
                params={'source': source_id})
            try:
                if not (200 <= response.status_code < 300
                        and response.content):
                    raise OpenmrsException()
                try:
                    # Example response: {"identifiers": ["CHR203007"]}
                    identifier = response.json()['identifiers'][0]
                except (ValueError, IndexError, KeyError):
                    raise OpenmrsException()
            except OpenmrsException:
                notify_exception(
                    request=None,
                    message=
                    'OpenMRS idgen module returned an unexpected response',
                    details=
                    'OpenMRS idgen module at "{}" returned an unexpected response {}: "{}"'
                    .format(response.url, response.status_code,
                            response.content))
    return identifier
Esempio n. 3
0
def import_encounter(repeater, encounter_uuid):
    try:
        encounter = get_encounter(repeater, encounter_uuid)
    except (RequestException, ValueError) as err:
        raise OpenmrsException(
            _(f'{repeater.domain}: {repeater}: Error fetching Encounter '
              f'"{encounter_uuid}": {err}')) from err

    case_blocks = []
    patient_case_id, default_owner_id, patient_case_block = get_case_id_owner_id_case_block(
        repeater,
        encounter['patientUuid'],
    )
    if not patient_case_id:
        # No patient to create or update. Ignore this encounter.
        return
    if patient_case_block:
        case_blocks.append(patient_case_block)

    case_block_kwargs, more_case_blocks = get_case_block_kwargs_from_encounter(
        repeater,
        encounter,
        patient_case_id,
        default_owner_id,
    )
    case_blocks.extend(more_case_blocks)
    if has_case_updates(case_block_kwargs) or case_blocks:
        update_case(repeater, patient_case_id, case_block_kwargs, case_blocks)
Esempio n. 4
0
def import_encounter(repeater, encounter_uuid):
    try:
        encounter = get_encounter(repeater, encounter_uuid)
    except (RequestException, ValueError) as err:
        raise OpenmrsException(
            _(f'{repeater.domain}: {repeater}: Error fetching Encounter '
              f'"{encounter_uuid}": {err}')) from err

    case_block_kwargs = {"update": {}, "index": {}}
    case_blocks = []

    # NOTE: Atom Feed integration requires Patient UUID to be external_id
    case = get_case(repeater, encounter['patientUuid'])
    if case:
        case_id = case.case_id
        default_owner_id = case.owner_id
    else:
        case_block = create_case(repeater, encounter['patientUuid'])
        case_blocks.append(case_block)
        case_id = case_block.case_id
        default_owner_id = case_block.owner_id

    case_type = repeater.white_listed_case_types[0]
    more_kwargs, more_case_blocks = get_case_block_kwargs_from_observations(
        encounter['observations'],
        repeater.observation_mappings,
        case_id,
        case_type,
        default_owner_id,
    )
    deep_update(case_block_kwargs, more_kwargs)
    case_blocks.extend(more_case_blocks)

    if 'bahmniDiagnoses' in encounter:
        more_kwargs, more_case_blocks = get_case_block_kwargs_from_bahmni_diagnoses(
            encounter['bahmniDiagnoses'],
            repeater.diagnosis_mappings,
            case_id,
            case_type,
            default_owner_id,
        )
        deep_update(case_block_kwargs, more_kwargs)
        case_blocks.extend(more_case_blocks)
        # O/ ... ... ... start snip
        # TODO: Remove this after deploy, once existing configs have
        #       been moved to repeater.bahmni_diagnoses
        more_kwargs, more_case_blocks = get_case_block_kwargs_from_bahmni_diagnoses(
            encounter['bahmniDiagnoses'],
            repeater.observation_mappings,
            case_id,
            case_type,
            default_owner_id,
        )
        deep_update(case_block_kwargs, more_kwargs)
        case_blocks.extend(more_case_blocks)
        #  O\ ˙˙˙ ˙˙˙ ˙˙˙ end snip

    if has_case_updates(case_block_kwargs) or case_blocks:
        update_case(repeater, case_id, case_block_kwargs, case_blocks)
Esempio n. 5
0
def update_patient(repeater, patient_uuid):
    """
    Fetch patient from OpenMRS, submit case update for all mapped case
    properties.

    .. NOTE:: OpenMRS UUID must be saved to "external_id" case property

    """
    if len(repeater.white_listed_case_types) != 1:
        raise ConfigurationError(
            _(f'{repeater.domain}: {repeater}: Error in settings: Unable to update '
              f'patients from OpenMRS unless only one case type is specified.')
        )
    case_type = repeater.white_listed_case_types[0]
    try:
        patient = get_patient_by_uuid(repeater.requests, patient_uuid)
    except (RequestException, ValueError) as err:
        raise OpenmrsException(
            _(f'{repeater.domain}: {repeater}: Error fetching Patient '
              f'{patient_uuid!r}: {err}')) from err

    case, error = importer_util.lookup_case(
        EXTERNAL_ID,
        patient_uuid,
        repeater.domain,
        case_type=case_type,
    )
    if error == LookupErrors.NotFound:
        if not repeater.openmrs_config.case_config.import_creates_cases:
            # We can't create cases via the Atom feed, just update them.
            # Nothing to do here.
            return
        default_owner: Optional[CommCareUser] = repeater.first_user
        case_block = get_addpatient_caseblock(case_type, default_owner,
                                              patient, repeater)
    elif error == LookupErrors.MultipleResults:
        # Multiple cases have been matched to the same patient.
        # Could be caused by:
        # * The cases were given the same identifier value. It could
        #   be user error, or case config assumed identifier was
        #   unique but it wasn't.
        # * PatientFinder matched badly.
        # * Race condition where a patient was previously added to
        #   both CommCare and OpenMRS.
        raise DuplicateCaseMatch(
            _(f'{repeater.domain}: {repeater}: More than one case found '
              f'matching unique OpenMRS UUID. case external_id: "{patient_uuid}"'
              ))
    else:
        case_block = get_updatepatient_caseblock(case, patient, repeater)

    if case_block:
        submit_case_blocks(
            [case_block.as_text()],
            repeater.domain,
            xmlns=XMLNS_OPENMRS,
            device_id=OPENMRS_ATOM_FEED_DEVICE_ID + repeater.get_id,
        )
Esempio n. 6
0
def generate_identifier(requests, identifier_type):
    """
    Calls the idgen module's generateIdentifier endpoint

    Identifier source ID is determined from `identifier_type`. If
    `identifier_type` doesn't have an identifier source, return None.
    If the identifier source doesn't return an identifier, return None.
    If anything goes wrong ... return None.
    """
    try:
        source_id = get_identifier_source_id(requests, identifier_type)
    except OpenmrsHtmlUiChanged as err:
        notify_exception(
            request=None,
            message='Unexpected OpenMRS HTML UI',
            details=str(err),
        )
        return None
    if source_id:
        # Example request: http://www.example.com/openmrs/module/idgen/generateIdentifier.form?source=1
        response = requests.get('/module/idgen/generateIdentifier.form',
                                {'source': source_id})
        try:
            if not (200 <= response.status_code < 300 and response.content):
                raise OpenmrsException()
            try:
                # Example response: {"identifiers": ["CHR203007"]}
                return response.json()['identifiers'][0]
            except (ValueError, IndexError, KeyError):
                raise OpenmrsException()
        except OpenmrsException:
            notify_exception(
                request=None,
                message='OpenMRS idgen module returned an unexpected response',
                details=
                'OpenMRS idgen module at "{}" returned an unexpected response {}: "{}"'
                .format(response.url, response.status_code, response.content))
    return None
Esempio n. 7
0
def get_feed_xml(requests, feed_name, page: str):
    assert feed_name in ATOM_FEED_NAMES
    feed_url = f'/ws/atomfeed/{feed_name}/{page}'
    resp = requests.get(feed_url)

    if resp.status_code == 500:
        if 'AtomFeedRuntimeException: feed does not exist' in resp.text:
            exception = OpenmrsFeedRuntimeException(
                f'Domain "{requests.domain_name}": Page does not exist in Atom '
                f'feed "{resp.url}". Resetting Atom feed status.')
            requests.notify_exception(
                str(exception),
                _("This can happen if the IP address of a Repeater is changed "
                  "to point to a different server, or if a server has been "
                  "rebuilt. It can signal more severe consequences, like "
                  "attempts to synchronize CommCare cases with OpenMRS "
                  "patients that can no longer be found."))
            raise exception
        if ('AtomFeedRuntimeException: feedId must not be null and must be '
                'greater than 0') in resp.text:
            exception = OpenmrsFeedRuntimeException(
                f'Domain "{requests.domain_name}": Page "{page}" is not valid '
                f'in Atom feed "{resp.url}". Resetting Atom feed status.')
            requests.notify_exception(
                str(exception),
                _('It is unclear how Atom feed pagination can lead to page '
                  '"{}". Follow up with OpenMRS system '
                  'administrator.').format(page))
            raise exception

        # Use OpenmrsException instead of OpenmrsFeedRuntimeException so
        # that we don't reset the Atom feed if we don't know what the
        # problem is.
        exception = OpenmrsException(
            f'Domain "{requests.domain_name}": Unrecognized error in Atom '
            f'feed "{resp.url}".')
        requests.notify_exception(str(exception),
                                  _('Response text: \n{}').format(resp.text))
        raise exception

    try:
        root = etree.fromstring(resp.content)
    except etree.XMLSyntaxError as err:
        requests.notify_exception(
            str(err),
            _('There is an XML syntax error in the OpenMRS Atom feed at '
              f'"{resp.url}".'))
        raise OpenmrsFeedSyntaxError() from err
    return root