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, )
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' )
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, )
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')
def first_user(self): return get_one_commcare_user_at_location(self.domain, self.location_id)
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
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, )
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, )
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