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.

Author: Narnik Gamarnik, 2017-08-04

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)]
 2
Author: Narnik Gamarnik, 2017-08-05 04:27:48