This question already has answers here:
Expanding tuples into arguments
(5 answers)
Closed 4 years ago.
My code:
def validate_record_schema():
"""Validate that the 0 or more Payload dicts in record
use proper types"""
err_path = "root"
try:
for record in test1:
for device in record.get('Payload', []):
payload = device.get('Payload', None)
if payload is None:
continue
device = payload["Device"]
key_data = ((device["ManualAdded"],bool), (device["Location"],str))
for i in key_data:
if not isinstance(i):
return False
except KeyError as err_path:
print("missing key")
return False
return True
print(validate_record_schema())
I want to do it like below but i am not able to do it.
key_data = ((device["ManualAdded"],bool), (device["Location"],str))
for i in key_data:
if not isinstance(i):
return False
If i am doing like below it's working
if not isinstance((device["ManualAdded"],bool)):
return False
But i need to do it like above.How can i do this?
Json data
test1 = [{'Id': '12', 'Type': 'DevicePropertyChangedEvent', 'Payload': [{'DeviceType': 'producttype', 'DeviceId': 2, 'IsFast': False, 'Payload': {'DeviceInstanceId': 2, 'IsResetNeeded': False, 'ProductType': 'product'
, 'Product': {'Family': 'home'}, 'Device': {'DeviceFirmwareUpdate': {'DeviceUpdateStatus': None, 'DeviceUpdateInProgress': None, 'DeviceUpdateProgress': None, 'LastDeviceUpdateId': None}, 'ManualAdded': False,
'Name': {'Value': 'Jigital60asew', 'IsUnique': True}, 'State': None, 'Location': "dg", 'Serial': None, 'Version': '2.0.1.100'}}}]}]
You can expand the tuple and pass its members as individual args using the * operator.
key_data = (('This is a string', str), ('This is a string', bool))
for i in key_data:
if isinstance(*i):
print("yes")
else:
print("no")
Related
I am calling the list operation to retrieve the metadata values of a blob storage.
My code looks like:
blob_service_list = storage_client.blob_services.list('rg-exercise1', 'sa36730')
for items in blob_service_list:
print((items.as_dict()))
What's happening in this case is that the returned output only contains the items which had a corresponding Azure object:
{'id': '/subscriptions/0601ba03-2e68-461a-a239-98cxxxxxx/resourceGroups/rg-exercise1/providers/Microsoft.Storage/storageAccounts/sa36730/blobServices/default', 'name': 'default', 'type': 'Microsoft.Storage/storageAccounts/blobServices', 'sku': {'name': 'Standard_LRS', 'tier': 'Standard'}, 'cors': {'cors_rules': [{'allowed_origins': ['www.xyz.com'], 'allowed_methods': ['GET'], 'max_age_in_seconds': 0, 'exposed_headers': [''], 'allowed_headers': ['']}]}, 'delete_retention_policy': {'enabled': False}}
Where-as, If I do a simple print of items, the output is much larger:
{'additional_properties': {}, 'id': '/subscriptions/0601ba03-2e68-461a-a239-98c1xxxxx/resourceGroups/rg-exercise1/providers/Microsoft.Storage/storageAccounts/sa36730/blobServices/default', 'name': 'default', 'type': 'Microsoft.Storage/storageAccounts/blobServices', 'sku': <azure.mgmt.storage.v2021_06_01.models._models_py3.Sku object at 0x7ff2f8f1a520>, 'cors': <azure.mgmt.storage.v2021_06_01.models._models_py3.CorsRules object at 0x7ff2f8f1a640>, 'default_service_version': None, 'delete_retention_policy': <azure.mgmt.storage.v2021_06_01.models._models_py3.DeleteRetentionPolicy object at 0x7ff2f8f1a6d0>, 'is_versioning_enabled': None, 'automatic_snapshot_policy_enabled': None, 'change_feed': None, 'restore_policy': None, 'container_delete_retention_policy': None, 'last_access_time_tracking_policy': None}
Any value which is None has been removed from my example code. How can I extend my example code to include the None fields and have the final output as a list?
I tried in my environment and got below results:
If you need to include the None values in the dictionary you can follow the below code:
Code:
from azure.mgmt.storage import StorageManagementClient
from azure.identity import DefaultAzureCredential
storage_client=StorageManagementClient(credential=DefaultAzureCredential(),subscription_id="<your sub id>")
blob_service_list = storage_client.blob_services.list('v-venkat-rg', 'venkat123')
for items in blob_service_list:
items_dict = items.as_dict()
for key, value in items.__dict__.items():
if value is None:
items_dict[key] = value
print(items_dict)
Console:
The above code executed with None value successfully.
Having a bit of difficulties here with looping through this json object content.
The json file is as such:
[{'archived': False,
'cache_ttl': None,
'collection': {'archived': False,
'authority_level': None,
'color': '#509EE3',
'description': None,
'id': 525,
'location': '/450/',
'name': 'eaf',
'namespace': None,
'personal_owner_id': None,
'slug': 'eaf'},
'collection_id': 525,
'collection_position': None,
'created_at': '2022-01-06T20:51:17.06376Z',
'creator_id': 1,
'database_id': 4,
}, ... ]
And I want to loop through each dict in the list check that the collection is not empty and then for each collection if the location equals '/450/' return append that dict to a list.
My code is as follows.
content = json.loads(res.text)
for q in content:
if q['collection']:
for col in q['collection']:
if col['location'] == '/450/':
data.append(q)
print(data)
Having played around with it I keep either getting ValueError: too many values to unpack (expected 2) OR TypeError: string indices must be integers
Any help with my structure would be much appreciated thanks.
Disclaimer:
I had previously written this as a list comprehension and it worked like a charm however that doesnt work anymore as I now need to check if the collection is empty.
How I wrote it previously:
content = [ x for x in content if x['collection']['location'] == '/450/']
That should work for you:
for q in content:
if q['collection']['location'] == '/450/':
data.append(q)
print(data)
If you go with for loop with for col in q['collection'], you just iterate over keys inside q['collection'], so cols = ['archived', 'authority_level', ...].
From your previous list comprehension, "location" is a key in q["collection"].
When you write
for col in q["collection"]
You are iterating over the keys in q["collection"]. One of these keys is "location". Your for loop seems to iterate more than necessary:
if q['collection'] and "location" in q["collection"] and q["collection"]["location"] == "/450/":
data.append(q)
Your Code Has Way too Iterations Than needed.
The error TypeError: string indices must be integers occurs at the second conditional statement when you check col['location'] = "/450/".
That's because not all tokens in the collection object have sub-objects where you can get data with their key.
Take a look at your old code and the modified code for more in depth understanding.
# Your old json datas
content = [{'archived': False,
'cache_ttl': None,
'collection': {'archived': False,
'authority_level': None,
'color': '#509EE3',
'description': None,
'id': 525,
'location': '/450/',
'name': 'eaf',
'namespace': None,
'personal_owner_id': None,
'slug': 'eaf'},
'collection_id': 525,
'collection_position': None,
'created_at': '2022-01-06T20:51:17.06376Z',
'creator_id': 1,
'database_id': 4,
} ]
data = []
for q in content:
if q['collection']:
for col in q['collection']:
if col['location'] == '/450/': # The first object in collection object is [archived] which is a string, this causes the program to throw error
data.append(q)
print(data)
Here is the modified code
# Your json datas
json_datas = [{'archived': False,
'cache_ttl': None,
'collection': {'archived': False,
'authority_level': None,
'color': '#509EE3',
'description': None,
'id': 525,
'location': '/450/',
'name': 'eaf',
'namespace': None,
'personal_owner_id': None,
'slug': 'eaf'},
'collection_id': 525,
'collection_position': None,
'created_at': '2022-01-06T20:51:17.06376Z',
'creator_id': 1,
'database_id': 4,
} ]
list_data = [] # Your list data in which appends the json data if the location is /450/
for data in json_datas: # Getting each Json data
if len(data["collection"]): # Continue if the length of collection is not 0 [NOTE: 0 = False, 1 or more = True]
if data['collection']['location'] == "/450/": # Check the location
list_data.append(data) # Append if true
print(list_data)
Don't need to iterate over the collection object since it's a dictionary and just need to check the location property.
Also, in case the "collection" or "location" properties are not present then use dict.get(key) function rather than dict[key] since the latter will raise a KeyError exception if key is not found and get() returns None value if key is not found.
content = [{'archived': False,
'cache_ttl': None,
'collection': {'archived': False,
'authority_level': None,
'color': '#509EE3',
'description': None,
'id': 525,
'location': '/450/',
'name': 'eaf',
'namespace': None,
'personal_owner_id': None,
'slug': 'eaf'},
'collection_id': 525,
'collection_position': None,
'created_at': '2022-01-06T20:51:17.06376Z',
'creator_id': 1,
'database_id': 4,
},
{'foo': None}
]
#content = json.loads(res.text)
data = []
for q in content:
c = q.get('collection')
if c and c.get('location') == '/450/':
data.append(q)
print(data)
Output:
[{'archived': False, 'cache_ttl': None, 'collection': { 'location': '/450/', 'name': 'eaf', 'namespace': None }, ...}]
I currently have a dictionary of lists that i am trying to map each list element to a customer id and by brand id.
So far, this is what I come up with - the issue is that i am missing the mapping between brand id and customer ids.
Apologies for not sharing the full inputs for replication as it contains api keys/secrets.
The goal is to have a dictionary per customer id belonging to the same brand id.
Currently, the loop is iterating over the length of brand ids so coercing customer id into a nested list. Attached are also snippet of current versus desired output.
*df_dict below is a dictionary of dataframes per brand id
df_dict = {}
for brand, values in reduced_df_send_message_now.groupby('brand_id'):
df_dict.update({'brand_' + str(brand) : values.reset_index(drop=True)})
customers = [['1704283852'], ['1542053114', '1268330200', '1006435031']]
casino_ids = [['2000'], ['2080', '2080', '2080']]
batch_list = []
for c, i in enumerate(df_dict):
batch = Batch()
batch.user_identities = {'customer_id': customers[c]}
batch.user_attributes = {
'casino_id': casino_ids[c]
}
print(batch)
batch_list.append(batch)
# current vs target split records
current = dict(
{'api_key': None,
'application_info': None,
'consent_state': None,
'context': None,
'deleted_user_attributes': None,
'device_info': None,
'environment': None,
'events': None,
'ip': None,
'mp_deviceid': None,
'mpid': None,
'schema_version': None,
'source_request_id': None,
'user_attributes': {'brand_id': ['2080', '2080', '2080']},
'user_identities': {'customer_id': ['1542053114', '1268330200', '1006435031']}
}
)
target = dict(
{'api_key': None,
'application_info': None,
'consent_state': None,
'context': None,
'deleted_user_attributes': None,
'device_info': None,
'environment': None,
'events': None,
'ip': None,
'mp_deviceid': None,
'mpid': None,
'schema_version': None,
'source_request_id': None,
'user_attributes': {'brand_id': '2080'},
'user_identities': {'customer_id': '1542053114'}
}
)
target2 = dict(
{'api_key': None,
'application_info': None,
'consent_state': None,
'context': None,
'deleted_user_attributes': None,
'device_info': None,
'environment': None,
'events': None,
'ip': None,
'mp_deviceid': None,
'mpid': None,
'schema_version': None,
'source_request_id': None,
'user_attributes': {'brand_id': '2080'},
'user_identities': {'customer_id': '1268330200'}
}
)
target3 = dict(
{'api_key': None,
'application_info': None,
'consent_state': None,
'context': None,
'deleted_user_attributes': None,
'device_info': None,
'environment': None,
'events': None,
'ip': None,
'mp_deviceid': None,
'mpid': None,
'schema_version': None,
'source_request_id': None,
'user_attributes': {'brand_id': '2080'},
'user_identities': {'customer_id': '1006435031'}
}
)
I think i figured it out - results match what i was trying to achieve (in the post). below is the code i used to split customer dictionary by brand id -
** the following was also fixed by last line**: only issue is that the function has to be called at the end with the brand id as argument (i am only working with 3 brand ids for now) but feel free to suggest a dynamic way of looping through brand ids to catch new ones (that are currently not part of the pull).
df_dict = {}
for brand, values in reduced_df_send_message_now.groupby('brand_id'):
df_dict.update({str(brand) : values.reset_index(drop=True)})
# function to transforme each column needed to a list so that we can include them as lists to dicts in batch further below
def col_to_list(df):
if df.shape[0] > 0:
# customers = df[['customerid', 'brand_id']].values.tolist()
customers = df['customerid'].values.tolist()
brand_ids = df['brand_id'].values.tolist()
casino_ids = df['signup_casino_id'].values.tolist()
balances = df['total_balance'].values.tolist()
free_bet_cash_balance = df['free_bet_cash_balance'].values.tolist()
bet_churn_cat = df['bet_churn_category'].values.tolist()
bet_churn_score = df['customer_lapsing_factor'].values.tolist()
login_churn_cat = df['login_churn_category'].values.tolist()
login_churn_score = df['login_churn_score'].values.tolist()
group_type = df['group_type'].values.tolist()
api_key = df['key'].values.tolist()
api_secret = df['secret'].values.tolist()
return customers, brand_ids, casino_ids, balances, free_bet_cash_balance, bet_churn_cat,\
bet_churn_score, login_churn_cat, login_churn_score, group_type, api_key, api_secret
else:
None
def return_needed_lists_by_brand(brand_id):
list_names = ['customer_id', 'casino_id', 'balances']
if len(df_dict.keys()) == 0:
return
if brand_id not in df_dict.keys():
return
else:
customers = col_to_list(df_dict[brand_id])[0]
casino_ids = col_to_list(df_dict[brand_id])[2]
balances = col_to_list(df_dict[brand_id])[3]
return dict(zip(list_names, [customers, casino_ids]))
def upload_brand(brand_id):
batch_list = []
if brand_id not in df_dict.keys():
return
for i in range(len(return_needed_lists_by_brand(brand_id)['customer_id'])):
batch = Batch()
try:
batch.user_identities = {'customer_id': return_needed_lists_by_brand(brand_id)['customer_id'][i]}
batch.user_attributes = {
'casino_id': return_needed_lists_by_brand(brand_id)['casino_id'][i]
}
print(batch)
except:
pass
batch_list.append(batch)
#upload_brand("213")
# final upload
for i in brand_id_list:
upload_brand(str(i))
In python3 I need to get a JSON response from an API call,
and parse it so I will get a dictionary That only contains the data I need.
The final dictionary I ecxpt to get is as follows:
{'Severity Rules': ('cc55c459-eb1a-11e8-9db4-0669bdfa776e', ['cc637182-eb1a-11e8-9db4-0669bdfa776e']), 'auto_collector': ('57e9a4ec-21f7-4e0e-88da-f0f1fda4c9d1', ['0ab2470a-451e-11eb-8856-06364196e782'])}
the JSON response returns the following output:
{
'RuleGroups': [{
'Id': 'cc55c459-eb1a-11e8-9db4-0669bdfa776e',
'Name': 'Severity Rules',
'Order': 1,
'Enabled': True,
'Rules': [{
'Id': 'cc637182-eb1a-11e8-9db4-0669bdfa776e',
'Name': 'Severity Rule',
'Description': 'Look for default severity text',
'Enabled': False,
'RuleMatchers': None,
'Rule': '\\b(?P<severity>DEBUG|TRACE|INFO|WARN|ERROR|FATAL|EXCEPTION|[I|i]nfo|[W|w]arn|[E|e]rror|[E|e]xception)\\b',
'SourceField': 'text',
'DestinationField': 'text',
'ReplaceNewVal': '',
'Type': 'extract',
'Order': 21520,
'KeepBlockedLogs': False
}],
'Type': 'user'
}, {
'Id': '4f6fa7c6-d60f-49cd-8c3d-02dcdff6e54c',
'Name': 'auto_collector',
'Order': 4,
'Enabled': True,
'Rules': [{
'Id': '2d6bdc1d-4064-11eb-8856-06364196e782',
'Name': 'auto_collector',
'Description': 'DO NOT CHANGE!! Created via API coralogix-blocker tool',
'Enabled': False,
'RuleMatchers': None,
'Rule': 'AUTODISABLED',
'SourceField': 'subsystemName',
'DestinationField': 'subsystemName',
'ReplaceNewVal': '',
'Type': 'block',
'Order': 1,
'KeepBlockedLogs': False
}],
'Type': 'user'
}]
}
I was able to create a dictionary that contains the name and the RuleGroupsID, like that:
response = requests.get(url,headers=headers)
output = response.json()
outputlist=(output["RuleGroups"])
groupRuleName = [li['Name'] for li in outputlist]
groupRuleID = [li['Id'] for li in outputlist]
# Create a dictionary of NAME + ID
ruleDic = {}
for key in groupRuleName:
for value in groupRuleID:
ruleDic[key] = value
groupRuleID.remove(value)
break
Which gave me a simple dictionary:
{'Severity Rules': 'cc55c459-eb1a-11e8-9db4-0669bdfa776e', 'Rewrites': 'ddbaa27e-1747-11e9-9db4-0669bdfa776e', 'Extract': '0cb937b6-2354-d23a-5806-4559b1f1e540', 'auto_collector': '4f6fa7c6-d60f-49cd-8c3d-02dcdff6e54c'}
but when I tried to parse it as nested JSON things just didn't work.
In the end, I managed to create a function that returns this dictionary,
I'm doing it by breaking the JSON into 3 lists by the needed elements (which are Name, Id, and Rules from the first nest), and then create another list from the nested JSON ( which listed everything under Rule) which only create a list from the keyword "Id".
Finally creating a dictionary using a zip command on the lists and dictionaries created earlier.
def get_filtered_rules() -> List[dict]:
groupRuleName = [li['Name'] for li in outputlist]
groupRuleID = [li['Id'] for li in outputlist]
ruleIDList = [li['Rules'] for li in outputlist]
ruleIDListClean = []
ruleClean = []
for sublist in ruleIDList:
try:
lstRule = [item['Rule'] for item in sublist]
ruleClean.append(lstRule)
ruleContent=list(zip(groupRuleName, ruleClean))
ruleContentDictionary = dict(ruleContent)
lstID = [item['Id'] for item in sublist]
ruleIDListClean.append(lstID)
# Create a dictionary of NAME + ID + RuleID
ruleDic = dict(zip(groupRuleName, zip(groupRuleID, ruleIDListClean)))
except Exception as e: print(e)
return ruleDic
I need to pass the value of a variable as a key of keyword agument.
def success_response(msg=None,**kwargs):
output = {"status": 'success',
"message": msg if msg else 'Success Msg'}
for key,value in kwargs.items():
output.update({key:value})
return output
the_key = 'purchase'
the_value = [
{"id": 1,"name":"Product1"},
{"id": 2,"name":"Product2"}
]
success_response(the_key=the_value)
actual output is
{'status': 'success', 'message': 'Success Msg', 'the_key': [{'id': 1, 'name': 'Product1'}, {'id': 2, 'name': 'Product2'}]}
expected output is
{'status': 'success', 'message': 'Success Msg', 'purchase': [{'id': 1, 'name': 'Product1'}, {'id': 2, 'name': 'Product2'}]}
I tried eval()
success_response(eval(the_key)=the_value)
but got the exception
SyntaxError: keyword can't be an expression
Use:
success_response(**{the_key: the_value})
Instead of:
success_response(the_key=the_value)
The key in this line:
for key,value in kwargs.items():
is the name of the keyword argument. In this case, key will be the_key which means the dictionary value passed to output.update(value) will be
[
{"id": 1,"name":"Product1"},
{"id": 2,"name":"Product2"}
]
I think what you really want is:
success_response(purchase=the_value)