GMaps API searchText

This commit is contained in:
itskovacs 2025-11-01 19:32:22 +01:00
parent bd8e0c2578
commit 028b9ab64f
3 changed files with 38 additions and 25 deletions

View File

@ -97,13 +97,13 @@ class PendingTOTP(BaseModel):
class GooglePlaceResult(BaseModel): class GooglePlaceResult(BaseModel):
id: str | None = None
name: str | None = None name: str | None = None
lat: float | None = None lat: float | None = None
lng: float | None = None lng: float | None = None
price: float | None = None price: float | None = None
allowsdog: bool | None = None
description: str | None = None
types: list[str] = [] types: list[str] = []
allows_dogs: bool | None = None
class ImageBase(SQLModel): class ImageBase(SQLModel):
@ -198,6 +198,7 @@ class UserUpdate(UserBase):
map_lng: float | None = None map_lng: float | None = None
currency: str | None = None currency: str | None = None
do_not_display: list[str] | None = None do_not_display: list[str] | None = None
google_apikey: str | None = None
class UserRead(UserBase): class UserRead(UserBase):
@ -219,7 +220,7 @@ class UserRead(UserBase):
mode_dark=obj.mode_dark, mode_dark=obj.mode_dark,
mode_gpx_in_place=obj.mode_gpx_in_place, mode_gpx_in_place=obj.mode_gpx_in_place,
totp_enabled=obj.totp_enabled, totp_enabled=obj.totp_enabled,
google_apikey=True if obj.google_apikey else False google_apikey=True if obj.google_apikey else False,
) )

View File

@ -6,12 +6,14 @@ from sqlmodel import select
from ..config import settings from ..config import settings
from ..deps import SessionDep, get_current_username from ..deps import SessionDep, get_current_username
from ..models.models import (Category, Image, GooglePlaceResult, Place, PlaceCreate, from ..models.models import (Category, GooglePlaceResult, Image, Place,
PlaceRead, PlacesCreate, PlaceUpdate) PlaceCreate, PlaceRead, PlacesCreate, PlaceUpdate,
User)
from ..security import verify_exists_and_owns from ..security import verify_exists_and_owns
from ..utils.gmaps import (compute_avg_price, compute_description,
gmaps_textsearch)
from ..utils.utils import (b64img_decode, download_file, patch_image, from ..utils.utils import (b64img_decode, download_file, patch_image,
save_image_to_file) save_image_to_file)
from ..utils.gmaps import compute_avg_price, gmaps_textsearch
router = APIRouter(prefix="/api/places", tags=["places"]) router = APIRouter(prefix="/api/places", tags=["places"])
@ -111,25 +113,26 @@ async def create_places(
return [PlaceRead.serialize(p) for p in new_places] return [PlaceRead.serialize(p) for p in new_places]
@router.get("/google-search", response_model=list[GooglePlaceResult]) @router.get("/google-search")
async def google_search_text(q: str, session: SessionDep, current_user: Annotated[str, Depends(get_current_username)]) -> list[GooglePlaceResult]: async def google_search_text(
q: str, session: SessionDep, current_user: Annotated[str, Depends(get_current_username)]
):
db_user = session.get(User, current_user) db_user = session.get(User, current_user)
if not db_user or not db_user.google_apikey: if not db_user or not db_user.google_apikey:
raise HTTPException(status_code=400, detail="Google Maps API key not configured") raise HTTPException(status_code=400, detail="Google Maps API key not configured")
data = await gmaps_textsearch(q, db_user.google_apikey) data = await gmaps_textsearch(q, db_user.google_apikey)
results = [] results = []
for place in data: for place in data:
loc = place.get("location", {}) loc = place.get("location", {})
result = GooglePlaceResult( result = GooglePlaceResult(
id=place.get("id"), name=place.get("displayName", {}).get("text"),
name=place.get("displayName", {}).get('text'), lat=loc.get("latitude", None),
lat=loc.get("latitude"), lng=loc.get("longitude", None),
lng=loc.get("longitude"),
price=compute_avg_price(place.get("priceRange")), price=compute_avg_price(place.get("priceRange")),
types=place.get("types", []), types=place.get("types", []),
allow_dogs=place.get("allowDogs") allowdog=place.get("allowDogs"),
description=compute_description(place),
) )
results.append(result) results.append(result)

View File

@ -1,9 +1,5 @@
# https://developers.google.com/maps/documentation/places/web-service/nearby-search
# https://developers.google.com/maps/documentation/places/web-service/text-search
# https://developers.google.com/maps/documentation/places/web-service/place-details
# https://developers.google.com/maps/documentation/places/web-service/place-photos
from typing import Any from typing import Any
import httpx import httpx
from fastapi import HTTPException from fastapi import HTTPException
@ -14,7 +10,7 @@ def compute_avg_price(price_range: dict | None) -> float | None:
start = price_range.get("startPrice", {}).get("units") start = price_range.get("startPrice", {}).get("units")
end = price_range.get("endPrice", {}).get("units") end = price_range.get("endPrice", {}).get("units")
if start and end: if start and end:
return (int(start) + int(end)) / 2 return (int(start) + int(end)) / 2
elif start: elif start:
@ -25,15 +21,28 @@ def compute_avg_price(price_range: dict | None) -> float | None:
return None return None
def compute_description(place: dict[str, Any]):
description = ""
address = place.get("formattedAddress")
phone = place.get("internationalPhoneNumber")
website = place.get("websiteUri")
if address:
description += f"{address}\n"
if phone:
description += f"Phone: {phone}\n"
if website:
description += f"Website: {website}"
return description.rstrip()
async def gmaps_textsearch(search: str, api_key: str) -> list[dict[str, Any]]: async def gmaps_textsearch(search: str, api_key: str) -> list[dict[str, Any]]:
url = "https://places.googleapis.com/v1/places:searchText" url = "https://places.googleapis.com/v1/places:searchText"
body = { body = {"textQuery": search}
"textQuery": search
}
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Goog-Api-Key": api_key, "X-Goog-Api-Key": api_key,
"X-Goog-FieldMask": "places.id,places.types,places.location,places.priceRange,places.displayName,places.allowsDogs" "X-Goog-FieldMask": "places.id,places.types,places.location,places.priceRange,places.formattedAddress,places.websiteUri,places.internationalPhoneNumber,places.displayName,places.allowsDogs",
} }
try: try:
@ -41,6 +50,6 @@ async def gmaps_textsearch(search: str, api_key: str) -> list[dict[str, Any]]:
response = await client.post(url, json=body, headers=headers) response = await client.post(url, json=body, headers=headers)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
return data.get('places', []) return data.get("places", [])
except Exception: except Exception:
raise HTTPException(status_code=400, detail="Bad Request") raise HTTPException(status_code=400, detail="Bad Request")