Beispiel #1
0
    def answer_query(self, query):
        """Selects the top N geographically closest SliverTools to the client.

        Finds the top N closest SliverTools to the client and returns them.
        Note that N is currently hardcoded to 4.

        Args:
            query: A LookupQuery instance.

        Returns:
            A list of SliverTool entities on success, or None if there is no
            SliverTool available that matches the query.
        """
        # Return no more than MAX_RESULTS SliverTools in the result.
        MAX_RESULTS = 4
        candidates = self.get_candidates(query)
        if len(candidates) == 0:
            logging.error('No results found for %s.', query.tool_id)
            return None

        if (query.latitude is None) or (query.longitude is None):
            logging.warning('No latide/longitude, return a random sliver tool.')
            return [random.choice(candidates)]

        min_distance = float('+inf')
        sliver_tool_bins = {}
        sliver_tools = []
        distances = {}

        # Combine the candidates into bins by site.
        for sliver_tool in candidates:
            if not sliver_tool_bins.has_key(sliver_tool.site_id):
                sliver_tool_bins[sliver_tool.site_id] = [sliver_tool]
            else:
                sliver_tool_bins[sliver_tool.site_id].append(sliver_tool)

        for site_id in sliver_tool_bins:
            # Take a random sliver from the list.
            sliver_tool = random.choice(sliver_tool_bins[site_id])
            # Check if we already computed the distance of this site.
            if distances.has_key(sliver_tool.site_id):
                current_distance = distances[sliver_tool.site_id]
            else:
                current_distance = distance.distance(
                    query.latitude,
                    query.longitude,
                    sliver_tool.latitude,
                    sliver_tool.longitude)
                distances[sliver_tool.site_id] = current_distance

            sliver_tools.append(\
                sliver_tool_distance.SliverToolDistance(sliver_tool, \
                                                        current_distance))

        sliver_tools_sorted = sorted(sliver_tools, key=attrgetter('distance'))
        final_results = []
        for std in sliver_tools_sorted:
            final_results.append(std.sliver_tool)
        return final_results[:MAX_RESULTS]
Beispiel #2
0
 def _add_candidate(self, query, candidate, site_distances, tool_distances):
     if candidate.site_id not in site_distances:
         site_distances[candidate.site_id] = distance.distance(
             query.latitude, query.longitude, candidate.latitude,
             candidate.longitude)
         tool_distances.append({
             'distance': site_distances[candidate.site_id],
             'tool': candidate
         })
Beispiel #3
0
 def _add_candidate(self, query, candidate, site_distances, tool_distances):
     if candidate.site_id not in site_distances:
         site_distances[candidate.site_id] = distance.distance(
             query.latitude, query.longitude, candidate.latitude,
             candidate.longitude)
         tool_distances.append({
             'distance': site_distances[candidate.site_id],
             'tool': candidate
         })
Beispiel #4
0
 def testInvalidInputs(self):
     import math
     from numbers import Number
     dist = 0
     try:
         dist = distance.distance(-700, 1000, 999, -5454)
     except Exception:
         self.fail("distance threw an exception on invalid entry")
     self.assertTrue(isinstance(dist, Number))
     self.assertFalse(math.isnan(dist))
 def testInvalidInputs(self):
     import math
     from numbers import Number
     dist = 0
     try:
         dist = distance.distance(-700, 1000, 999, -5454)
     except Exception:
         self.fail("distance threw an exception on invalid entry")
     self.assertTrue(isinstance(dist, Number))
     self.assertFalse(math.isnan(dist))
Beispiel #6
0
    def answer_query(self, query):
        """Selects the geographically closest SliverTool.

        Args:
            query: A LookupQuery instance.

        Returns:
            A SliverTool entity in case of success, or None if there is no
            SliverTool available that matches the query.
        """
        candidates = self.get_candidates(query)
        if len(candidates) == 0:
            logging.error('No results found for %s.', query.tool_id)
            return None

        if (query.latitude is None) or (query.longitude is None):
            logging.warning('No latide/longitude, return a random sliver tool.')
            return [random.choice(candidates)]

        min_distance = float('+inf')
        closest_sliver_tools = []
        distances = {}

        # Compute for each SliverTool the distance and add keep in the
        # 'closest_sliver_tools' list only the SliverTools whose distance is
        # less or equal than the current minimum.
        for sliver_tool in candidates:
            # Check if we already computed the distance of this site.
            if distances.has_key(sliver_tool.site_id):
                current_distance = distances[sliver_tool.site_id]
            else:
                current_distance = distance.distance(
                    query.latitude,
                    query.longitude,
                    sliver_tool.latitude,
                    sliver_tool.longitude)

                distances[sliver_tool.site_id] = current_distance

            # Update the min distance and add the SliverTool to the list.
            if current_distance < min_distance:
                min_distance = current_distance
                closest_sliver_tools = [sliver_tool]
            elif current_distance == min_distance:
                closest_sliver_tools.append(sliver_tool)

        # Add the min_distance to the query so it can be logged later. Round to
        # the next highest kilometre radius to remove precision.
        query.distance = math.ceil(min_distance)

        # Choose randomly among candidates with the same, minimum distance.
        return [random.choice(closest_sliver_tools)]
Beispiel #7
0
    def log_location(self, query, sliver_tools):
        """Logs the client Country and compares Maxmind to AppEngine distance"""
        if query.tool_id != 'ndt_ssl':
            # We only want to look at ndt_ssl clients for now.
            return

        if type(sliver_tools) != list or not sliver_tools:
            logging.info('unexpected sliver_tools type: %s', len(sliver_tools))
            return

        if query._geolocation_type != constants.GEOLOCATION_APP_ENGINE:
            # We cannot compare AppEngine location to Maxmind in this case.
            return

        t0 = datetime.datetime.now()

        # Log client country to display geomap summaries of client origins.
        logging.info('[client.country],%s', query.country)

        # Log only the first (closest) site.
        sliver_tool = sliver_tools[0]

        # Lookup the maxmind information, if it doesn't exist, then just
        # return.
        if not query._set_maxmind_geolocation(query.ip_address, None, None):
            return

        # Calculate the difference between the two systems.
        difference = distance.distance(query._gae_latitude,
                                       query._gae_longitude,
                                       query._maxmind_latitude,
                                       query._maxmind_longitude)

        logging.info(
            ('[server.distance],{scheme},{tool_id},{site_id},{country},'
             '{city},{same_country},{difference}').format(
                 scheme=self.request.scheme,
                 tool_id=query.tool_id,
                 site_id=sliver_tool.site_id,
                 country=sliver_tool.country,
                 city=sliver_tool.city,
                 same_country=(query._gae_country == query._maxmind_country),
                 difference=difference))

        t1 = datetime.datetime.now()
        logging.info('[log_location.delay],{delay}'.format(
            delay=str((t1 - t0).total_seconds())))
Beispiel #8
0
    def log_location(self, query, sliver_tools):
        """Logs the client Country and compares Maxmind to AppEngine distance"""
        if query.tool_id != 'ndt_ssl':
            # We only want to look at ndt_ssl clients for now.
            return

        if type(sliver_tools) != list or not sliver_tools:
            logging.info('unexpected sliver_tools type: %s', len(sliver_tools))
            return

        if query._geolocation_type != constants.GEOLOCATION_APP_ENGINE:
            # We cannot compare AppEngine location to Maxmind in this case.
            return

        t0 = datetime.datetime.now()

        # Log client country to display geomap summaries of client origins.
        logging.info('[client.country],%s', query.country)

        # Log only the first (closest) site.
        sliver_tool = sliver_tools[0]

        # Lookup the maxmind information.
        query._set_maxmind_geolocation(query.ip_address, None, None)

        # Calculate the difference between the two systems.
        difference = distance.distance(
            query._gae_latitude, query._gae_longitude, query._maxmind_latitude,
            query._maxmind_longitude)

        logging.info((
            '[server.distance],{scheme},{tool_id},{site_id},{country},'
            '{city},{same_country},{difference}').format(
                scheme=self.request.scheme,
                tool_id=query.tool_id,
                site_id=sliver_tool.site_id,
                country=sliver_tool.country,
                city=sliver_tool.city,
                same_country=(query._gae_country == query._maxmind_country),
                difference=difference))

        t1 = datetime.datetime.now()
        logging.info('[log_location.delay],{delay}'.format(delay=str((
            t1 - t0).total_seconds())))
Beispiel #9
0
    def _get_closest_n_candidates(self, query, max_results):
        """Selects the top N geographically closest SliverTools to the client.

        Args:
            query: A LookupQuery instance.
            max_results: The maximum number of candidates to return.

        Returns:
            A list of SliverTool entities on success, or None if there is no
            SliverTool available that matches the query.
        """
        candidates = self._get_matching_candidates(query)
        if not candidates:
            return None

        if (query.latitude is None) or (query.longitude is None):
            logging.warning(
                'No latitude/longitude, return random sliver tool(s).')
            return random.sample(candidates, min(len(candidates), max_results))

        # Pre-shuffle the candidates to randomize the order of equidistant
        # results.
        random.shuffle(candidates)

        site_distances = {}
        tool_distances = []
        for candidate in candidates:
            if candidate.site_id not in site_distances:
                site_distances[candidate.site_id] = distance.distance(
                    query.latitude, query.longitude, candidate.latitude,
                    candidate.longitude)
            tool_distances.append({
                'distance': site_distances[candidate.site_id],
                'tool': candidate
            })

        # Sort the tools by distance
        tool_distances.sort(key=lambda t: t['distance'])

        # Create a new list of just the sorted SliverTool objects.
        sorted_tools = [t['tool'] for t in tool_distances]
        return sorted_tools[:max_results]
Beispiel #10
0
    def _get_closest_n_candidates(self, query, max_results):
        """Selects the top N geographically closest SliverTools to the client.

        Args:
            query: A LookupQuery instance.
            max_results: The maximum number of candidates to return.

        Returns:
            A list of SliverTool entities on success, or None if there is no
            SliverTool available that matches the query.
        """
        candidates = self._get_matching_candidates(query)
        if not candidates:
            return None

        if (query.latitude is None) or (query.longitude is None):
            logging.warning(
                'No latitude/longitude, return random sliver tool(s).')
            return random.sample(candidates, min(len(candidates), max_results))

        # Pre-shuffle the candidates to randomize the order of equidistant
        # results.
        random.shuffle(candidates)

        site_distances = {}
        tool_distances = []
        for candidate in candidates:
            if candidate.site_id not in site_distances:
                site_distances[candidate.site_id] = distance.distance(
                    query.latitude, query.longitude, candidate.latitude,
                    candidate.longitude)
            tool_distances.append({
                'distance': site_distances[candidate.site_id],
                'tool': candidate
            })

        # Sort the tools by distance
        tool_distances.sort(key=lambda t: t['distance'])

        # Create a new list of just the sorted SliverTool objects.
        sorted_tools = [t['tool'] for t in tool_distances]
        return sorted_tools[:max_results]
Beispiel #11
0
 def testValidSmallDistance(self):
     dist = distance.distance(0, 0, 10, 10)
     self.assertEqual(1568.5205567985761, dist)
Beispiel #12
0
 def testValidLargeDistance(self):
     dist = distance.distance(20, 20, 100, 100)
     self.assertEqual(8009.5721050828461, dist)
 def testValidSmallDistance(self):
     dist = distance.distance(0, 0, 10, 10)
     self.assertEqual(1568.5205567985761, dist)
 def testValidLargeDistance(self):
     dist = distance.distance(20, 20, 100, 100)
     self.assertEqual(8009.5721050828461, dist)