Feat(Builder): Add Google Maps Search Block (#8162)

* Feat(Builder): Add Google Maps Search Block

* format

* Updates to google maps search block

* fixes

* format + updates again

* fix for pytest

* format again

* updates based on new comments

* fix for format?

---------

Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
This commit is contained in:
Bently
2024-09-27 21:29:59 +01:00
committed by GitHub
parent a5c63880b8
commit d7e59966ef
5 changed files with 146 additions and 1 deletions

View File

@@ -74,6 +74,8 @@ SMTP_PASSWORD=
MEDIUM_API_KEY=
MEDIUM_AUTHOR_ID=
# Google Maps
GOOGLE_MAPS_API_KEY=
# Logging Configuration
LOG_LEVEL=INFO

View File

@@ -0,0 +1,127 @@
import googlemaps
from pydantic import BaseModel
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import BlockSecret, SchemaField, SecretField
class Place(BaseModel):
name: str
address: str
phone: str
rating: float
reviews: int
website: str
class GoogleMapsSearchBlock(Block):
class Input(BlockSchema):
api_key: BlockSecret = SecretField(
key="google_maps_api_key",
description="Google Maps API Key",
)
query: str = SchemaField(
description="Search query for local businesses",
placeholder="e.g., 'restaurants in New York'",
)
radius: int = SchemaField(
description="Search radius in meters (max 50000)",
default=5000,
ge=1,
le=50000,
)
max_results: int = SchemaField(
description="Maximum number of results to return (max 60)",
default=20,
ge=1,
le=60,
)
class Output(BlockSchema):
place: Place = SchemaField(description="Place found")
error: str = SchemaField(description="Error message if the search failed")
def __init__(self):
super().__init__(
id="f47ac10b-58cc-4372-a567-0e02b2c3d479",
description="This block searches for local businesses using Google Maps API.",
categories={BlockCategory.SEARCH},
input_schema=GoogleMapsSearchBlock.Input,
output_schema=GoogleMapsSearchBlock.Output,
test_input={
"api_key": "your_test_api_key",
"query": "restaurants in new york",
"radius": 5000,
"max_results": 5,
},
test_output=[
(
"place",
{
"name": "Test Restaurant",
"address": "123 Test St, New York, NY 10001",
"phone": "+1 (555) 123-4567",
"rating": 4.5,
"reviews": 100,
"website": "https://testrestaurant.com",
},
),
],
test_mock={
"search_places": lambda *args, **kwargs: [
{
"name": "Test Restaurant",
"address": "123 Test St, New York, NY 10001",
"phone": "+1 (555) 123-4567",
"rating": 4.5,
"reviews": 100,
"website": "https://testrestaurant.com",
}
]
},
)
def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
places = self.search_places(
input_data.api_key.get_secret_value(),
input_data.query,
input_data.radius,
input_data.max_results,
)
for place in places:
yield "place", place
except Exception as e:
yield "error", str(e)
def search_places(self, api_key, query, radius, max_results):
client = googlemaps.Client(key=api_key)
return self._search_places(client, query, radius, max_results)
def _search_places(self, client, query, radius, max_results):
results = []
next_page_token = None
while len(results) < max_results:
response = client.places(
query=query,
radius=radius,
page_token=next_page_token,
)
for place in response["results"]:
if len(results) >= max_results:
break
place_details = client.place(place["place_id"])["result"]
results.append(
Place(
name=place_details.get("name", ""),
address=place_details.get("formatted_address", ""),
phone=place_details.get("formatted_phone_number", ""),
rating=place_details.get("rating", 0),
reviews=place_details.get("user_ratings_total", 0),
website=place_details.get("website", ""),
)
)
next_page_token = response.get("next_page_token")
if not next_page_token:
break
return results

View File

@@ -216,6 +216,8 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings):
sentry_dsn: str = Field(default="", description="Sentry DSN")
google_maps_api_key: str = Field(default="", description="Google Maps API Key")
# Add more secret fields as needed
model_config = SettingsConfigDict(

View File

@@ -989,6 +989,19 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4
[package.extras]
grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"]
[[package]]
name = "googlemaps"
version = "4.10.0"
description = "Python client library for Google Maps Platform"
optional = false
python-versions = ">=3.5"
files = [
{file = "googlemaps-4.10.0.tar.gz", hash = "sha256:3055fcbb1aa262a9159b589b5e6af762b10e80634ae11c59495bd44867e47d88"},
]
[package.dependencies]
requests = ">=2.20.0,<3.0"
[[package]]
name = "gotrue"
version = "2.8.1"
@@ -3623,4 +3636,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "311c527a1d1947af049dac27c7a2b2f49d7fa4cdede52ef436422a528b0ad866"
content-hash = "f42fdf521b0476048626238ee631b618a71ed079c369c28035a899f2252369c9"

View File

@@ -44,6 +44,7 @@ tenacity = "^8.3.0"
uvicorn = { extras = ["standard"], version = "^0.30.1" }
websockets = "^12.0"
youtube-transcript-api = "^0.6.2"
googlemaps = "^4.10.0"
[tool.poetry.group.dev.dependencies]
poethepoet = "^0.26.1"