Esempio n. 1
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

    """
    _assert(
        len(repeater.white_listed_case_types) == 1,
        'Unable to update patients from OpenMRS unless a single case type is '
        'specified. domain: "{}". repeater: "{}".'.format(
            repeater.domain, repeater.get_id
        )
    )
    case_type = repeater.white_listed_case_types[0]
    patient = get_patient_by_uuid(repeater.requests, patient_uuid)
    case, error = importer_util.lookup_case(
        EXTERNAL_ID,
        patient_uuid,
        repeater.domain,
        case_type=case_type,
    )
    if error == LookupErrors.NotFound:
        owner = get_one_commcare_user_at_location(repeater.domain, repeater.location_id)
        _assert(
            owner,
            'No users found at location "{}" to own patients added from '
            'OpenMRS atom feed. domain: "{}". repeater: "{}".'.format(
                repeater.location_id, repeater.domain, repeater.get_id
            )
        )
        case_block = get_addpatient_caseblock(case_type, owner, patient, repeater)

    else:
        _assert(
            error != LookupErrors.MultipleResults,
            # Multiple cases 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.
            'More than one case found matching unique OpenMRS UUID. '
            'domain: "{}". case external_id: "{}". repeater: "{}".'.format(
                repeater.domain, patient_uuid, repeater.get_id
            )
        )
        case_block = get_updatepatient_caseblock(case, patient, repeater)

    if case_block:
        submit_case_blocks(
            [case_block.as_string()],
            repeater.domain,
            xmlns=XMLNS_OPENMRS,
            device_id=OPENMRS_ATOM_FEED_DEVICE_ID + repeater.get_id,
        )
Esempio n. 2
0
def import_patients_with_importer(importer_json):
    importer = OpenmrsImporter.wrap(importer_json)
    password = b64_aes_decrypt(importer.password)
    requests = get_basic_requests(
        importer.domain, importer.server_url, importer.username, password,
        notify_addresses=importer.notify_addresses,
    )
    if importer.location_type_name:
        try:
            location_type = LocationType.objects.get(domain=importer.domain, name=importer.location_type_name)
        except LocationType.DoesNotExist:
            requests.notify_error(
                f'No organization level named "{importer.location_type_name}" '
                f'found in project space "{importer.domain}".'
            )
            return
        if importer.location_id:
            location = SQLLocation.objects.filter(domain=importer.domain).get(importer.location_id)
            locations = location.get_descendants.filter(location_type=location_type)
        else:
            locations = SQLLocation.objects.filter(domain=importer.domain, location_type=location_type)
        for location in locations:
            # Assign cases to the first user in the location, not to the location itself
            owner = get_one_commcare_user_at_location(importer.domain, location.location_id)
            if not owner:
                requests.notify_error(
                    f'Project space "{importer.domain}" at location '
                    f'"{location.name}" has no user to own cases imported '
                    f'from OpenMRS Importer "{importer}"'
                )
                continue
            # The same report is fetched for each location. WE DO THIS
            # ASSUMING THAT THE LOCATION IS USED IN THE REPORT
            # PARAMETERS. If not, OpenMRS will return THE SAME PATIENTS
            # multiple times and they will be assigned to a different
            # user each time.
            try:
                import_patients_of_owner(requests, importer, importer.domain, owner.user_id, location)
            except ConfigurationError as err:
                requests.notify_error(str(err))
    elif importer.owner_id:
        if not is_valid_owner(importer.owner_id):
            requests.notify_error(
                f'Error importing patients for project space "{importer.domain}" '
                f'from OpenMRS Importer "{importer}": owner_id "{importer.owner_id}" '
                'is invalid.'
            )
            return
        try:
            import_patients_of_owner(requests, importer, importer.domain, importer.owner_id)
        except ConfigurationError as err:
            requests.notify_error(str(err))
    else:
        requests.notify_error(
            f'Error importing patients for project space "{importer.domain}" from '
            f'OpenMRS Importer "{importer}": Unable to determine the owner of '
            'imported cases without either owner_id or location_type_name'
        )
Esempio n. 3
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

    """
    _assert(
        len(repeater.white_listed_case_types) == 1,
        'Unable to update patients from OpenMRS unless a single case type is '
        'specified. domain: "{}". repeater: "{}".'.format(
            repeater.domain, repeater.get_id))
    case_type = repeater.white_listed_case_types[0]
    patient = get_patient_by_uuid(repeater.requests, patient_uuid)
    case, error = importer_util.lookup_case(
        EXTERNAL_ID,
        patient_uuid,
        repeater.domain,
        case_type=case_type,
    )
    if error == LookupErrors.NotFound:
        owner = get_one_commcare_user_at_location(repeater.domain,
                                                  repeater.location_id)
        _assert(
            owner,
            'No users found at location "{}" to own patients added from '
            'OpenMRS atom feed. domain: "{}". repeater: "{}".'.format(
                repeater.location_id, repeater.domain, repeater.get_id))
        case_block = get_addpatient_caseblock(case_type, owner, patient,
                                              repeater)

    else:
        _assert(
            error != LookupErrors.MultipleResults,
            # Multiple cases 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.
            'More than one case found matching unique OpenMRS UUID. '
            'domain: "{}". case external_id: "{}". repeater: "{}".'.format(
                repeater.domain, patient_uuid, repeater.get_id))
        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. 4
0
def import_patients_with_importer(importer_json):
    importer = OpenmrsImporter.wrap(importer_json)
    password = b64_aes_decrypt(importer.password)
    requests = Requests(importer.domain, importer.server_url,
                        importer.username, password)
    if importer.location_type_name:
        try:
            location_type = LocationType.objects.get(
                domain=importer.domain, name=importer.location_type_name)
        except LocationType.DoesNotExist:
            logger.error(
                f'No organization level named "{importer.location_type_name}" '
                f'found in project space "{importer.domain}".')
            return
        if importer.location_id:
            location = SQLLocation.objects.filter(domain=importer.domain).get(
                importer.location_id)
            locations = location.get_descendants.filter(
                location_type=location_type)
        else:
            locations = SQLLocation.objects.filter(domain=importer.domain,
                                                   location_type=location_type)
        for location in locations:
            # Assign cases to the first user in the location, not to the location itself
            owner = get_one_commcare_user_at_location(importer.domain,
                                                      location.location_id)
            if not owner:
                logger.error(
                    f'Project space "{importer.domain}" at location '
                    f'"{location.name}" has no user to own cases imported '
                    f'from OpenMRS Importer "{importer}"')
                continue
            import_patients_of_owner(requests, importer, importer.domain,
                                     owner, location)
    elif importer.owner_id:
        try:
            owner = CommCareUser.get(importer.owner_id)
        except ResourceNotFound:
            logger.error(
                f'Project space "{importer.domain}" has no user to own cases '
                f'imported from OpenMRS Importer "{importer}"')
            return
        import_patients_of_owner(requests, importer, importer.domain, owner)
    else:
        logger.error(
            f'Error importing patients for project space "{importer.domain}" from '
            f'OpenMRS Importer "{importer}": Unable to determine the owner of '
            'imported cases without either owner_id or location_type_name')
Esempio n. 5
0
 def first_user(self):
     return get_one_commcare_user_at_location(self.domain, self.location_id)
Esempio n. 6
0
def import_patients_to_domain(domain_name, force=False):
    """
    Iterates OpenmrsImporters of a domain, and imports patients

    Who owns the imported cases?

    If `importer.owner_id` is set, then the server will be queried
    once. All patients, regardless of their location, will be assigned
    to the mobile worker whose ID is `importer.owner_id`.

    If `importer.location_type_name` is set, then check whether the
    OpenmrsImporter's location is set with `importer.location_id`.

    If `importer.location_id` is given, then the server will be queried
    for each location among its descendants whose type is
    `importer.location_type_name`. The request's query parameters will
    include that descendant location's OpenMRS UUID. Imported cases
    will be owned by the first mobile worker in that location.

    If `importer.location_id` is given, then the server will be queried
    for each location in the project space whose type is
    `importer.location_type_name`. As when `importer.location_id` is
    specified, the request's query parameters will include that
    location's OpenMRS UUID, and imported cases will be owned by the
    first mobile worker in that location.

    ..NOTE:: As you can see from the description above, if
             `importer.owner_id` is set then `importer.location_id` is
             not used.

    :param domain_name: The name of the domain
    :param force: Import regardless of the configured import frequency / today's date
    """
    today = datetime.today()
    for importer in get_openmrs_importers_by_domain(domain_name):
        if not force and importer.import_frequency == IMPORT_FREQUENCY_WEEKLY and today.weekday(
        ) != 1:
            continue  # Import on Tuesdays
        if not force and importer.import_frequency == IMPORT_FREQUENCY_MONTHLY and today.day != 1:
            continue  # Import on the first of the month
        # TODO: ^^^ Make those configurable

        password = b64_aes_decrypt(importer.password)
        requests = Requests(domain_name, importer.server_url,
                            importer.username, password)
        if importer.location_type_name:
            try:
                location_type = LocationType.objects.get(
                    domain=domain_name, name=importer.location_type_name)
            except LocationType.DoesNotExist:
                logger.error(
                    'No organization level named "{location_type}" found in project space "{domain}".'
                    .format(location_type=importer.location_type_name,
                            domain=domain_name))
                continue
            if importer.location_id:
                location = SQLLocation.objects.filter(domain=domain_name).get(
                    importer.location_id)
                locations = location.get_descendants.filter(
                    location_type=location_type)
            else:
                locations = SQLLocation.objects.filter(
                    domain=domain_name, location_type=location_type)
            for location in locations:
                # Assign cases to the first user in the location, not to the location itself
                owner = get_one_commcare_user_at_location(
                    domain_name, location.location_id)
                if not owner:
                    logger.error(
                        'Project space "{domain}" at location "{location}" has no user to own cases imported from '
                        'OpenMRS Importer "{importer}"'.format(
                            domain=domain_name,
                            location=location.name,
                            importer=importer))
                    continue
                import_patients_of_owner(requests, importer, domain_name,
                                         owner, location)
        elif importer.owner_id:
            try:
                owner = CommCareUser.get(importer.owner_id)
            except ResourceNotFound:
                logger.error(
                    'Project space "{domain}" has no user to own cases imported from OpenMRS Importer '
                    '"{importer}"'.format(domain=domain_name,
                                          importer=importer))
                continue
            import_patients_of_owner(requests, importer, domain_name, owner)
        else:
            logger.error(
                'Error importing patients for project space "{domain}" from OpenMRS Importer "{importer}": Unable '
                'to determine the owner of imported cases without either owner_id or location_type_name'
                .format(domain=domain_name, importer=importer))
            continue
Esempio n. 7
0
def import_encounter(repeater, encounter_uuid):
    # It's possible that an OpenMRS concept appears more than once in
    # form_configs. Use a defaultdict(list) so that earlier definitions
    # don't get overwritten by later ones:

    def fields_from_observations(observations, mappings):
        """
        Traverse a tree of observations, and return the ones mapped to
        case properties.
        """
        fields = {}
        for obs in observations:
            if obs['concept']['uuid'] in mappings:
                for mapping in mappings[obs['concept']['uuid']]:
                    fields[mapping.case_property] = mapping.value.deserialize(
                        obs['value'])
            if obs['groupMembers']:
                fields.update(
                    fields_from_observations(obs['groupMembers'], mappings))
        return fields

    response = repeater.requests.get(
        '/ws/rest/v1/bahmnicore/bahmniencounter/' + encounter_uuid,
        {'includeAll': 'true'},
        raise_for_status=True)
    encounter = response.json()

    case_property_updates = fields_from_observations(
        encounter['observations'], repeater.observation_mappings)

    if case_property_updates:
        case_blocks = []
        patient_uuid = encounter['patientUuid']
        case_type = repeater.white_listed_case_types[0]
        case, error = importer_util.lookup_case(
            EXTERNAL_ID,
            patient_uuid,
            repeater.domain,
            case_type=case_type,
        )
        if case:
            case_id = case.get_id

        elif error == LookupErrors.NotFound:
            # The encounter is for a patient that has not yet been imported
            patient = get_patient_by_uuid(repeater.requests, patient_uuid)
            owner = get_one_commcare_user_at_location(repeater.domain,
                                                      repeater.location_id)
            case_block = get_addpatient_caseblock(case_type, owner, patient,
                                                  repeater)
            case_blocks.append(case_block)
            case_id = case_block.case_id

        else:
            _assert(
                error != LookupErrors.MultipleResults,
                'More than one case found matching unique OpenMRS UUID. '
                'domain: "{}". case external_id: "{}". repeater: "{}".'.format(
                    repeater.domain, patient_uuid, repeater.get_id))
            return

        case_blocks.append(
            CaseBlock(
                case_id=case_id,
                create=False,
                update=case_property_updates,
            ))
        submit_case_blocks(
            [cb.as_text() for cb in case_blocks],
            repeater.domain,
            xmlns=XMLNS_OPENMRS,
            device_id=OPENMRS_ATOM_FEED_DEVICE_ID + repeater.get_id,
        )
Esempio n. 8
0
def import_encounter(repeater, encounter_uuid):
    # It's possible that an OpenMRS concept appears more than once in
    # form_configs. Use a defaultdict(list) so that earlier definitions
    # don't get overwritten by later ones:

    def fields_from_observations(observations, mappings):
        """
        Traverse a tree of observations, and return the ones mapped to
        case properties.
        """
        fields = {}
        for obs in observations:
            if obs['concept']['uuid'] in mappings:
                for mapping in mappings[obs['concept']['uuid']]:
                    fields[mapping.case_property] = mapping.value.deserialize(obs['value'])
            if obs['groupMembers']:
                fields.update(fields_from_observations(obs['groupMembers'], mappings))
        return fields

    response = repeater.requests.get(
        '/ws/rest/v1/bahmnicore/bahmniencounter/' + encounter_uuid,
        {'includeAll': 'true'},
        raise_for_status=True
    )
    encounter = response.json()

    case_property_updates = fields_from_observations(encounter['observations'], repeater.observation_mappings)

    if case_property_updates:
        case_blocks = []
        patient_uuid = encounter['patientUuid']
        case_type = repeater.white_listed_case_types[0]
        case, error = importer_util.lookup_case(
            EXTERNAL_ID,
            patient_uuid,
            repeater.domain,
            case_type=case_type,
        )
        if case:
            case_id = case.get_id

        elif error == LookupErrors.NotFound:
            # The encounter is for a patient that has not yet been imported
            patient = get_patient_by_uuid(repeater.requests, patient_uuid)
            owner = get_one_commcare_user_at_location(repeater.domain, repeater.location_id)
            case_block = get_addpatient_caseblock(case_type, owner, patient, repeater)
            case_blocks.append(case_block)
            case_id = case_block.case_id

        else:
            _assert(
                error != LookupErrors.MultipleResults,
                'More than one case found matching unique OpenMRS UUID. '
                'domain: "{}". case external_id: "{}". repeater: "{}".'.format(
                    repeater.domain, patient_uuid, repeater.get_id
                )
            )
            return

        case_blocks.append(CaseBlock(
            case_id=case_id,
            create=False,
            update=case_property_updates,
        ))
        submit_case_blocks(
            [cb.as_string() for cb in case_blocks],
            repeater.domain,
            xmlns=XMLNS_OPENMRS,
            device_id=OPENMRS_ATOM_FEED_DEVICE_ID + repeater.get_id,
        )
Esempio n. 9
0
def import_patients_to_domain(domain_name, force=False):
    """
    Iterates OpenmrsImporters of a domain, and imports patients

    Who owns the imported cases?

    If `importer.owner_id` is set, then the server will be queried
    once. All patients, regardless of their location, will be assigned
    to the mobile worker whose ID is `importer.owner_id`.

    If `importer.location_type_name` is set, then check whether the
    OpenmrsImporter's location is set with `importer.location_id`.

    If `importer.location_id` is given, then the server will be queried
    for each location among its descendants whose type is
    `importer.location_type_name`. The request's query parameters will
    include that descendant location's OpenMRS UUID. Imported cases
    will be owned by the first mobile worker in that location.

    If `importer.location_id` is given, then the server will be queried
    for each location in the project space whose type is
    `importer.location_type_name`. As when `importer.location_id` is
    specified, the request's query parameters will include that
    location's OpenMRS UUID, and imported cases will be owned by the
    first mobile worker in that location.

    ..NOTE:: As you can see from the description above, if
             `importer.owner_id` is set then `importer.location_id` is
             not used.

    :param domain_name: The name of the domain
    :param force: Import regardless of the configured import frequency / today's date
    """
    today = datetime.today()
    for importer in get_openmrs_importers_by_domain(domain_name):
        if not force and importer.import_frequency == IMPORT_FREQUENCY_WEEKLY and today.weekday() != 1:
            continue  # Import on Tuesdays
        if not force and importer.import_frequency == IMPORT_FREQUENCY_MONTHLY and today.day != 1:
            continue  # Import on the first of the month
        # TODO: ^^^ Make those configurable

        password = b64_aes_decrypt(importer.password)
        requests = Requests(domain_name, importer.server_url, importer.username, password)
        if importer.location_type_name:
            try:
                location_type = LocationType.objects.get(domain=domain_name, name=importer.location_type_name)
            except LocationType.DoesNotExist:
                logger.error(
                    'No organization level named "{location_type}" found in project space "{domain}".'.format(
                        location_type=importer.location_type_name, domain=domain_name)
                )
                continue
            if importer.location_id:
                location = SQLLocation.objects.filter(domain=domain_name).get(importer.location_id)
                locations = location.get_descendants.filter(location_type=location_type)
            else:
                locations = SQLLocation.objects.filter(domain=domain_name, location_type=location_type)
            for location in locations:
                # Assign cases to the first user in the location, not to the location itself
                owner = get_one_commcare_user_at_location(domain_name, location.location_id)
                if not owner:
                    logger.error(
                        'Project space "{domain}" at location "{location}" has no user to own cases imported from '
                        'OpenMRS Importer "{importer}"'.format(
                            domain=domain_name, location=location.name, importer=importer)
                    )
                    continue
                import_patients_of_owner(requests, importer, domain_name, owner, location)
        elif importer.owner_id:
            try:
                owner = CommCareUser.get(importer.owner_id)
            except ResourceNotFound:
                logger.error(
                    'Project space "{domain}" has no user to own cases imported from OpenMRS Importer '
                    '"{importer}"'.format(
                        domain=domain_name, importer=importer)
                )
                continue
            import_patients_of_owner(requests, importer, domain_name, owner)
        else:
            logger.error(
                'Error importing patients for project space "{domain}" from OpenMRS Importer "{importer}": Unable '
                'to determine the owner of imported cases without either owner_id or location_type_name'.format(
                    domain=domain_name, importer=importer)
            )
            continue