diff --git a/drb-c2-core/app/routers/places.py b/drb-c2-core/app/routers/places.py index 2ce71db..80797cf 100644 --- a/drb-c2-core/app/routers/places.py +++ b/drb-c2-core/app/routers/places.py @@ -5,8 +5,9 @@ from app.internal.logger import logger router = APIRouter(prefix="/places", tags=["places"]) -PLACES_SEARCH_URL = "https://maps.googleapis.com/maps/api/place/textsearch/json" -DIRECTIONS_URL = "https://maps.googleapis.com/maps/api/directions/json" +_PLACES_SEARCH_URL = "https://places.googleapis.com/v1/places:searchText" +_ROUTES_URL = "https://routes.googleapis.com/directions/v2:computeRoutes" +_PLACES_FIELDS = "places.id,places.displayName,places.formattedAddress,places.rating,places.googleMapsUri,places.location" @router.get("/search") @@ -17,9 +18,13 @@ async def search_places(query: str = Query(...), near: str = Query("")): full_query = f"{query} {near}".strip() try: async with httpx.AsyncClient(timeout=10) as client: - r = await client.get( - PLACES_SEARCH_URL, - params={"query": full_query, "key": settings.google_maps_api_key}, + r = await client.post( + _PLACES_SEARCH_URL, + json={"textQuery": full_query}, + headers={ + "X-Goog-Api-Key": settings.google_maps_api_key, + "X-Goog-FieldMask": _PLACES_FIELDS, + }, ) r.raise_for_status() data = r.json() @@ -29,15 +34,15 @@ async def search_places(query: str = Query(...), near: str = Query("")): return [ { - "name": p.get("name"), - "address": p.get("formatted_address"), - "place_id": p.get("place_id"), - "lat": p.get("geometry", {}).get("location", {}).get("lat"), - "lng": p.get("geometry", {}).get("location", {}).get("lng"), - "maps_link": f"https://www.google.com/maps/place/?q=place_id:{p.get('place_id')}", + "name": p.get("displayName", {}).get("text"), + "address": p.get("formattedAddress"), + "place_id": p.get("id"), + "lat": p.get("location", {}).get("latitude"), + "lng": p.get("location", {}).get("longitude"), + "maps_link": p.get("googleMapsUri"), "rating": p.get("rating"), } - for p in data.get("results", [])[:6] + for p in data.get("places", [])[:6] ] @@ -51,13 +56,16 @@ async def get_directions( try: async with httpx.AsyncClient(timeout=10) as client: - r = await client.get( - DIRECTIONS_URL, - params={ - "origin": origin, - "destination": destination, - "mode": "driving", - "key": settings.google_maps_api_key, + r = await client.post( + _ROUTES_URL, + json={ + "origin": {"address": origin}, + "destination": {"address": destination}, + "travelMode": "DRIVE", + }, + headers={ + "X-Goog-Api-Key": settings.google_maps_api_key, + "X-Goog-FieldMask": "routes.duration,routes.distanceMeters", }, ) r.raise_for_status() @@ -70,9 +78,23 @@ async def get_directions( if not routes: return {"duration_text": None, "duration_seconds": None, "distance_text": None} - leg = routes[0]["legs"][0] + route = routes[0] + duration_seconds = int(route.get("duration", "0s").rstrip("s") or 0) + distance_m = route.get("distanceMeters", 0) + + # Format human-readable strings + hours, rem = divmod(duration_seconds, 3600) + mins = rem // 60 + if hours: + duration_text = f"{hours} hr {mins} min" if mins else f"{hours} hr" + else: + duration_text = f"{mins} min" + + miles = distance_m / 1609.34 + distance_text = f"{miles:.1f} mi" + return { - "duration_text": leg["duration"]["text"], - "duration_seconds": leg["duration"]["value"], - "distance_text": leg["distance"]["text"], + "duration_text": duration_text, + "duration_seconds": duration_seconds, + "distance_text": distance_text, } diff --git a/drb-c2-core/app/routers/trips.py b/drb-c2-core/app/routers/trips.py index 2e70597..bd5b430 100644 --- a/drb-c2-core/app/routers/trips.py +++ b/drb-c2-core/app/routers/trips.py @@ -74,30 +74,38 @@ _TOOLS = [ ] +_PLACES_SEARCH_URL = "https://places.googleapis.com/v1/places:searchText" +_PLACES_FIELDS = "places.id,places.displayName,places.formattedAddress,places.rating,places.googleMapsUri" + + async def _places_search(query: str, near: str) -> list[dict]: if not settings.google_maps_api_key: return [] + full_query = f"{query} {near}".strip() try: async with httpx.AsyncClient(timeout=8) as client: - r = await client.get( - "https://maps.googleapis.com/maps/api/place/textsearch/json", - params={"query": f"{query} {near}".strip(), "key": settings.google_maps_api_key}, + r = await client.post( + _PLACES_SEARCH_URL, + json={"textQuery": full_query}, + headers={ + "X-Goog-Api-Key": settings.google_maps_api_key, + "X-Goog-FieldMask": _PLACES_FIELDS, + }, ) data = r.json() - status = data.get("status") - results = data.get("results", []) - logger.info(f"Places search '{query} {near}': status={status}, count={len(results)}") - if status not in ("OK", "ZERO_RESULTS"): - logger.warning(f"Places API error: {status} — {data.get('error_message', '')}") + places = data.get("places", []) + logger.info(f"Places search '{full_query}': count={len(places)}") + if not places and "error" in data: + logger.warning(f"Places API error: {data['error'].get('message', '')}") return [ { - "name": p.get("name"), - "address": p.get("formatted_address"), - "place_id": p.get("place_id"), - "maps_link": f"https://www.google.com/maps/place/?q=place_id:{p.get('place_id')}", + "name": p.get("displayName", {}).get("text"), + "address": p.get("formattedAddress"), + "place_id": p.get("id"), + "maps_link": p.get("googleMapsUri"), "rating": p.get("rating"), } - for p in results[:5] + for p in places[:5] ] except Exception as e: logger.error(f"Places search in assistant failed: {e}")