Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ international_street_api:
us_autocomplete_pro_api:
PYTHONPATH=. python3 examples/us_autocomplete_pro_example.py

us_autocomplete_api:
PYTHONPATH=. python3 examples/us_autocomplete_example.py

us_enrichment_api:
PYTHONPATH=. python3 examples/us_enrichment_example.py && PYTHONPATH=. python3 examples/us_enrichment_etag_example.py && PYTHONPATH=. python3 examples/us_enrichment_business_example.py && PYTHONPATH=. python3 examples/us_enrichment_business_name_search_example.py && PYTHONPATH=. python3 examples/us_enrichment_generic_example.py && PYTHONPATH=. python3 examples/us_enrichment_geo_reference_example.py && PYTHONPATH=. python3 examples/us_enrichment_secondary_example.py && PYTHONPATH=. python3 examples/us_enrichment_secondary_count_example.py

Expand All @@ -57,7 +60,7 @@ us_street_match_strategy_api:
us_zipcode_api:
PYTHONPATH=. python3 examples/us_zipcode_single_lookup_example.py && PYTHONPATH=. python3 examples/us_zipcode_multiple_lookups_example.py

examples: international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_street_iana_timezone_api us_zipcode_api
examples: international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_autocomplete_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_street_iana_timezone_api us_zipcode_api


.PHONY: clean test dependencies package publish examples international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_street_iana_timezone_api us_street_match_strategy_api us_zipcode_api
.PHONY: clean test dependencies package publish examples international_autocomplete_api international_postal_code_api international_street_api us_autocomplete_pro_api us_autocomplete_api us_enrichment_api us_extract_api us_reverse_geo_api us_street_api us_street_iana_timezone_api us_street_match_strategy_api us_zipcode_api
79 changes: 79 additions & 0 deletions examples/us_autocomplete_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import os

from smartystreets_python_sdk import SharedCredentials, BasicAuthCredentials, ClientBuilder
from smartystreets_python_sdk.us_autocomplete import Lookup as AutocompleteLookup, geolocation_type
from smartystreets_python_sdk.us_autocomplete.source import Source

# This example is for us-autocomplete (V2). It has the same name as a previous product
# which has been deprecated since 2022 which we refer to as US Autocomplete Basic.
#
# If you are still using US Autocomplete Basic, this SDK will not work.

def run():
# key = "Your SmartyStreets Key here"
# hostname = "Your Hostname here"

# We recommend storing your secret keys in environment variables instead---it's safer!
# for client-side requests (browser/mobile), use this code:
# key = os.environ['SMARTY_AUTH_WEB']
# hostname = os.environ['SMARTY_WEBSITE_DOMAIN']
#
# credentials = SharedCredentials(key, hostname)

# for server-to-server requests, use this code:
auth_id = os.environ['SMARTY_AUTH_ID']
auth_token = os.environ['SMARTY_AUTH_TOKEN']

credentials = BasicAuthCredentials(auth_id, auth_token)

client = ClientBuilder(credentials).build_us_autocomplete_api_client()

lookup = AutocompleteLookup('1042 W Center')

# Uncomment the below line to add a custom parameter
# lookup.add_custom_parameter("parameter", "value")

client.send(lookup)

print('*** Result with no filter ***')
print()
for suggestion in lookup.result:
print(suggestion.street_line + " " + suggestion.city, suggestion.state, sep=", ")

# Documentation for input fields can be found at:
# https://www.smarty.com/docs/apis/us-autocomplete-v2/reference#http-request-input-fields

lookup.add_city_filter('Denver,Aurora,CO')
lookup.add_city_filter('Orem,UT')
lookup.add_state_preference('CO')
# lookup.selected = '1042 W Center St Apt A (24) Orem UT 84057'
lookup.max_results = 5
lookup.prefer_geolocation = geolocation_type.NONE
lookup.prefer_ratio = 33
lookup.source = Source.ALL

suggestions = client.send(lookup) # The client will also return the suggestions directly

print()
print('*** Result with some filters ***')
entry_id = ''
address_with_secondaries = ''
for suggestion in suggestions:
print(suggestion.street_line + " " + suggestion.city + ", " + suggestion.state)
if suggestion.entry_id:
address_with_secondaries = suggestion.street_line + " " + suggestion.city + " " + suggestion.state
entry_id = suggestion.entry_id

# Expand the secondaries of a result that has an entry_id by passing it back as the selected address.
if entry_id:
lookup.selected = entry_id
suggestions = client.send(lookup)

print()
print('*** Secondaries for: [' + address_with_secondaries + '] ***')
for suggestion in suggestions:
print(suggestion.street_line + " " + suggestion.city + ", " + suggestion.state)


if __name__ == "__main__":
run()
6 changes: 6 additions & 0 deletions smartystreets_python_sdk/client_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from smartystreets_python_sdk.us_zipcode import Client as USZIPClient
from smartystreets_python_sdk.us_extract import Client as USExtractClient
from smartystreets_python_sdk.us_autocomplete_pro import Client as USAutocompleteProClient
from smartystreets_python_sdk.us_autocomplete import Client as USAutocompleteClient
from smartystreets_python_sdk.us_reverse_geo import Client as USReverseGeoClient
from smartystreets_python_sdk.international_street import Client as InternationalStreetClient
from smartystreets_python_sdk.international_autocomplete import Client as InternationalAutocompleteClient
Expand Down Expand Up @@ -34,6 +35,7 @@ def __init__(self, signer):
self.INTERNATIONAL_STREET_API_URL = "https://international-street.api.smarty.com/verify"
self.INTERNATIONAL_AUTOCOMPLETE_API_URL = "https://international-autocomplete.api.smarty.com/v2/lookup"
self.US_AUTOCOMPLETE_PRO_API_URL = "https://us-autocomplete-pro.api.smarty.com/lookup"
self.US_AUTOCOMPLETE_API_URL = "https://us-autocomplete.api.smarty.com/v2/lookup"
self.US_EXTRACT_API_URL = "https://us-extract.api.smarty.com"
self.US_STREET_API_URL = "https://us-street.api.smarty.com/street-address"
self.US_ZIP_CODE_API_URL = "https://us-zipcode.api.smarty.com/lookup"
Expand Down Expand Up @@ -229,6 +231,10 @@ def build_us_autocomplete_pro_api_client(self):
self.ensure_url_prefix_not_null(self.US_AUTOCOMPLETE_PRO_API_URL)
return USAutocompleteProClient(self.build_sender(), self.serializer)

def build_us_autocomplete_api_client(self):
self.ensure_url_prefix_not_null(self.US_AUTOCOMPLETE_API_URL)
return USAutocompleteClient(self.build_sender(), self.serializer)

def build_us_extract_api_client(self):
self.ensure_url_prefix_not_null(self.US_EXTRACT_API_URL)
return USExtractClient(self.build_sender(), self.serializer)
Expand Down
4 changes: 4 additions & 0 deletions smartystreets_python_sdk/us_autocomplete/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .suggestion import Suggestion
from .lookup import Lookup
from .client import Client
from .source import Source
70 changes: 70 additions & 0 deletions smartystreets_python_sdk/us_autocomplete/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from enum import Enum

from smartystreets_python_sdk import Request
from smartystreets_python_sdk.exceptions import SmartyException
from smartystreets_python_sdk.us_autocomplete import Suggestion, geolocation_type


class Client:
def __init__(self, sender, serializer):
"""
It is recommended to instantiate this class using ClientBuilder.build_us_autocomplete_api_client()
"""
self.sender = sender
self.serializer = serializer

def send(self, lookup):
"""
Sends a Lookup object to the US Autocomplete API and stores the result in the Lookup's result field.
"""
if not lookup or not lookup.search:
raise SmartyException('Send() must be passed a Lookup with the search field set.')

request = self.build_request(lookup)

response = self.sender.send(request)

if response.error:
raise response.error

result = self.serializer.deserialize(response.payload)
suggestions = self.convert_suggestions(result.get('suggestions') or [])
lookup.result = suggestions

return suggestions

def build_request(self, lookup):
request = Request()

self.add_parameter(request, 'search', lookup.search)
self.add_parameter(request, 'max_results', lookup.max_results)
self.add_parameter(request, 'include_only_cities', self.build_filter_string(lookup.city_filter))
self.add_parameter(request, 'include_only_states', self.build_filter_string(lookup.state_filter))
self.add_parameter(request, 'include_only_zip_codes', self.build_filter_string(lookup.zip_filter))
self.add_parameter(request, 'exclude_states', self.build_filter_string(lookup.exclude_states))
self.add_parameter(request, 'prefer_cities', self.build_filter_string(lookup.prefer_cities))
self.add_parameter(request, 'prefer_states', self.build_filter_string(lookup.prefer_states))
self.add_parameter(request, 'prefer_zip_codes', self.build_filter_string(lookup.prefer_zips))
self.add_parameter(request, 'prefer_ratio', lookup.prefer_ratio)
self.add_parameter(request, 'prefer_geolocation', lookup.prefer_geolocation)
self.add_parameter(request, 'source', lookup.source)
self.add_parameter(request, 'selected', lookup.selected)
self.add_parameter(request, 'exclude', self.build_filter_string(lookup.exclude, ','))

for parameter in lookup.custom_parameter_array:
self.add_parameter(request, parameter, lookup.custom_parameter_array[parameter])

return request

@staticmethod
def build_filter_string(filter_list, separator=';'):
return separator.join(filter_list or []) or None

@staticmethod
def convert_suggestions(suggestion_dictionaries):
return [Suggestion(suggestion) for suggestion in suggestion_dictionaries]

@staticmethod
def add_parameter(request, key, value):
if value and value != 'none':
request.parameters[key] = value.value if isinstance(value, Enum) else value
3 changes: 3 additions & 0 deletions smartystreets_python_sdk/us_autocomplete/geolocation_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CITY = 'city'

NONE = 'none'
83 changes: 83 additions & 0 deletions smartystreets_python_sdk/us_autocomplete/lookup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from smartystreets_python_sdk.us_autocomplete import geolocation_type


class Lookup:
def __init__(self, search=None, max_results=None, city_filter=None, state_filter=None, zip_filter=None,
exclude_states=None, prefer_cities=None, prefer_states=None, prefer_zips=None, prefer_ratio=None,
prefer_geolocation=None, selected=None, exclude=None, source=None):

"""
In addition to holding all of the input data for this lookup, this class also will contain the result
of the lookup after it comes back from the API.

See "https://www.smarty.com/docs/apis/us-autocomplete-v2/reference#http-request-input-fields"

:param search: The part of the address that has already been typed (required)
:param max_results: Maximum number of address suggestions to return
:param city_filter: Limit the results to only those cities listed, as well as those in state_filter
:param state_filter: Limit the results to only those states listed, as well as those in city_filter
:param zip_filter: Limit the result to only those ZIP Codes listed. When this parameter is used,
no other _cities, _states parameters can be used
:param exclude_states: Exclude the following states from the results. When this parameter is used,
no other include_ parameters can be used
:param prefer_cities: Display suggestions with the listed cities and states at the top of the suggestion list
:param prefer_states: Display suggestions with the listed states at the top of the suggestion list
:param prefer_zips: Display suggestions with the listed ZIP Codes at the top of the suggestion list.
When this parameter is used, no other _cities or _state parameters can be used
:param prefer_ratio: Specifies the percentage of address suggestions that should be preferred
and will appear at the top of the suggestion list
:param prefer_geolocation: If omitted or set to city it uses the sender's IP address to determine location,
then automatically adds the city and state to the prefer_cities value.
This parameter takes precedence over other _include or exclude parameters,
meaning that if it is not set to none, you may see addresses from the customer's area
when you may not desire it
:param selected: Used by UI components to request a list of secondaries (up to 100) for the specified address
:param exclude: Removes the given partial address from the suggestion results
:param source: Include results from alternate data sources. Defaults to None (API default: postal).
Use Source.ALL to include non-postal addresses, Source.POSTAL to limit to postal only.
"""
self.result = []
self.custom_parameter_array = {}
self.search = search
self.max_results = max_results
self.city_filter = city_filter or []
self.state_filter = state_filter or []
self.zip_filter = zip_filter or []
self.exclude_states = exclude_states or []
self.prefer_cities = prefer_cities or []
self.prefer_states = prefer_states or []
self.prefer_zips = prefer_zips or []
self.prefer_ratio = prefer_ratio
self.prefer_geolocation = prefer_geolocation
self.selected = selected
self.exclude = exclude or []
self.source = source

def add_city_filter(self, city):
self.city_filter.append(city)

def add_state_filter(self, state):
self.state_filter.append(state)

def add_zip_filter(self, zipcode):
self.prefer_geolocation = geolocation_type.NONE
self.zip_filter.append(zipcode)

def add_state_exclusion(self, state):
self.exclude_states.append(state)

def add_city_preference(self, city):
self.prefer_cities.append(city)

def add_state_preference(self, state):
self.prefer_states.append(state)

def add_zip_preference(self, zipcode):
self.prefer_geolocation = geolocation_type.NONE
self.prefer_zips.append(zipcode)

def add_exclude(self, exclude_type):
self.exclude.append(exclude_type)

def add_custom_parameter(self, parameter, value):
self.custom_parameter_array[parameter] = value
6 changes: 6 additions & 0 deletions smartystreets_python_sdk/us_autocomplete/source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum


class Source(str, Enum):
ALL = "all"
POSTAL = "postal"
14 changes: 14 additions & 0 deletions smartystreets_python_sdk/us_autocomplete/suggestion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Suggestion:
def __init__(self, obj=None):
"""
See "https://www.smarty.com/docs/apis/us-autocomplete-v2/reference#http-response-status"
"""
self.smarty_key = obj.get('smarty_key', None)
self.entry_id = obj.get('entry_id', None)
self.street_line = obj.get('street_line', None)
self.secondary = obj.get('secondary', None)
self.city = obj.get('city', None)
self.state = obj.get('state', None)
self.zipcode = obj.get('zipcode', None)
self.entries = obj.get('entries', None)
Comment thread
mae-smarty marked this conversation as resolved.
self.source = obj.get('source', None)
Empty file.
Loading