def test_valid_json(self): self.assertEqual( pformat_json('{"ham": "spam", "eggs": "spam"}'), '{\n "eggs": "spam",\n "ham": "spam"\n}' if six.PY3 else '{\n "eggs": "spam", \n "ham": "spam"\n}' ) self.assertEqual( pformat_json({'ham': 'spam', 'eggs': 'spam'}), '{\n "eggs": "spam",\n "ham": "spam"\n}' if six.PY3 else '{\n "eggs": "spam", \n "ham": "spam"\n}' )
def test_valid_json(self): self.assertEqual( pformat_json('{"ham": "spam", "eggs": "spam"}'), '{\n "eggs": "spam",\n "ham": "spam"\n}' if six.PY3 else '{\n "eggs": "spam", \n "ham": "spam"\n}') self.assertEqual( pformat_json({ 'ham': 'spam', 'eggs': 'spam' }), '{\n "eggs": "spam",\n "ham": "spam"\n}' if six.PY3 else '{\n "eggs": "spam", \n "ham": "spam"\n}')
def request_wrapper(method, url, *args, **kwargs): log_level = logging.INFO request_error = '' response_status = None response_body = '' try: response = func(method, url, *args, **kwargs) response_status = response.status_code response_body = response.content except Exception as err: log_level = logging.ERROR request_error = str(err) if getattr(err, 'response', None) is not None: response_status = err.response.status_code response_body = pformat_json(err.response.text) raise else: return response finally: params, data, headers = unpack_request_args(method, args, kwargs) entry = RequestLogEntry( self.domain_name, self.payload_id, method, url, headers, params, data, request_error, response_status, response_body ) logger(log_level, entry)
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 request_wrapper(self, *args, **kwargs): log_level = logging.INFO request_error = '' response_status = None response_body = '' try: response = func(self, *args, **kwargs) response_status = response.status_code response_body = response.content except Exception as err: log_level = logging.ERROR request_error = str(err) if getattr(err, 'response', None) is not None: response_status = err.response.status_code response_body = pformat_json(err.response.text) raise else: return response finally: # args will be Requests method, url, and optionally params, data or json. # kwargs may include Requests method kwargs and raise_for_status. kwargs.pop('raise_for_status', None) RequestLog.log(log_level, self.domain_name, self.payload_id, request_error, response_status, response_body, *args, **kwargs)
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 = self.get_requests(payload_id=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 OpenmrsResponse(400, 'Bad Request', pformat_json(str(err))) return response
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 test_nonascii_json_bytes(self): pigs_and_eggs = (u'{"\U0001f416": "\U0001f416\U0001f416\U0001f416", ' u'"\U0001f95a\U0001f95a": "\U0001f416\U0001f416\U0001f416"}') json_string = pformat_json(pigs_and_eggs.encode("utf-8")) self.assertEqual( json_string, '{\n "\\ud83d\\udc16": "\\ud83d\\udc16\\ud83d\\udc16\\ud83d\\udc16",\n ' '"\\ud83e\\udd5a\\ud83e\\udd5a": "\\ud83d\\udc16\\ud83d\\udc16\\ud83d\\udc16"\n}' )
def test_nonascii_dict(self): pigs_and_eggs = {"\U0001f416": "\U0001f416\U0001f416\U0001f416", "\U0001f95a\U0001f95a": "\U0001f416\U0001f416\U0001f416"} json_string = pformat_json(pigs_and_eggs) self.assertEqual( json_string, '{\n "\\ud83d\\udc16": "\\ud83d\\udc16\\ud83d\\udc16\\ud83d\\udc16",\n ' '"\\ud83e\\udd5a\\ud83e\\udd5a": "\\ud83d\\udc16\\ud83d\\udc16\\ud83d\\udc16"\n}' ) assert json.loads(json_string) == pigs_and_eggs
def parse_request_exception(err): """ Parses an instance of RequestException and returns a request string and response string tuple """ err_request = '{method} {url}\n\n{body}'.format( method=err.request.method, url=err.request.url, body=err.request.body) if err.request.body else ' '.join( (err.request.method, err.request.url)) err_content = pformat_json( err.response.content) # pformat_json returns non-JSON values unchanged err_response = '\n\n'.join((str(err), err_content)) return err_request, err_response
def parse_request_exception(err): """ Parses an instance of RequestException and returns a request string and response string tuple """ err_request = '{method} {url}\n\n{body}'.format( method=err.request.method, url=err.request.url, body=err.request.body ) if err.request.body else ' '.join((err.request.method, err.request.url)) if err.response: err_content = pformat_json(err.response.content) # pformat_json returns non-JSON values unchanged err_response = '\n\n'.join((str(err), err_content)) else: err_response = str(err) return err_request, err_response
def get(self, request, domain): record_id = request.GET.get('record_id') record = self.get_record_or_404(domain, record_id) content_type = record.repeater.generator.content_type try: payload = record.get_payload() except XFormNotFound: return JsonResponse({ 'error': 'Odd, could not find payload for: {}'.format(record.payload_id) }, status=404) if content_type == 'text/xml': payload = indent_xml(payload) elif content_type == 'application/json': payload = pformat_json(payload) return JsonResponse({ 'payload': payload, 'content_type': content_type, })
def get(self, request, domain): record_id = request.GET.get('record_id') record = self.get_record_or_404(request, domain, record_id) content_type = record.repeater.generator.content_type try: payload = record.get_payload() except XFormNotFound: return json_response({ 'error': 'Odd, could not find payload for: {}'.format(record.payload_id) }, status_code=404) if content_type == 'text/xml': payload = indent_xml(payload) elif content_type == 'application/json': payload = pformat_json(payload) return json_response({ 'payload': payload, 'content_type': content_type, })
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 request_wrapper(self, *args, **kwargs): log_level = logging.INFO request_error = '' response_status = None response_body = '' try: response = func(self, *args, **kwargs) response_status = response.status_code response_body = response.content except Exception as err: log_level = logging.ERROR request_error = str(err) if getattr(err, 'response', None) is not None: response_status = err.response.status_code response_body = pformat_json(err.response.content) raise else: return response finally: # args will be Requests method, url, and optionally params, data or json. # kwargs may include Requests method kwargs and raise_for_status. kwargs.pop('raise_for_status', None) RequestLog.log(log_level, self.domain_name, request_error, response_status, response_body, *args, **kwargs)
def get(self, request, domain): record_id = request.GET.get('record_id') record = self.get_record_or_404(request, domain, record_id) content_type = record.repeater.generator.content_type try: payload = record.get_payload() except XFormNotFound: return json_response({ 'error': 'Odd, could not find payload for: {}'.format(record.payload_id) }, status_code=404) if content_type == 'text/xml': payload = indent_xml(payload) elif content_type == 'application/json': payload = pformat_json(payload) elif content_type == 'application/soap+xml': # we return a payload that is a dict, which is then converted to # XML by the zeep library before being sent along as a SOAP request. payload = json.dumps(payload, indent=4) return json_response({ 'payload': payload, 'content_type': content_type, })
def send_dataset( dataset_map: SQLDataSetMap, send_date: datetime.date ) -> dict: """ Sends a data set of data values in the following format. "period" is determined from ``send_date``. :: { "dataSet": "dataSetID", "completeDate": "date", "period": "period", "orgUnit": "orgUnitID", "attributeOptionCombo", "aocID", "dataValues": [ { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "1", "comment": "comment1" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "2", "comment": "comment2" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "3", "comment": "comment3" } ] } See `DHIS2 API docs`_ for more details. .. _DHIS2 API docs: https://docs.dhis2.org/master/en/developer/html/webapi_data_values.html """ # payload_id lets us filter API logs, and uniquely identifies the # dataset map, to help AEs and administrators link an API log back # to a dataset map. payload_id = f'dhis2/map/{dataset_map.pk}/' response_log_url = reverse( 'motech_log_list_view', args=[dataset_map.domain], params={'filter_payload': payload_id} ) with dataset_map.connection_settings.get_requests(payload_id) as requests: response = None try: datavalues_sets = parse_dataset_for_request(dataset_map, send_date) for datavalues_set in datavalues_sets: response = requests.post('/api/dataValueSets', json=datavalues_set, raise_for_status=True) except DatabaseError as db_err: requests.notify_error(message=str(db_err), details=traceback.format_exc()) return { 'success': False, 'error': _('There was an error retrieving some UCR data. ' 'Try contacting support to help resolve this issue.'), 'text': None, 'log_url': response_log_url, } except Exception as err: requests.notify_error(message=str(err), details=traceback.format_exc()) text = pformat_json(response.text if response else None) return { 'success': False, 'error': str(err), 'status_code': response.status_code if response else None, 'text': text, 'log_url': response_log_url, } else: return { 'success': True, 'status_code': response.status_code, 'text': pformat_json(response.text), 'log_url': response_log_url, }
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 OpenmrsResponse( 400, 'Bad Request', "Errors: " + pformat_json([str(e) for e in errors])) if warnings: return OpenmrsResponse( 201, "Accepted", "Warnings: " + pformat_json([str(e) for e in warnings])) return OpenmrsResponse(200, "OK")
def pp_request_headers(self): """ Pretty-print the request headers """ return pformat_json(self.request_headers)
def pp_json(data): """ Pretty-print data as JSON """ return pformat_json(data)
def pp_request_params(self): """ Pretty-print the request params """ return pformat_json(self.request_params)
def test_invalid_json(self): self.assertEqual(pformat_json('ham spam eggs spam'), 'ham spam eggs spam')
def pp_response_body(self): """ Pretty-print the response body """ return pformat_json(self.response_body)
def test_invalid_json(self): self.assertEqual( pformat_json('ham spam eggs spam'), 'ham spam eggs spam' )
def test_valid_json_string(self): self.assertEqual( pformat_json('{"ham": "spam", "eggs": "spam"}'), '{\n "eggs": "spam",\n "ham": "spam"\n}' )
def send_openmrs_data(requests, domain, form_json, openmrs_config, case_trigger_infos, form_question_values): """ 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 .. _OpenMRS REST Web Services: https://wiki.openmrs.org/display/docs/REST+Web+Services+API+For+Clients """ errors = [] for info in case_trigger_infos: assert isinstance(info, CaseTriggerInfo) patient = get_patient(requests, domain, info, openmrs_config) if patient is None: errors.append('Warning: CommCare case "{}" was not found in OpenMRS'.format(info.case_id)) 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, form_question_values, openmrs_config, patient['person']['uuid'] ), ) errors.extend( execute_workflow(workflow) ) if errors: logger.error('Errors encountered sending OpenMRS data: %s', 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 OpenmrsResponse(400, 'Bad Request', pformat_json([str(e) for e in errors])) else: return OpenmrsResponse(200, 'OK', '')
def test_none(self): self.assertEqual( pformat_json(None), '' )
def test_empty_string(self): self.assertEqual( pformat_json(''), '' )
def send_openmrs_data(requests, domain, form_json, openmrs_config, case_trigger_infos, form_question_values): """ 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 .. _OpenMRS REST Web Services: https://wiki.openmrs.org/display/docs/REST+Web+Services+API+For+Clients """ errors = [] for info in case_trigger_infos: assert isinstance(info, CaseTriggerInfo) patient = get_patient(requests, domain, info, openmrs_config) if patient is None: errors.append('Warning: CommCare case "{}" was not found in OpenMRS'.format(info.case_id)) 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, form_question_values, openmrs_config, patient['person']['uuid'] ), ) errors.extend( execute_workflow(workflow) ) if errors: logger.error('Errors encountered sending OpenMRS data: %s', 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 OpenmrsResponse(400, 'Bad Request', pformat_json([str(e) for e in errors])) else: return OpenmrsResponse(200, 'OK', '')
def pp_json(data): """ Pretty-print data as JSON """ return pformat_json(data)
def pp_request_body(self): """ Pretty-print the request body """ return pformat_json(self.request_body)
def test_invalid_json_bytes(self): self.assertEqual( pformat_json(b'{"ham": "spam", "spam", "spa'), b'{"ham": "spam", "spam", "spa' )
def test_dict(self): self.assertEqual( pformat_json({'ham': 'spam', 'eggs': 'spam'}), '{\n "eggs": "spam",\n "ham": "spam"\n}' )