create a python dictionary based on another dictionary - python

I have the the following dict that contains the following data:
response = {"status":"ERROR","email":"EMAIL_INVALID","name":"NAME_INVALID"}
I am trying to create a new dict based on 'response' that is suposed to look like the following:
{'api_error': {'list': [{'converted_value': 'No special characters allowed.',
'field': 'name',
'value': 'NAME_INVALID'},
{'converted_value': 'invalid email',
'field': 'email',
'value': 'EMAIL_INVALID'}],
'status': 'ERROR'},
'email': 'EMAIL_INVALID',
'email_label': 'invalid email',
'name': 'NAME_INVALID',
'name_label': 'No special characters allowed.',
'status': 'ERROR'}
So far I have been able to do the following:
ret = {}
for k in response:
if k != 'status':
ret[k+"_label"] = convert(response[k])
ret[k] = response[k]
else:
ret[k] = convert(response[k])
where 'convert' function translates each value of response. for example NAME_INVALID is converted to 'No special characters allowed.' and so on. Here is the output of what above code is doing:
{"status":"ERROR","name_label":"No special characters allowed.",
"email_label":"invalid email","name":"NAME_INVALID","email":"EMAIL_INVALID"}
I am getting problem creating the rest of the dictionary. The one whose key is 'api_error'. What will be the most efficient way of doing that?

import pprint
response = {"status": "ERROR", "email": "EMAIL_INVALID", "name":
"NAME_INVALID"}
def convert(label):
return {'NAME_INVALID': 'No special characters allowed',
'EMAIL_INVALID': 'invalid email',
'ERROR': 'ERROR'}[label]
ret = {}
for k in response:
if k != 'status':
ret[k + "_label"] = convert(response[k])
ret[k] = response[k]
info = {'converted_value': ret[k + "_label"],
'field': k,
'value': response[k]}
(ret.setdefault('api_error', {})
.setdefault('list', [])
.append(info))
else:
ret[k] = convert(response[k])
ret.setdefault('api_error', {})['status'] = ret[k]
pprint.pprint(ret)
yields
{'api_error': {'list': [{'converted_value': 'invalid email',
'field': 'email',
'value': 'EMAIL_INVALID'},
{'converted_value': 'No special characters allowed',
'field': 'name',
'value': 'NAME_INVALID'}],
'status': 'ERROR'},
'email': 'EMAIL_INVALID',
'email_label': 'invalid email',
'name': 'NAME_INVALID',
'name_label': 'No special characters allowed',
'status': 'ERROR'}

make one more function that will be like
def make2nddict(response):
for k in response:
if k != 'status':
d = {}
d['converted_value'] = convert(k)
d['field'] = k
d['value'] = response[k]
arr.append(d)
else:
final[k] = response[k]
final['list'] = arr
arr= []
final = {}
def convert(error):
if error == 'NAME_INVALID':
return 'No special characters allowed'
elif error == 'EMAIL_INVALID':
return 'EMAIL_INVALID'
else:
return error
ret = {}
for k in response:
if k != 'status':
ret[k+"_label"] = convert(response[k])
ret[k] = response[k]
else:
ret[k] = convert(response[k])
put output of noth the function in your api_error dictionary
good luck

Here the full code: ---
>>>response = {"status":"ERROR","email":"EMAIL_INVALID","name":"NAME_INVALID"}
>>>def convert(parameter):
if parameter == "NAME_INVALID":
return "No special characters allowed."
if parameter =="EMAIL_INVALID":
return "invalid email"
>>>def newdic(response):
ret={}
response_keys = response.keys()
response_keys.remove("status")
if response['status']=="ERROR":
ret = response
ret['api_error'] ={'list':[],'status':"ERROR"}
for key in response_keys:
ret[key+"_label"] = convert(response[key])
dic={}
dic['converted_value'] = convert(response[key])
dic['field'] = key
dic['value'] = response[key]
ret['api_error']['list'].append(dic)
return ret
Here is the answer:--
>>>newdic(response)
{'status': 'ERROR',
'email_label': 'invalid email',
'name': 'NAME_INVALID',
'name_label': 'No special characters allowed.',
'api_error': {
'email': 'EMAIL_INVALID',
'list': [{'field': 'email', 'value': 'EMAIL_INVALID',
'converted_value': 'invalid email'},
{'field': 'name', 'value': 'NAME_INVALID',
'converted_value': 'No special characters allowed.'}
],
'status': 'ERROR'}
}

Related

How to extract a couple of fields nested in response using python

I'm a python beginner. I would like to ask for help regarding the retrieve the response data. Here's my script:
import pandas as pd
import re
import time
import requests as re
import json
response = re.get(url, headers=headers, auth=auth)
data = response.json()
Here's a part of json response:
{'result': [{'display': '',
'closure_code': '',
'service_offer': 'Integration Platforms',
'updated_on': '2022-04-23 09:05:53',
'urgency': '2',
'business_service': 'Operations',
'updated_by': 'serviceaccount45',
'description': 'ALERT returned 400 but expected 200',
'sys_created_on': '2022-04-23 09:05:53',
'sys_created_by': 'serviceaccount45',
'subcategory': 'Integration',
'contact_type': 'Email',
'problem_type': 'Design: Availability',
'caller_id': '',
'action': 'create',
'company': 'aaaa',
'priority': '3',
'status': '1',
'opened': 'smith.j',
'assigned_to': 'doe.j',
'number': '123456',
'group': 'blabla',
'impact': '2',
'category': 'Business Application & Databases',
'caused_by_change': '',
'location': 'All Locations',
'configuration_item': 'Monitor',
},
I would like to extract the data only for one group = 'blablabla'. Then I would like to extract fields such as:
number = data['number']
group = data['group']
service_offer = data['service_offer']
updated = data['updated_on']
urgency = data['urgency']
username = data['created_by']
short_desc = data['description']
How it should be done?
I know that to check the first value I should use:
service_offer = data['result'][0]['service_offer']
I've tried to create a dictionary, but, I'm getting an error:
data_result = response.json()['result']
payload ={
number = data_result['number']
group = data_result['group']
service_offer = data_result['service_offer']
updated = data_result['updated_on']
urgency = data_result['urgency']
username = data_result['created_by']
short_desc = data_result['description']
}
TypeError: list indices must be integers or slices, not str:
So, I've started to create something like below., but I'm stuck:
get_data = []
if len(data) > 0:
for item in range(len(data)):
get_data.append(data[item])
May I ask for help?
If data is your decoded json response from the question then you can do:
# find group `blabla` in result:
g = next(d for d in data["result"] if d["group"] == "blabla")
# get data from the `blabla` group:
number = g["number"]
group = g["group"]
service_offer = g["service_offer"]
updated = g["updated_on"]
urgency = g["urgency"]
username = g["sys_created_by"]
short_desc = g["description"]
print(number, group, service_offer, updated, urgency, username, short_desc)
Prints:
123456 blabla Integration Platforms 2022-04-23 09:05:53 2 serviceaccount45 ALERT returned 400 but expected 200

With format='multipart' in test client, data of nested dict been ignored or removed

I have a nested serializer containing, containing an Image Field in the nested serializer, the serializers are:-
class FloorPlanLocationSerializer(serializers.ModelSerializer):
class Meta:
model = FloorPlan
fields = (
'floor',
'image',
)
extra_kwargs = {'floor': {'required': False}, 'image': {'required': False}}
class LocationSerializer(FilterSerializerByOrgManaged, serializers.ModelSerializer):
floorplan = FloorPlanLocationSerializer(required=False, allow_null=True)
class Meta:
model = Location
fields = (
'id',
'organization',
'name',
'type',
'is_mobile',
'address',
'geometry',
'created',
'modified',
'floorplan',
)
read_only_fields = ('created', 'modified')
def to_representation(self, instance):
request = self.context['request']
data = super().to_representation(instance)
floorplans = instance.floorplan_set.all().order_by('-modified')
floorplan_list = []
for floorplan in floorplans:
dict_ = {
'floor': floorplan.floor,
'image': request.build_absolute_uri(floorplan.image.url),
}
floorplan_list.append(dict_)
data['floorplan'] = floorplan_list
return data
def create(self, validated_data):
floorplan_data = None
if validated_data.get('floorplan'):
floorplan_data = validated_data.pop('floorplan')
instance = self.instance or self.Meta.model(**validated_data)
with transaction.atomic():
instance.full_clean()
instance.save()
if floorplan_data:
floorplan_data['location'] = instance
floorplan_data['organization'] = instance.organization
with transaction.atomic():
fl = FloorPlan.objects.create(**floorplan_data)
fl.full_clean()
fl.save()
return instance
With this above serialzier, it works fine with DRF Browsable page, but when I try to send the data with the test client in multipart format, the nested data gets removed while send the POST request, this is how I wrote the tests:-
def test_create_location_with_floorplan_api(self):
path = reverse('geo_api:list_location')
coords = json.loads(Point(2, 23).geojson)
image = Image.new("RGB", (100, 100))
with tempfile.NamedTemporaryFile(suffix=".png", mode="w+b") as tmp_file:
image.save(tmp_file, format="png")
tmp_file.seek(0)
byio = BytesIO(tmp_file.read())
inm_file = InMemoryUploadedFile(
file=byio,
field_name="avatar",
name="testImage.png",
content_type="image/png",
size=byio.getbuffer().nbytes,
charset=None,
)
data = {
'organization': self._get_org().pk,
'name': 'test-location',
'type': 'indoor',
'is_mobile': False,
'address': 'Via del Corso, Roma, Italia',
'geometry': {'Type': 'Point', 'coordinates': [12.32,43.222]},
'floorplan': {
'floor': 12,
'image': inm_file
},
}
with self.assertNumQueries(6):
response = self.client.post(path, data, format='multipart')
self.assertEqual(response.status_code, 201)
The data doesn't come in the same format as I sent, i.e., when I try to see the data in the to_internal method this is how I receive it:-
<QueryDict: {'organization': ['f6c406e5-0602-44a7-9160-ec109ac29f4c'], 'name': ['test-location'], 'type': ['indoor'], 'is_mobile': ['False'], 'address': ['Via del Corso, Roma, Italia'], 'geometry': ['type', 'coordinates'], 'floorplan': ['floor', 'image']}>
the values of type, coordinates, floorplan are not present inside it.
How can I write a proper tests for the above case???
If you want to post form data, you need to flatten everything the same way a browser would. Maybe this gist will help, flatten_dict_for_form_data. Its quite old and could use some cleanup, but it still works.
This recursively flattens a dict, which you can then send to test client (or to live services):
def flatten_dict_for_formdata(input_dict, sep="[{i}]"):
def __flatten(value, prefix, result_dict, previous=None):
if isinstance(value, dict):
if previous == "dict":
prefix += "."
for key, v in value.items():
__flatten(v, prefix + key, result_dict, "dict")
elif isinstance(value, (list, tuple)):
for i, v in enumerate(value):
__flatten(v, prefix + sep.format(i=i), result_dict)
else:
result_dict[prefix] = value
return result_dict
return __flatten(input_dict, '', {})
>>> flatten_dict_for_formdata({
>>> "name": "Test",
>>> "location": {"lat": 1, "lng": 2},
>>> "sizes": ["S", "M", "XL"]
>>> })
>>> {
>>> "name": "Test",
>>> "location.lat": 1,
>>> "location.lng": 2,
>>> "sizes[0]": "S",
>>> "sizes[1]": "M",
>>> "sizes[2]": "XL"
>>> }

How to get specific value from json api with python

So i have code that gets data from one api but i can only use frist 3 keys like message idk why is that code:
import requests
import json
result=requests.get('https://api.etherscan.io/api?module=account&action=tokentx&contractaddress=0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2&page=1&offset=1&sort=asc&apikey=mytoken')
result.status_code
result.text
result.json()
print (result.json()['message]) # work
print (result.json()['gas]) # or any other key dont work
Output from api:
{"status":"1","message":"OK","result":[{"blockNumber":"4620855","timeStamp":"1511634257","hash":"0x5c9b0f9c6c32d2690771169ec62dd648fef7bce3d45fe8a6505d99fdcbade27a","nonce":"5417","blockHash":"0xee385ac028bb7d8863d70afa02d63181894e0b2d51b99c0c525ef24538c44c24","from":"0x731c6f8c754fa404cfcc2ed8035ef79262f65702","contractAddress":"0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2","to":"0x642ae78fafbb8032da552d619ad43f1d81e4dd7c","value":"1000000000000000000000000","tokenName":"Maker","tokenSymbol":"MKR","tokenDecimal":"18","transactionIndex":"55","gas":"3000000","gasPrice":"1000000000","gasUsed":"1594668","cumulativeGasUsed":"4047394","input":"deprecated","confirmations":"7045304"}]}
i can only get status message ect.
when i try gas this is error
Traceback (most recent call last):
File "main.py", line 11, in
print (result.json()[gas])
NameError: name 'gas' is not defined
You should add a few print statements to understand your response and debug
your_reponse = {
'message': 'OK',
'result': [{'blockHash': '0xee385ac028bb7d8863d70afa02d63181894e0b2d51b99c0c525ef24538c44c24',
'blockNumber': '4620855',
'confirmations': '7045304',
'contractAddress': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
'cumulativeGasUsed': '4047394',
'from': '0x731c6f8c754fa404cfcc2ed8035ef79262f65702',
'gas': '3000000',
'gasPrice': '1000000000',
'gasUsed': '1594668',
'hash': '0x5c9b0f9c6c32d2690771169ec62dd648fef7bce3d45fe8a6505d99fdcbade27a',
'input': 'deprecated',
'nonce': '5417',
'timeStamp': '1511634257',
'to': '0x642ae78fafbb8032da552d619ad43f1d81e4dd7c',
'tokenDecimal': '18',
'tokenName': 'Maker',
'tokenSymbol': 'MKR',
'transactionIndex': '55',
'value': '1000000000000000000000000'}],
'status': '1'}
>>> your_reponse['result']
[{'blockHash': '0xee385ac028bb7d8863d70afa02d63181894e0b2d51b99c0c525ef24538c44c24',
'blockNumber': '4620855',
'confirmations': '7045304',
'contractAddress': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
'cumulativeGasUsed': '4047394',
'from': '0x731c6f8c754fa404cfcc2ed8035ef79262f65702',
'gas': '3000000',
'gasPrice': '1000000000',
'gasUsed': '1594668',
'hash': '0x5c9b0f9c6c32d2690771169ec62dd648fef7bce3d45fe8a6505d99fdcbade27a',
'input': 'deprecated',
'nonce': '5417',
'timeStamp': '1511634257',
'to': '0x642ae78fafbb8032da552d619ad43f1d81e4dd7c',
'tokenDecimal': '18',
'tokenName': 'Maker',
'tokenSymbol': 'MKR',
'transactionIndex': '55',
'value': '1000000000000000000000000'}]
>>> print(your_reponse['result'][0]['gas'])
3000000
Use this recursive function to work around changes in API response:
def price_of_gas(inp):
def recursive_function(inp):
if type(inp) is list:
for i in inp:
ans = recursive_function(i)
if ans!=None: return ans
elif type(inp) is dict:
if 'gas' in inp: return inp['gas']
for i in inp:
ans = recursive_function(inp[i])
if ans!=None: return ans
else: return None
ans = recursive_function(inp)
return ans if ans else "Could NOT find the gas"
price_of_gas(your_reponse)
See the JSON response carefully. Its a dictionary with three keys (status, message and result). The key result is a list having another dictionary inside.
JSON response:
{
"status":"1",
"message":"OK",
"result":[
{
"blockNumber":"4620855",
"timeStamp":"1511634257",
"hash":"0x5c9b0f9c6c32d2690771169ec62dd648fef7bce3d45fe8a6505d99fdcbade27a",
"nonce":"5417",
"blockHash":"0xee385ac028bb7d8863d70afa02d63181894e0b2d51b99c0c525ef24538c44c24",
"from":"0x731c6f8c754fa404cfcc2ed8035ef79262f65702",
"contractAddress":"0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2",
"to":"0x642ae78fafbb8032da552d619ad43f1d81e4dd7c",
"value":"1000000000000000000000000",
"tokenName":"Maker",
"tokenSymbol":"MKR",
"tokenDecimal":"18",
"transactionIndex":"55",
"gas":"3000000",
"gasPrice":"1000000000",
"gasUsed":"1594668",
"cumulativeGasUsed":"4047394",
"input":"deprecated",
"confirmations":"7045304"
}
]
}
So if you need to access the keys present inside the result dictionary, you have to nest your code accordingly.
result.json['result'][0]['gas']

Python MagicMock.return_value returning MagicMock instead of return_value

I have a function that verifies if a given input string is a proper GCP zone:
def validate_zone(compute, project_id, zone):
try:
zone_response = compute.zones().get(project=project_id, zone=zone).execute()
print(zone_response)
print(zone_response.return_value)
if ['status'] in zone_response:
zone_details = {
'status': zone_response['status'],
'region': zone_response['region'],
'name': zone_response['name']
}
return zone_details
else:
return "Zone {} not found for project {}".format(zone, project_id)
except HttpError as error:
print("Error calling zone {}: \n {}".format(zone, error))
I am trying to write a test to verify that but I can't mock the output of the compute method correctly.
#patch('googleapiclient.discovery')
def test_validate_zone(self, mock_response):
compute = mock_response.build(serviceName='compute', version='v1')
compute.zones().get(project_id=self.project_id, zone=self.zone).execute().return_value = {
'status': 'status',
'region': 'region',
'name': 'name'
}
zone_response = inventory.validate_zone(compute, self.project_id, self.zone)
print(zone_response)
This results in the zone_response output being a MagicMock object with its return_value being correct as developed in the test.
zone_response = MagicMock name='discovery.build().zones().get().execute()' id='139870134525456'
zone_response.return_value = {'status': 'status', 'region': 'region', 'name': 'name'}
Any ideas on what I'm doing wrong? I've been trying to write tests for this for quite a while so maybe my approach is just off.
Turns out the issue was the () on the execute method in the test. So the correct test should be:
#patch('inventory.discovery.build', serviceName='compute', version='v1')
def test_validate_zone(self, compute):
print(compute)
compute.zones().get(project_id=self.project_id, zone=self.zone).execute.return_value = {
'status': 'status',
'region': 'region',
'name': 'name'
}
zone_response = inventory.validate_zone(compute, self.project_id, self.zone)
print(zone_response)
Source can be found at: https://realpython.com/python-mock-library/#managing-a-mocks-return-value

Update values inside dictionary of a dictionary

I have a dictionary that contains the information to make a POST request to site and fetch data.
Below is my dictionary
request_dict = {
"scope_id": "user1",
"tool_id": "appdynamics",
"api_id": "get metrics names",
"input_params": {"user": "abcd", "pwd": "1234", "acc_id": "ab12", "app_id": "TestApp",
"metric-path": "ars",
"time-range-type": "BEFORE_NOW", "duration-in-mins": 10},
"output_filters": null
}
I have another dictionary which contains the updated value for some of the keys mentioned in the above dictionary.Below is the dictionary
new_metric_dict = {'tool_id': 'appdynamics', 'pwd': '5678', 'user': 'efgh', 'metric-path': 'ars', 'app_id': 'TestApp', 'acc_id': 'cd34'}
As you can see I got new values for user, pwd and acc_id.But I am unable to put these values into the POST request dictionary since they are inside input_params.
I tried doing something like this
for i in request_dict:
if type(request_dict[i]) == dict:
for j in request_dict:
if j in new_metric_dict:
request_dict[j] = new_metric_dict[j]
if i in new_metric_dict:
request_dict[i] = new_metric_dict[i]
But it doesn't work.What is an efficient way to achieve this?
This is one way, which creates a new dictionary with the desired updates.
def update_dict(d, new):
out = {}
for m, val in d.items():
if isinstance(d[m], dict):
out[m] = {k: new.get(k, d[m][k]) for k, v in d[m].items()}
else:
out[m] = new.get(m, val)
return out
new = update_dict(request_dict, new_metric_dict)
# {'api_id': 'get metrics names',
# 'input_params': {'acc_id': 'cd34',
# 'app_id': 'TestApp',
# 'duration-in-mins': 10,
# 'metric-path': 'ars',
# 'pwd': '5678',
# 'time-range-type': 'BEFORE_NOW',
# 'user': 'efgh'},
# 'output_filters': 'null',
# 'scope_id': 'user1',
# 'tool_id': 'appdynamics'}

Categories

Resources