diff --git a/backend/trip/models/models.py b/backend/trip/models/models.py index 907d22c..b6d28d3 100644 --- a/backend/trip/models/models.py +++ b/backend/trip/models/models.py @@ -97,13 +97,13 @@ class PendingTOTP(BaseModel): class GooglePlaceResult(BaseModel): - id: str | None = None name: str | None = None lat: float | None = None lng: float | None = None price: float | None = None + allowsdog: bool | None = None + description: str | None = None types: list[str] = [] - allows_dogs: bool | None = None class ImageBase(SQLModel): @@ -198,6 +198,7 @@ class UserUpdate(UserBase): map_lng: float | None = None currency: str | None = None do_not_display: list[str] | None = None + google_apikey: str | None = None class UserRead(UserBase): @@ -219,7 +220,7 @@ class UserRead(UserBase): mode_dark=obj.mode_dark, mode_gpx_in_place=obj.mode_gpx_in_place, totp_enabled=obj.totp_enabled, - google_apikey=True if obj.google_apikey else False + google_apikey=True if obj.google_apikey else False, ) diff --git a/backend/trip/routers/places.py b/backend/trip/routers/places.py index 46e6733..cf0f3ca 100644 --- a/backend/trip/routers/places.py +++ b/backend/trip/routers/places.py @@ -6,12 +6,14 @@ from sqlmodel import select from ..config import settings from ..deps import SessionDep, get_current_username -from ..models.models import (Category, Image, GooglePlaceResult, Place, PlaceCreate, - PlaceRead, PlacesCreate, PlaceUpdate) +from ..models.models import (Category, GooglePlaceResult, Image, Place, + PlaceCreate, PlaceRead, PlacesCreate, PlaceUpdate, + User) 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, save_image_to_file) -from ..utils.gmaps import compute_avg_price, gmaps_textsearch router = APIRouter(prefix="/api/places", tags=["places"]) @@ -111,25 +113,26 @@ async def create_places( return [PlaceRead.serialize(p) for p in new_places] -@router.get("/google-search", response_model=list[GooglePlaceResult]) -async def google_search_text(q: str, session: SessionDep, current_user: Annotated[str, Depends(get_current_username)]) -> list[GooglePlaceResult]: +@router.get("/google-search") +async def google_search_text( + q: str, session: SessionDep, current_user: Annotated[str, Depends(get_current_username)] +): db_user = session.get(User, current_user) if not db_user or not db_user.google_apikey: raise HTTPException(status_code=400, detail="Google Maps API key not configured") data = await gmaps_textsearch(q, db_user.google_apikey) - results = [] for place in data: loc = place.get("location", {}) result = GooglePlaceResult( - id=place.get("id"), - name=place.get("displayName", {}).get('text'), - lat=loc.get("latitude"), - lng=loc.get("longitude"), + name=place.get("displayName", {}).get("text"), + lat=loc.get("latitude", None), + lng=loc.get("longitude", None), price=compute_avg_price(place.get("priceRange")), types=place.get("types", []), - allow_dogs=place.get("allowDogs") + allowdog=place.get("allowDogs"), + description=compute_description(place), ) results.append(result) diff --git a/backend/trip/utils/gmaps.py b/backend/trip/utils/gmaps.py index 53d46a1..5437e65 100644 --- a/backend/trip/utils/gmaps.py +++ b/backend/trip/utils/gmaps.py @@ -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 + import httpx 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") end = price_range.get("endPrice", {}).get("units") - + if start and end: return (int(start) + int(end)) / 2 elif start: @@ -25,15 +21,28 @@ def compute_avg_price(price_range: dict | None) -> float | 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]]: url = "https://places.googleapis.com/v1/places:searchText" - body = { - "textQuery": search - } + body = {"textQuery": search} headers = { "Content-Type": "application/json", "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: @@ -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.raise_for_status() data = response.json() - return data.get('places', []) + return data.get("places", []) except Exception: raise HTTPException(status_code=400, detail="Bad Request") \ No newline at end of file