How to make a dictionary generator?
The task is to generate a dictionary of cities of this type:
{city: {'distance': '443 mi', 'duration': '9 hours 0 mins'}}
Here is the model:
@python_2_unicode_compatible
class Route(Base):
cities = models.ManyToManyField(City)
@cached_property
def cities_arrival_time(self, *args, **kwargs):
r = lambda: x, y: requests.get(
'https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&mode=driving&origins={}&destinations={}&key={}'.format(
x.name,
y.name,
settings.GEOPOSITION_GOOGLE_MAPS_API_KEY
)
)
After that, you need to transform duration
in such a way as to get datetime
The problem is that now I don't understand how to write a generator that will iterate QuerySet
excluding the last element.
1 answers
I did it completely differently.
import json
import urllib
import os
import copy
import ast
import operator
import requests
DISTANCE_MATRIX_URL = "http://maps.googleapis.com/maps/api/distancematrix/"
class DM(object):
def __init__(self, api_key=None, url=DISTANCE_MATRIX_URL):
self.api_key = api_key
self.url = url
self.response = ''
self.dict_response = {'distance': {'value': {}, 'text': {}, },
'duration': {'value': {}, 'text': {}, },
}
self.origins = ''
self.destinations = ''
def make_request(self, origins, destinations, mode='driving'):
data = {}
self.origins = [origins] if type(origins) == str else origins
self.destinations = [destinations] if type(destinations) == str else destinations
data['origins'] = origins if type(origins) == str else '|'.join(origins)
data['destinations'] = destinations if type(destinations) == str else '|'.join(destinations)
data['mode'] = mode
url_values = urllib.parse.urlencode(data)
output_format = 'json'
url = os.path.join(self.url, output_format)
self.response = ast.literal_eval((urllib.request.urlopen(url + '?' + url_values).read()).decode("utf-8"))['rows']
self.dict_response = {'distance': {'value': {}, 'text': {}, }, # Reset temporary dict
'duration': {'value': {}, 'text': {}, },
}
def __get_response_element_data(self, key1, key2):
if not self.dict_response[key1][key2]:
l = self.response
for i, orig in enumerate(self.origins):
self.dict_response[key1][key2][orig] = {}
for j, dest in enumerate(self.destinations):
if l[i]['elements'][j]['status'] == 'OK':
self.dict_response[key1][key2][orig][dest] = l[i]['elements'][j][key1][key2]
else:
self.dict_response[key1][key2][orig][dest] = l[i]['elements'][j]['status']
return self.dict_response[key1][key2]
def get_distance_values(self):
return self.__get_response_element_data('distance', 'value')
def get_distance_texts(self):
return self.__get_response_element_data('distance', 'text')
def get_time_values(self):
return self.__get_response_element_data('duration', 'value')
def get_time_texts(self):
return self.__get_response_element_data('duration', 'text')
def get_closest_points(self, max_distance=None, num=10, origin_index=0, origin_raw=None):
if not self.dict_response['distance']['value']:
self.get_distance_values()
if origin_raw:
origin = copy.deepcopy(self.dict_response['distance']['value'][origin_raw])
else:
origin = copy.deepcopy(self.dict_response['distance']['value'][self.origins[origin_index]])
tmp_origin = copy.deepcopy(origin)
if max_distance:
for k, v in tmp_origin.iteritems():
if v > max_distance:
del(origin[k])
if origin:
return sorted(origin.iteritems(), key=operator.itemgetter(1))[:num]
It works like this:
a = DM()
a.make_request(['Minsk'], ['Moscow'])
# We give one origin
(the point from which we count) and one destination
(the destination).
If we want to get the result in text format (it is less accurate), then we do this:
a.get_distance_texts()
And we get the result:
{'Minsk': {'Moscow': '717 km'}}
If you need to calculate the distance between one origin
and several destination
, then do this:
a.make_request(['Minsk'], ['Moscow', 'Sankt-Peterburg'])
a.get_distance_texts()
{'Minsk': {'Moscow': '717 km', 'Sankt-Peterburg': '795 km'}}
In reality, the distance is different. But the difference between Moscow and St. Petersburg are more or less accurate.
This is how we get the data in meters:
a.get_distance_values()
{'Minsk': {'Moscow': 717038, 'Sankt-Peterburg': 795481}}
And so, we get duration
a.get_time_values()
{'Minsk': {'Moscow': 28751, 'Sankt-Peterburg': 36415}}
Similarly, with text display:
a.get_time_texts()
{'Minsk': {'Moscow': '7 hours 59 mins', 'Sankt-Peterburg': '10 hours 7 mins'}}
If you need to know about the distance between several origins
and destinations
, then so:
a.make_request(['Minsk', 'Moscow'], ['Moscow', 'Varshava'])
a.get_distance_texts()
{'Minsk': {'Moscow': '717 km', 'Varshava': '554 km'}, 'Moscow': {'Moscow': '1 m', 'Varshava': '1,264 km'}}
And here it is:
a.get_closest_points()
We get a list of tuples, where the first element is the destination, and the second is the distance from the first origin
[('Varshava', 553521), ('Moscow', 717038)]