from fastapi import APIRouter, HTTPException, BackgroundTasks, Query from typing import Optional import logging from db.geocoding_repository import GeocodingRepository from config.settings import GEOAPIFY_API_KEY router = APIRouter(prefix="/geocoding", tags=["Geocoding"]) logger = logging.getLogger(__name__) # Global variable to track geocoding status geocoding_status = { "running": False, "last_run": None, "last_result": None } async def run_geocoding_task(api_key: str = None): """Background task to run geocoding""" global geocoding_status try: geocoding_status["running"] = True geocoding_status["last_result"] = None async with GeocodingRepository() as repo: result = await repo.batch_geocode_stores(api_key=api_key) geocoding_status["last_result"] = result geocoding_status["last_run"] = "completed" except Exception as e: logger.error(f"Error in geocoding task: {e}") geocoding_status["last_result"] = {"error": str(e)} geocoding_status["last_run"] = "failed" finally: geocoding_status["running"] = False @router.post("/start") async def start_geocoding( background_tasks: BackgroundTasks, api_key: Optional[str] = Query(None, description="Geoapify API key (optional - will use settings if not provided)") ): """ Start the geocoding process for all stores without coordinates using Geoapify. - **api_key**: Geoapify API key (optional - will use environment variable if not provided) """ if geocoding_status["running"]: raise HTTPException( status_code=400, detail="Geocoding process is already running" ) # Use provided API key or fallback to settings final_api_key = api_key or GEOAPIFY_API_KEY if not final_api_key: raise HTTPException( status_code=400, detail="Geoapify API key is required. Provide it as parameter or set GEOAPIFY_API_KEY environment variable." ) # Start background task background_tasks.add_task(run_geocoding_task, final_api_key) return { "message": "Geocoding process started", "status": "running", "service": "Geoapify" } @router.get("/status") async def get_geocoding_status(): """Get the current status of the geocoding process""" return { "running": geocoding_status["running"], "last_run": geocoding_status["last_run"], "last_result": geocoding_status["last_result"] } @router.get("/stores-without-coordinates") async def get_stores_without_coordinates(): """Get count of stores that still need geocoding""" try: async with GeocodingRepository() as repo: stores = repo.get_stores_without_coordinates() return { "count": len(stores), "stores": stores[:10] # Return first 10 as sample } except Exception as e: logger.error(f"Error getting stores without coordinates: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.post("/geocode-single/{store_id}") async def geocode_single_store( store_id: str, api_key: Optional[str] = Query(None, description="Geoapify API key") ): """Geocode a single store by ID""" try: # Use provided API key or fallback to settings final_api_key = api_key or GEOAPIFY_API_KEY if not final_api_key: raise HTTPException( status_code=400, detail="Geoapify API key is required" ) async with GeocodingRepository() as repo: # Get store details store_response = repo.supabase.table('stores').select('*').eq('store_id', store_id).execute() if not store_response.data: raise HTTPException(status_code=404, detail="Store not found") store = store_response.data[0] address = store.get('store_address', '') if not address: raise HTTPException(status_code=400, detail="Store has no address") # Geocode address using Geoapify coordinates = await repo.geocode_address_geoapify(address, store_id, final_api_key) if coordinates: lat, lng = coordinates if repo.update_store_coordinates(store_id, lat, lng): return { "message": "Store geocoded successfully", "store_id": store_id, "coordinates": {"lat": lat, "lng": lng} } else: raise HTTPException(status_code=500, detail="Failed to update coordinates") else: raise HTTPException(status_code=400, detail="Failed to geocode address") except HTTPException: raise except Exception as e: logger.error(f"Error geocoding single store: {e}") raise HTTPException(status_code=500, detail=str(e))