Reputation: 974
There are 2 dicts of params:
fb_config = {
"page_likes" : {
"page_id": "1111",
"metric": "page_fans",
"since": "2019-01-01 00:00:00",
"until": "2019-01-01 00:00:00",
"date_preset" : "yesterday",
"period": "day"},
"page_impressions" : {"page_id": "1111",
"metric": "page_impressions",
"since": "yesterday",
"until": "yesterday",
"date_preset" : "yesterday",
"period": "day"},
"page_engaged_users" : {"page_id": "1111",
"metric": "page_engaged_users",
"since": "today",
"until": "today",
"date_preset" : "yesterday",
"period": "day"}}
and
ga_config =
{
'view_id_123': {'view_id': '123',
'start_date': "2019-01-01 00:00:00",
'end_date': "2019-01-01 00:00:00",
'metrics': [{'expression': 'ga:sessions'}, {'expression':'ga:pageviews'}, {'expression':'ga:users'}, {'expression':'ga:bounces'},
{'expression':'ga:avgSessionDuration'}],
'dimensions': [{'name': 'ga:year'}, {'name': 'ga:userType'}, {'name': 'ga:sessionCount'}, {'name': 'ga:browser'},
{'name': 'ga:source'}]},
'view_id_456': {'view_id': '456',
'start_date': "today",
'end_date': "today",
'metrics': [{'expression': 'ga:bounces'}, {'expression':'ga:users'}],
'dimensions': [{'name': 'ga:mobileDeviceModel'}, {'name': 'ga:dataSource'}]},
'view_id_789': {'view_id': '789',
'start_date': "yesterday",
'end_date': "yesterday",
'metrics': [{'expression': 'ga:bounces'}, {'expression':'ga:users'}],
'dimensions': [{'name': 'ga:mobileDeviceModel'}, {'name': 'ga:dataSource'}]}
}
In the code I can't keep the params and want to pull them from a json file. After loading the file all the dates have to be converted from str to datetime format.
This process is accomlished through several loops:
for values in params.values():
if 'since' in values:
if values['since'] == 'today':
values['since'] = datetime.now()
elif values['since'] == 'yesterday':
values['since'] = datetime.now() - timedelta(1)
else:
values['since'] = dt.datetime.strptime(values['since'], '%Y-%m-%d %H:%M:%S')
for values in params.values():
if 'until' in y:
if values['until'] == 'today':
values['until'] = datetime.now()
elif values['until'] == 'yesterday':
values['until'] = datetime.now() - timedelta(1)
else:
values['until'] = dt.datetime.strptime(y['until'], '%Y-%m-%d %H:%M:%S')
for values in params.values():
if 'start_date' in values:
if values['start_date'] == 'today':
values['start_date'] = datetime.now()
elif values['start_date'] == 'yesterday':
values['start_date'] = datetime.now() - timedelta(1)
else:
values['start_date'] = dt.datetime.strptime(values['start_date'], '%Y-%m-%d %H:%M:%S')
for values in params.values():
if 'end_date' in values:
if values['end_date'] == 'today':
values['end_date'] = datetime.now()
elif values['end_date'] == 'yesterday':
values['end_date'] = datetime.now() - timedelta(1)
else:
values['end_date'] = dt.datetime.strptime(values['end_date'], '%Y-%m-%d %H:%M:%S')
But the code is really ugly and DRY is violated. Are there better ways to iterate through nested dics and change the values of the certain keys?
Upvotes: 0
Views: 185
Reputation: 107587
Consider a nested dictionary comprehension and a dict mapping function for time conversion:
def time_conversion(arg):
switcher = {
'today': datetime.now(),
'yesterday': datetime.now() - timedelta(1)
}
output = (datetime.strptime(arg, '%Y-%m-%d %H:%M:%S')
if arg not in switcher.keys() else switcher.get(arg))
return output
new_params = {outer_k:{inner_k: time_conversion(inner_v)
if inner_k in ('since', 'until', 'date_preset') else inner_v
for inner_k, inner_v in outer_v.items()}
for outer_k, outer_v in params.items()}
Upvotes: 1
Reputation: 181
Instead of iterating over a nested dict after you load json, you need to use object_hook method of json.loads() to deserialize dates on import (I assume you're using built-in json library to parse json).
This has already been answered, please have a look at this thread: How to convert to a Python datetime object with JSON.loads?
Upvotes: 1
Reputation: 13064
I am not a Python developer, but I can see that you are looping 4 times over the same data, when you should do it only once:
def formatDate(value, default):
if value == 'today':
date = datetime.now()
elif value == 'yesterday':
date = datetime.now() - timedelta(1)
else:
date = default
return date
def your_function():
for values in params.values():
if 'since' in values:
values['since'] = formatDate(values['since'], dt.datetime.strptime(values['since'], '%Y-%m-%d %H:%M:%S'))
if 'until' in values:
values['until'] = formatDate(values['until'], dt.datetime.strptime(y['until'], '%Y-%m-%d %H:%M:%S'))
if 'start_date' in values:
values['start_date'] = formatDate(values['start_date'], dt.datetime.strptime(values['start_date'], '%Y-%m-%d %H:%M:%S'))
if 'end_date' in values:
values['end_date'] = formatDate(values['end_date'], dt.datetime.strptime(values['end_date'], '%Y-%m-%d %H:%M:%S'))
Code not tested, but I would extract the common logic to a function and have only one foreach loop.
Upvotes: 1
Reputation: 49803
This is a bit more compact:
for values in params.values():
for k in ('since','until','start_date','end_date'):
if k in values:
if values[k] == 'today':
values[k] = datetime.now()
elif values[k] == 'yesterday':
values[k] = datetime.now() - timedelta(1)
else:
values[k] = dt.datetime.strptime(values[k], '%Y-%m-%d %H:%M:%S')
Upvotes: 1