832 lines
27 KiB
Python
832 lines
27 KiB
Python
"""
|
|
|
|
AUTHOR:
|
|
|
|
Khushal P Soonderji
|
|
|
|
DATE:
|
|
|
|
Friday, 27th Jun., 2025
|
|
|
|
OBJECTIVE:
|
|
|
|
To get location and business info from Google's Places API.
|
|
|
|
REFERENCES:
|
|
|
|
1. GMail Quickstart: https://developers.google.com/gmail/api/quickstart/python
|
|
2. GMail Labels: https://developers.google.com/gmail/api/guides/labels
|
|
3. GMail Messages: https://developers.google.com/gmail/api/reference/rest/v1/users.messages
|
|
4. People Profile: https://developers.google.com/people/api/rest/v1/people/get
|
|
|
|
DOWNLOADS:
|
|
|
|
N/A
|
|
|
|
"""
|
|
|
|
|
|
# *****************************************************************************************************************
|
|
# ***** ****
|
|
# *** IMPORT ***
|
|
# ***** ****
|
|
# *****************************************************************************************************************
|
|
|
|
|
|
# To make sibling directories accessible for imports:
|
|
import sys
|
|
sys.path.append(".")
|
|
sys.path.append("..")
|
|
|
|
# System-level activities:
|
|
import io
|
|
|
|
# My utils:
|
|
from utils_v2.string import json
|
|
|
|
# My Google utils:
|
|
from utils_v2.goog.controllers.base import AsyncGoogleBase
|
|
from utils_v2.goog.models.auth_tokens import GoogleAuthTokens
|
|
from utils_v2.goog.models.api_call import GoogleApiResponse
|
|
|
|
# Related to Google:
|
|
from google.auth.transport.requests import Request
|
|
from google.oauth2.credentials import Credentials
|
|
from googleapiclient.discovery import build
|
|
|
|
# To make API calls:
|
|
import httpx
|
|
|
|
# For asynchronous activities:
|
|
import asyncio
|
|
|
|
# To work with date and time:
|
|
import datetime
|
|
|
|
# For working with datatypes:
|
|
from typing import Dict, Literal, List, Any
|
|
|
|
# For debugging:
|
|
from icecream import IceCreamDebugger
|
|
import inspect
|
|
|
|
# For computational help:
|
|
import math
|
|
|
|
|
|
# *****************************************************************************************************************
|
|
# ***** ****
|
|
# *** MACROS / ONE-TIME INIT ***
|
|
# ***** ****
|
|
# *****************************************************************************************************************
|
|
|
|
|
|
# Google Scopes:
|
|
SCOPES_PLACES_FULL = [
|
|
r"https://www.googleapis.com/auth/cloud-platform",
|
|
r"https://www.googleapis.com/auth/userinfo.profile",
|
|
r"https://www.googleapis.com/auth/gmail.metadata"
|
|
]
|
|
|
|
|
|
# *****************************************************************************************************************
|
|
# ***** ****
|
|
# *** VARIABLES ***
|
|
# ***** ****
|
|
# *****************************************************************************************************************
|
|
|
|
|
|
# --- Nothing Yet
|
|
|
|
|
|
# *****************************************************************************************************************
|
|
# ***** ****
|
|
# *** FUNCTIONS ***
|
|
# ***** ****
|
|
# *****************************************************************************************************************
|
|
|
|
|
|
# --- Nothing Yet
|
|
|
|
|
|
# *****************************************************************************************************************
|
|
# ***** ****
|
|
# *** CLASSES ***
|
|
# ***** ****
|
|
# *****************************************************************************************************************
|
|
|
|
|
|
class AsyncPlacesClient(AsyncGoogleBase):
|
|
|
|
# The types of places:
|
|
PLACE_TYPES = {
|
|
"Automotive": [
|
|
"car_dealer",
|
|
"car_rental",
|
|
"car_repair",
|
|
"car_wash",
|
|
"electric_vehicle_charging_station",
|
|
"gas_station",
|
|
"parking",
|
|
"rest_stop"
|
|
],
|
|
"Business": [
|
|
"corporate_office",
|
|
"farm",
|
|
"ranch"
|
|
],
|
|
"Culture": [
|
|
"art_gallery",
|
|
"art_studio",
|
|
"auditorium",
|
|
"cultural_landmark",
|
|
"historical_place",
|
|
"monument",
|
|
"museum",
|
|
"performing_arts_theater",
|
|
"sculpture"
|
|
],
|
|
"Education": [
|
|
"library",
|
|
"preschool",
|
|
"primary_school",
|
|
"secondary_school",
|
|
"university"
|
|
],
|
|
"Entertainment and Recreation": [
|
|
"adventure_sports_center",
|
|
"amphitheatre",
|
|
"amusement_center",
|
|
"amusement_park",
|
|
"aquarium",
|
|
"banquet_hall",
|
|
"barbecue_area",
|
|
"botanical_garden",
|
|
"bowling_alley",
|
|
"casino",
|
|
"childrens_camp",
|
|
"comedy_club",
|
|
"community_center",
|
|
"concert_hall",
|
|
"convention_center",
|
|
"cultural_center",
|
|
"cycling_park",
|
|
"dance_hall",
|
|
"dog_park",
|
|
"event_venue",
|
|
"ferris_wheel",
|
|
"garden",
|
|
"hiking_area",
|
|
"historical_landmark",
|
|
"internet_cafe",
|
|
"karaoke",
|
|
"marina",
|
|
"movie_rental",
|
|
"movie_theater",
|
|
"national_park",
|
|
"night_club",
|
|
"observation_deck",
|
|
"off_roading_area",
|
|
"opera_house",
|
|
"park",
|
|
"philharmonic_hall",
|
|
"picnic_ground",
|
|
"planetarium",
|
|
"plaza",
|
|
"roller_coaster",
|
|
"skateboard_park",
|
|
"state_park",
|
|
"tourist_attraction",
|
|
"video_arcade",
|
|
"visitor_center",
|
|
"water_park",
|
|
"wedding_venue",
|
|
"wildlife_park",
|
|
"wildlife_refuge",
|
|
"zoo"
|
|
],
|
|
"Facilities": [
|
|
"public_bath",
|
|
"public_bathroom",
|
|
"stable"
|
|
],
|
|
"Finance": [
|
|
"accounting",
|
|
"atm",
|
|
"bank"
|
|
],
|
|
"Food and Drink": [
|
|
"acai_shop",
|
|
"afghani_restaurant",
|
|
"african_restaurant",
|
|
"american_restaurant",
|
|
"asian_restaurant",
|
|
"bagel_shop",
|
|
"bakery",
|
|
"bar",
|
|
"bar_and_grill",
|
|
"barbecue_restaurant",
|
|
"brazilian_restaurant",
|
|
"breakfast_restaurant",
|
|
"brunch_restaurant",
|
|
"buffet_restaurant",
|
|
"cafe",
|
|
"cafeteria",
|
|
"candy_store",
|
|
"cat_cafe",
|
|
"chinese_restaurant",
|
|
"chocolate_factory",
|
|
"chocolate_shop",
|
|
"coffee_shop",
|
|
"confectionery",
|
|
"deli",
|
|
"dessert_restaurant",
|
|
"dessert_shop",
|
|
"diner",
|
|
"dog_cafe",
|
|
"donut_shop",
|
|
"fast_food_restaurant",
|
|
"fine_dining_restaurant",
|
|
"food_court",
|
|
"french_restaurant",
|
|
"greek_restaurant",
|
|
"hamburger_restaurant",
|
|
"ice_cream_shop",
|
|
"indian_restaurant",
|
|
"indonesian_restaurant",
|
|
"italian_restaurant",
|
|
"japanese_restaurant",
|
|
"juice_shop",
|
|
"korean_restaurant",
|
|
"lebanese_restaurant",
|
|
"meal_delivery",
|
|
"meal_takeaway",
|
|
"mediterranean_restaurant",
|
|
"mexican_restaurant",
|
|
"middle_eastern_restaurant",
|
|
"pizza_restaurant",
|
|
"pub",
|
|
"ramen_restaurant",
|
|
"restaurant",
|
|
"sandwich_shop",
|
|
"seafood_restaurant",
|
|
"spanish_restaurant",
|
|
"steak_house",
|
|
"sushi_restaurant",
|
|
"tea_house",
|
|
"thai_restaurant",
|
|
"turkish_restaurant",
|
|
"vegan_restaurant",
|
|
"vegetarian_restaurant",
|
|
"vietnamese_restaurant",
|
|
"wine_bar"
|
|
],
|
|
"Geographical Areas": [
|
|
"administrative_area_level_1",
|
|
"administrative_area_level_2",
|
|
"country",
|
|
"locality",
|
|
"postal_code",
|
|
"school_district"
|
|
],
|
|
"Government": [
|
|
"city_hall",
|
|
"courthouse",
|
|
"embassy",
|
|
"fire_station",
|
|
"government_office",
|
|
"police",
|
|
"post_office"
|
|
],
|
|
"Health and Wellness": [
|
|
"chiropractor",
|
|
"dental_clinic",
|
|
"dentist",
|
|
"doctor",
|
|
"drugstore",
|
|
"hospital",
|
|
"massage",
|
|
"medical_lab",
|
|
"pharmacy",
|
|
"physiotherapist",
|
|
"sauna",
|
|
"skin_care_clinic",
|
|
"spa",
|
|
"tanning_studio",
|
|
"wellness_center",
|
|
"yoga_studio"
|
|
],
|
|
"Housing": [
|
|
"apartment_building",
|
|
"apartment_complex",
|
|
"condominium_complex",
|
|
"housing_complex"
|
|
],
|
|
"Lodging": [
|
|
"bed_and_breakfast",
|
|
"budget_japanese_inn",
|
|
"campground",
|
|
"camping_cabin",
|
|
"cottage",
|
|
"extended_stay_hotel",
|
|
"farmstay",
|
|
"guest_house",
|
|
"hostel",
|
|
"hotel",
|
|
"inn",
|
|
"japanese_inn",
|
|
"lodging",
|
|
"mobile_home_park",
|
|
"motel",
|
|
"private_guest_room",
|
|
"resort_hotel",
|
|
"rv_park"
|
|
],
|
|
"Natural Features": [
|
|
"beach"
|
|
],
|
|
"Places of Worship": [
|
|
"church",
|
|
"hindu_temple",
|
|
"mosque",
|
|
"synagogue"
|
|
],
|
|
"Services": [
|
|
"astrologer",
|
|
"barber_shop",
|
|
"beautician",
|
|
"beauty_salon",
|
|
"body_art_service",
|
|
"catering_service",
|
|
"cemetery",
|
|
"child_care_agency",
|
|
"consultant",
|
|
"courier_service",
|
|
"electrician",
|
|
"florist",
|
|
"food_delivery",
|
|
"foot_care",
|
|
"funeral_home",
|
|
"hair_care",
|
|
"hair_salon",
|
|
"insurance_agency",
|
|
"laundry",
|
|
"lawyer",
|
|
"locksmith",
|
|
"makeup_artist",
|
|
"moving_company",
|
|
"nail_salon",
|
|
"painter",
|
|
"plumber",
|
|
"psychic",
|
|
"real_estate_agency",
|
|
"roofing_contractor",
|
|
"storage",
|
|
"summer_camp_organizer",
|
|
"tailor",
|
|
"telecommunications_service_provider",
|
|
"tour_agency",
|
|
"tourist_information_center",
|
|
"travel_agency",
|
|
"veterinary_care"
|
|
],
|
|
"Shopping": [
|
|
"asian_grocery_store",
|
|
"auto_parts_store",
|
|
"bicycle_store",
|
|
"book_store",
|
|
"butcher_shop",
|
|
"cell_phone_store",
|
|
"clothing_store",
|
|
"convenience_store",
|
|
"department_store",
|
|
"discount_store",
|
|
"electronics_store",
|
|
"food_store",
|
|
"furniture_store",
|
|
"gift_shop",
|
|
"grocery_store",
|
|
"hardware_store",
|
|
"home_improvement_store",
|
|
"jewelry_store",
|
|
"market",
|
|
"pet_store",
|
|
"shoe_store",
|
|
"shopping_mall",
|
|
"sporting_goods_store",
|
|
"store",
|
|
"supermarket",
|
|
"warehouse_store",
|
|
"wholesaler"
|
|
],
|
|
"Sports": [
|
|
"arena",
|
|
"athletic_field",
|
|
"fishing_charter",
|
|
"fishing_pond",
|
|
"fitness_center",
|
|
"golf_course",
|
|
"gym",
|
|
"ice_skating_rink",
|
|
"playground",
|
|
"ski_resort",
|
|
"sports_activity_location",
|
|
"sports_club",
|
|
"sports_coaching",
|
|
"sports_complex",
|
|
"stadium",
|
|
"swimming_pool"
|
|
],
|
|
"Transportation": [
|
|
"airport",
|
|
"airstrip",
|
|
"bus_station",
|
|
"bus_stop",
|
|
"ferry_terminal",
|
|
"heliport",
|
|
"international_airport",
|
|
"light_rail_station",
|
|
"park_and_ride",
|
|
"subway_station",
|
|
"taxi_stand",
|
|
"train_station",
|
|
"transit_depot",
|
|
"transit_station",
|
|
"truck_stop"
|
|
]
|
|
}
|
|
|
|
# Kinds of fields:
|
|
FIELD_MASK_BASIC_INFO = [
|
|
"nextPageToken",
|
|
"places.id",
|
|
"places.name",
|
|
"places.displayName",
|
|
"places.formattedAddress",
|
|
"places.location",
|
|
"places.googleMapsUri",
|
|
"places.photos",
|
|
"places.primaryType",
|
|
"places.types"
|
|
]
|
|
FIELD_MASK_BUSINESS_LEADS = [
|
|
"nextPageToken",
|
|
"places.id",
|
|
"places.name",
|
|
"places.displayName",
|
|
"places.businessStatus",
|
|
"places.formattedAddress",
|
|
"places.postalAddress",
|
|
"places.location"
|
|
"places.googleMapsUri",
|
|
"places.photos",
|
|
"places.primaryType",
|
|
"places.types",
|
|
"places.regularOpeningHours",
|
|
"places.regularSecondaryOpeningHours",
|
|
"places.rating",
|
|
"places.userRatingCount",
|
|
"places.websiteUri",
|
|
"places.internationalPhoneNumber",
|
|
"places.priceLevel",
|
|
"places.priceRange"
|
|
]
|
|
|
|
# ┳┳ ┏┓ ┏•┓
|
|
# ┃┃┏┏┓┏┓ ┃┃┏┓┏┓╋┓┃┏┓
|
|
# ┗┛┛┗ ┛ ┣┛┛ ┗┛┛┗┗┗
|
|
|
|
async def get_user_profile(
|
|
self,
|
|
tokens: GoogleAuthTokens,
|
|
user_id: str = "me",
|
|
) -> GoogleApiResponse:
|
|
|
|
"""
|
|
To get the details of the user that has logged in.
|
|
DOCUMENTATION:
|
|
1. https://developers.google.com/gmail/api/reference/rest/v1/users/getProfile
|
|
2. https://developers.google.com/people/api/rest/v1/people/get
|
|
3. https://developers.google.com/people/api/rest/v1/people#Person
|
|
:param tokens: The object that holds the access token to the service.
|
|
:param user_id: The id of the user for which this service must be run. Typically, the e-mail id itself or "me".
|
|
:return: A structured response where the list of labels will be in the 'data' variable.
|
|
"""
|
|
|
|
# Ensure that the tokens are valid:
|
|
await tokens.arefresh(
|
|
http_client=self._http_client,
|
|
client_id=self._client_id,
|
|
client_secret=self._client_secret,
|
|
force_refresh=False
|
|
)
|
|
|
|
# Make the GMail API call:
|
|
if not self._debug_only_errors: self._printer("Getting User Profile.")
|
|
gmail_api_response = await self.get(
|
|
url=f"https://gmail.googleapis.com/gmail/v1/users/{user_id}/profile",
|
|
headers={"Authorization": f"Bearer {tokens.accessToken}"}
|
|
)
|
|
|
|
# If the call was successful:
|
|
if gmail_api_response.httpCode in [200]:
|
|
gmail_api_response.success = True
|
|
gmail_api_response.data = await gmail_api_response.get_json()
|
|
gmail_api_response.data["displayName"] = None
|
|
gmail_api_response.data["displayPictureUrl"] = None
|
|
|
|
# If the call failed:
|
|
else:
|
|
self._printer(
|
|
gmail_api_response.action,
|
|
gmail_api_response.method,
|
|
gmail_api_response.httpCode,
|
|
)
|
|
return gmail_api_response
|
|
|
|
# Make the People API call:
|
|
if not self._debug_only_errors: self._printer("Getting User Profile.")
|
|
people_api_response = await self.get(
|
|
url=f"https://people.googleapis.com/v1/people/me?personFields=names,photos,birthdays,phoneNumbers,genders,emailAddresses,addresses",
|
|
headers={"Authorization": f"Bearer {tokens.accessToken}"}
|
|
)
|
|
|
|
# If the call was successful:
|
|
if people_api_response.httpCode in [200]:
|
|
people_api_response.success = True
|
|
people_api_response.data = await people_api_response.get_json()
|
|
for item in people_api_response.data.get("names", []):
|
|
if item["metadata"]["primary"]:
|
|
gmail_api_response.data["displayName"] = item.get("displayName")
|
|
for item in people_api_response.data.get("photos", []):
|
|
if item["metadata"]["primary"]:
|
|
gmail_api_response.data["displayPictureUrl"] = item.get("url")
|
|
|
|
# If the call failed:
|
|
else:
|
|
self._printer(
|
|
people_api_response.action,
|
|
people_api_response.method,
|
|
people_api_response.httpCode,
|
|
)
|
|
|
|
# Done here:
|
|
return gmail_api_response
|
|
|
|
# ┏┓ •
|
|
# ┃┃┏┓┏┓┏┓┏┓┏┓╋┓┏┓┏
|
|
# ┣┛┛ ┗┛┣┛┗ ┛ ┗┗┗ ┛
|
|
# ┛
|
|
|
|
@property
|
|
def primary_types(self):
|
|
return list(self.PLACE_TYPES.keys())
|
|
|
|
def types_for(self, primary_type: str):
|
|
return self.PLACE_TYPES.get(primary_type)
|
|
|
|
# async def _nearby_radius_search(
|
|
# self,
|
|
# tokens: GoogleAuthTokens,
|
|
# latitude: float,
|
|
# longitude: float,
|
|
# radius: float,
|
|
# max_count: int = 100,
|
|
# next_page_token: str = None,
|
|
# ) -> GoogleApiResponse:
|
|
|
|
async def nearby_radius_search(
|
|
self,
|
|
tokens: GoogleAuthTokens,
|
|
latitude: float,
|
|
longitude: float,
|
|
radius: float,
|
|
included_primary_types: List[str] = None,
|
|
included_types: List[str] = None,
|
|
excluded_primary_types: List[str] = None,
|
|
excluded_types: List[str] = None,
|
|
field_mask: List[str] = None,
|
|
max_count: int = 100,
|
|
next_page_token: str = None,
|
|
) -> GoogleApiResponse:
|
|
|
|
"""
|
|
https://developers.google.com/maps/documentation/places/web-service/nearby-search
|
|
"""
|
|
|
|
# Ensure that the tokens are valid:
|
|
await tokens.arefresh(
|
|
http_client = self._http_client,
|
|
client_id = self._client_id,
|
|
client_secret = self._client_secret,
|
|
force_refresh = False
|
|
)
|
|
|
|
# Create the headers:
|
|
request_headers = {"Authorization": f"Bearer {tokens.accessToken}"}
|
|
if field_mask: request_headers["X-Goog-FieldMask"] = ",".join(field_mask)
|
|
|
|
# Start creating the JSON payload based on the inputs:
|
|
request_json = {
|
|
"maxResultCount": max_count,
|
|
"locationRestriction": {
|
|
"circle": {
|
|
"center": {
|
|
"latitude": latitude,
|
|
"longitude": longitude
|
|
},
|
|
"radius": radius
|
|
}
|
|
}
|
|
}
|
|
if included_primary_types: request_json["includedPrimaryTypes"] = included_primary_types
|
|
if included_types: request_json["includedTypes"] = included_types
|
|
if excluded_primary_types: request_json["excludedPrimaryTypes"] = excluded_primary_types
|
|
if excluded_types: request_json["excludedTypes"] = excluded_types
|
|
|
|
# Make the API call:
|
|
if not self._debug_only_errors: self._printer("Listing Nearby (Radius) Places.")
|
|
api_response = await self.post(
|
|
url = f"https://places.googleapis.com/v1/places:searchNearby",
|
|
headers = request_headers,
|
|
json = request_json
|
|
)
|
|
|
|
# If the call was successful:
|
|
if api_response.httpCode in [200]:
|
|
api_response.success = True
|
|
api_response.data = await api_response.get_json()
|
|
|
|
# Done here:
|
|
return api_response
|
|
|
|
|
|
async def text_query_search(
|
|
self,
|
|
tokens: GoogleAuthTokens,
|
|
text_query: str,
|
|
included_primary_types: List[str] = None,
|
|
included_types: List[str] = None,
|
|
excluded_primary_types: List[str] = None,
|
|
excluded_types: List[str] = None,
|
|
field_mask: List[str] = None,
|
|
max_count: int = 100,
|
|
next_page_token: str = None,
|
|
) -> GoogleApiResponse:
|
|
|
|
"""
|
|
https://developers.google.com/maps/documentation/places/web-service/text-search
|
|
"""
|
|
|
|
print("IN TOKEN", tokens)
|
|
print("IN TOKEN", tokens.model_dump())
|
|
print("IN TOKEN", tokens.token["accessToken"])
|
|
|
|
# Ensure that the tokens are valid:
|
|
# await tokens.arefresh(
|
|
# http_client=self._http_client,
|
|
# client_id=self._client_id,
|
|
# client_secret=self._client_secret,
|
|
# force_refresh=False
|
|
# )
|
|
print("RH --")
|
|
# Create the headers:
|
|
request_headers = {"Authorization": f"Bearer {tokens.token['accessToken']}"}
|
|
# if field_mask: request_headers["X-Goog-FieldMask"] = ",".join(field_mask)
|
|
print("RH",request_headers)
|
|
if field_mask: request_headers["X-Goog-FieldMask"] = "places.displayName,places.formattedAddress,places.priceLevel"
|
|
|
|
# Start creating the JSON payload based on the inputs:
|
|
# request_json = {
|
|
# "maxResultCount": max_count,
|
|
# "locationRestriction": {
|
|
# "circle": {
|
|
# "center": {
|
|
# "latitude": latitude,
|
|
# "longitude": longitude
|
|
# },
|
|
# "radius": radius
|
|
# }
|
|
# }
|
|
# }
|
|
|
|
# Start creating the JSON payload based on the inputs:
|
|
request_json = {
|
|
"maxResultCount": max_count,
|
|
"textQuery": text_query
|
|
}
|
|
|
|
# if included_primary_types: request_json["includedPrimaryTypes"] = included_primary_types
|
|
# if included_types: request_json["includedTypes"] = included_types
|
|
# if excluded_primary_types: request_json["excludedPrimaryTypes"] = excluded_primary_types
|
|
# if excluded_types: request_json["excludedTypes"] = excluded_types
|
|
print(request_json)
|
|
# Make the API call:
|
|
if not self._debug_only_errors: self._printer("Listing text Search Places.")
|
|
api_response = await self.post(
|
|
url=f"https://places.googleapis.com/v1/places:searchText",
|
|
headers=request_headers,
|
|
json=request_json
|
|
)
|
|
|
|
print(api_response)
|
|
|
|
# If the call was successful:
|
|
if api_response.httpCode in [200]:
|
|
api_response.success = True
|
|
api_response.data = await api_response.get_json()
|
|
|
|
# Done here:
|
|
return api_response
|
|
|
|
|
|
# *****************************************************************************************************************
|
|
# ***** ****
|
|
# *** MAIN PROGRAM ***
|
|
# ***** ****
|
|
# *****************************************************************************************************************
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# Create an HTTP client:
|
|
test_client = httpx.AsyncClient(
|
|
limits = httpx.Limits(
|
|
max_connections = 100, # ............ Maximum number of connections allowed in the pool.
|
|
max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive.
|
|
),
|
|
timeout = httpx.Timeout(
|
|
connect = 2.5, # ... Shorter connection timeout.
|
|
read = 2.5, # ...... Like what EasyEcom gives.
|
|
write = 10.0, # .... Time to wait for sending data.
|
|
pool = 120.0 # ..... Time to wait for a free connection from the pool.
|
|
)
|
|
)
|
|
|
|
# Read the secrets that give you access to the app:
|
|
# secrets_file = r"C:\Users\Khushal P Soonderji\Downloads\google_places_test_secret.json"
|
|
secrets_file = r"/home/python-dev-debug/Downloads/client_secret_349360248417-uuba8eudk75jg1jag212g5obhc1uostk.apps.googleusercontent.com.json"
|
|
secrets_dict = json.from_file(secrets_file)
|
|
|
|
async def main():
|
|
|
|
# Create an instance of the client:
|
|
my_goog = AsyncPlacesClient(
|
|
service_name = "places",
|
|
oauth_json = secrets_dict,
|
|
http_client = test_client,
|
|
redirect_url = r"https://wtt.ditscentre.in/shopify/test/1",
|
|
debug = True,
|
|
debug_prefix = "Places (M) | ",
|
|
debug_only_errors = False
|
|
)
|
|
|
|
# Request Auth:
|
|
print("AUTH URL:", await my_goog.get_authorization_url(
|
|
scopes = SCOPES_PLACES_FULL,
|
|
state = "Bhopli",
|
|
approval_prompt = "force"
|
|
))
|
|
|
|
# Get tokens from callback:
|
|
test_tokens = await my_goog.get_authorization_tokens(
|
|
scopes = SCOPES_PLACES_FULL,
|
|
redirect_url = input("Paste the redirect URL here: ")
|
|
)
|
|
print("TOKENS:", test_tokens)
|
|
|
|
# List out the primary and secondary types:
|
|
print("PRIMARY TYPES:", my_goog.primary_types)
|
|
print("'Food and Drink' TYPES:", my_goog.types_for("Food and Drink"))
|
|
print("'Alien Spaceship' TYPES:", my_goog.types_for("Alien Spaceship"))
|
|
|
|
response = await my_goog.get_user_profile(
|
|
tokens=test_tokens
|
|
)
|
|
|
|
print("SUCCESS:", response.success)
|
|
print("SUMMARY:", response.to_markdown())
|
|
print("\n\n---\n\n")
|
|
print("DATA:", json.to_string(response.data, default=str))
|
|
if not response.success:
|
|
print("\n\n---\n\n")
|
|
|
|
# Test some feature:
|
|
response = await my_goog.nearby_radius_search(
|
|
tokens = test_tokens,
|
|
latitude = 19.03145183334671,
|
|
longitude = 72.85437426861148,
|
|
radius = 1_000.0,
|
|
field_mask = None,
|
|
max_count = 5,
|
|
)
|
|
print("SUCCESS:", response.success)
|
|
print("SUMMARY:", response.to_markdown())
|
|
print("\n\n---\n\n")
|
|
print("DATA:", json.to_string(response.data, default = str))
|
|
if not response.success:
|
|
print("\n\n---\n\n")
|
|
|
|
|
|
asyncio.run(main())
|