135 lines
5.0 KiB
Python
135 lines
5.0 KiB
Python
"""Call this endpoint to retrieve the timezone for a given location"""
|
|
from http import HTTPStatus
|
|
|
|
from selene.api import PublicEndpoint
|
|
from selene.data.geography import CityRepository
|
|
from selene.util.log import get_selene_logger
|
|
|
|
ONE_HUNDRED_MILES = 100
|
|
|
|
_log = get_selene_logger(__name__)
|
|
|
|
|
|
class GeolocationEndpoint(PublicEndpoint):
|
|
"""Selene endpoint that will search for a geography give a city name."""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.device_id = None
|
|
self.request_geolocation = None
|
|
self.cities = None
|
|
self._city_repo = None
|
|
|
|
@property
|
|
def city_repo(self):
|
|
"""Lazy load the CityRepository."""
|
|
if self._city_repo is None:
|
|
self._city_repo = CityRepository(self.db)
|
|
|
|
return self._city_repo
|
|
|
|
def get(self):
|
|
"""Handle a HTTP GET request."""
|
|
self.request_geolocation = self.request.args["location"].lower()
|
|
response_geolocation = self._get_geolocation()
|
|
|
|
return dict(data=response_geolocation), HTTPStatus.OK
|
|
|
|
def _get_geolocation(self):
|
|
"""Try our best to find a geolocation matching the request."""
|
|
self._get_cities()
|
|
if self.cities:
|
|
selected_geolocation = self._select_geolocation_from_cities()
|
|
else:
|
|
selected_geolocation = self.city_repo.get_biggest_city_in_region(
|
|
self.request_geolocation
|
|
)
|
|
|
|
if selected_geolocation is None:
|
|
selected_geolocation = self.city_repo.get_biggest_city_in_country(
|
|
self.request_geolocation
|
|
)
|
|
|
|
if selected_geolocation is not None:
|
|
selected_geolocation.latitude = float(selected_geolocation.latitude)
|
|
selected_geolocation.longitude = float(selected_geolocation.longitude)
|
|
|
|
return selected_geolocation
|
|
|
|
def _get_cities(self):
|
|
"""Retrieve a list of cities matching the requested location.
|
|
|
|
City names can be a single word (e.g. Seattle) or multiple words
|
|
(e.g. Kansas City). Query the database for all permutations of words
|
|
in the location passed in the request. For example, a request for
|
|
"Kansas City Missouri" will pass "Kansas" and "Kansas City" and
|
|
"Kansas City Missouri"
|
|
|
|
This logic assumes that it will not find a match when a city and
|
|
region/country are included in the request. For example, a request for
|
|
"Kansas City Missouri" should only find a match for "Kansas City".
|
|
"""
|
|
possible_city_names = []
|
|
geolocation_words = self.request_geolocation.split()
|
|
for index, _ in enumerate(geolocation_words):
|
|
possible_city_name = " ".join(geolocation_words[: index + 1])
|
|
possible_city_names.append(possible_city_name)
|
|
|
|
self.cities = self.city_repo.get_geographic_location_by_city(
|
|
possible_city_names
|
|
)
|
|
|
|
def _select_geolocation_from_cities(self):
|
|
"""Select one of the cities returned by the database.
|
|
|
|
If a single match is found, select it. If multiple matches are found,
|
|
return the city with the biggest population. If multiple matches are
|
|
found and a region or country is included in the requested location,
|
|
attempt to match based on the extra criteria.
|
|
"""
|
|
selected_geolocation = None
|
|
if len(self.cities) == 1:
|
|
selected_geolocation = self.cities[0]
|
|
elif len(self.cities) > 1:
|
|
biggest_city = self.cities[0]
|
|
if biggest_city.city.lower() == self.request_geolocation:
|
|
selected_geolocation = biggest_city
|
|
else:
|
|
city_in_region = self._get_city_for_requested_region()
|
|
city_in_country = self._get_city_for_requested_country()
|
|
selected_geolocation = city_in_region or city_in_country
|
|
|
|
return selected_geolocation
|
|
|
|
def _get_city_for_requested_region(self):
|
|
"""If a region is in the request, get the city in that region.
|
|
|
|
Example:
|
|
A request for "Kansas City Missouri" should return the city of
|
|
Kansas City in the state of Missouri
|
|
"""
|
|
city_in_requested_region = None
|
|
for city in self.cities:
|
|
location_without_city = self.request_geolocation[len(city.city) :]
|
|
if city.region.lower() in location_without_city.strip():
|
|
city_in_requested_region = city
|
|
break
|
|
|
|
return city_in_requested_region
|
|
|
|
def _get_city_for_requested_country(self):
|
|
"""If a country is in the request, get the city in that country.
|
|
|
|
Examples:
|
|
A request for "Sydney Australia" should return the city of Syndey
|
|
in the country of Australia.
|
|
"""
|
|
selected_city = None
|
|
for city in self.cities:
|
|
location_without_city = self.request_geolocation[len(city.city) :]
|
|
if city.country.lower() in location_without_city.strip():
|
|
selected_city = city
|
|
break
|
|
|
|
return selected_city
|