import httpx from fastapi import APIRouter, HTTPException, Query from app.config import settings from app.internal.logger import logger router = APIRouter(prefix="/places", tags=["places"]) _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") async def search_places(query: str = Query(...), near: str = Query("")): if not settings.google_maps_api_key: raise HTTPException(503, "Google Maps API not configured.") full_query = f"{query} {near}".strip() try: async with httpx.AsyncClient(timeout=10) as client: 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() except Exception as e: logger.error(f"Places search failed: {e}") raise HTTPException(502, "Places search failed.") return [ { "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("places", [])[:6] ] @router.get("/directions") async def get_directions( origin: str = Query(...), destination: str = Query(...), ): if not settings.google_maps_api_key: raise HTTPException(503, "Google Maps API not configured.") try: async with httpx.AsyncClient(timeout=10) as client: 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() data = r.json() except Exception as e: logger.error(f"Directions failed: {e}") raise HTTPException(502, "Directions request failed.") routes = data.get("routes", []) if not routes: return {"duration_text": None, "duration_seconds": None, "distance_text": None} 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": duration_text, "duration_seconds": duration_seconds, "distance_text": distance_text, }