usse/funda-scraper/venv/lib/python3.10/site-packages/geopy/geocoders/smartystreets.py

145 lines
4.6 KiB
Python
Raw Normal View History

2023-02-20 22:38:24 +00:00
from functools import partial
from urllib.parse import urlencode
from geopy.adapters import AdapterHTTPError
from geopy.exc import GeocoderQuotaExceeded
from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder
from geopy.location import Location
from geopy.util import logger
__all__ = ("LiveAddress", )
class LiveAddress(Geocoder):
"""Geocoder using the LiveAddress API provided by SmartyStreets.
Documentation at:
https://smartystreets.com/docs/cloud/us-street-api
"""
geocode_path = '/street-address'
def __init__(
self,
auth_id,
auth_token,
*,
timeout=DEFAULT_SENTINEL,
proxies=DEFAULT_SENTINEL,
user_agent=None,
ssl_context=DEFAULT_SENTINEL,
adapter_factory=None
):
"""
:param str auth_id: Valid `Auth ID` from SmartyStreets.
:param str auth_token: Valid `Auth Token` from SmartyStreets.
:param int timeout:
See :attr:`geopy.geocoders.options.default_timeout`.
:param dict proxies:
See :attr:`geopy.geocoders.options.default_proxies`.
:param str user_agent:
See :attr:`geopy.geocoders.options.default_user_agent`.
:type ssl_context: :class:`ssl.SSLContext`
:param ssl_context:
See :attr:`geopy.geocoders.options.default_ssl_context`.
:param callable adapter_factory:
See :attr:`geopy.geocoders.options.default_adapter_factory`.
.. versionadded:: 2.0
"""
super().__init__(
scheme='https',
timeout=timeout,
proxies=proxies,
user_agent=user_agent,
ssl_context=ssl_context,
adapter_factory=adapter_factory,
)
self.auth_id = auth_id
self.auth_token = auth_token
domain = 'api.smartystreets.com'
self.api = '%s://%s%s' % (self.scheme, domain, self.geocode_path)
def geocode(
self,
query,
*,
exactly_one=True,
timeout=DEFAULT_SENTINEL,
candidates=1
):
"""
Return a location point by address.
:param str query: The address or query you wish to geocode.
:param bool exactly_one: Return one result or a list of results, if
available.
:param int timeout: Time, in seconds, to wait for the geocoding service
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
exception. Set this only if you wish to override, on this call
only, the value set during the geocoder's initialization.
:param int candidates: An integer between 1 and 10 indicating the max
number of candidate addresses to return if a valid address
could be found.
:rtype: ``None``, :class:`geopy.location.Location` or a list of them, if
``exactly_one=False``.
"""
if not (1 <= candidates <= 10):
raise ValueError('candidates must be between 1 and 10')
query = {
'auth-id': self.auth_id,
'auth-token': self.auth_token,
'street': query,
'candidates': candidates,
}
url = '{url}?{query}'.format(url=self.api, query=urlencode(query))
logger.debug("%s.geocode: %s", self.__class__.__name__, url)
callback = partial(self._parse_json, exactly_one=exactly_one)
return self._call_geocoder(url, callback, timeout=timeout)
def _geocoder_exception_handler(self, error):
search = "no active subscriptions found"
if isinstance(error, AdapterHTTPError):
if search in str(error).lower():
raise GeocoderQuotaExceeded(str(error)) from error
if search in (error.text or "").lower():
raise GeocoderQuotaExceeded(error.text) from error
def _parse_json(self, response, exactly_one=True):
"""
Parse responses as JSON objects.
"""
if not len(response):
return None
if exactly_one:
return self._format_structured_address(response[0])
else:
return [self._format_structured_address(c) for c in response]
def _format_structured_address(self, address):
"""
Pretty-print address and return lat, lon tuple.
"""
latitude = address['metadata'].get('latitude')
longitude = address['metadata'].get('longitude')
return Location(
", ".join((address['delivery_line_1'], address['last_line'])),
(latitude, longitude) if latitude and longitude else None,
address
)