# ================================================================ # services/data/app/api/traffic.py - FIXED VERSION # ================================================================ """Traffic data API endpoints with improved error handling""" from fastapi import APIRouter, Depends, HTTPException, Query, Path from typing import List, Dict, Any from datetime import datetime, timedelta import structlog from uuid import UUID from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.services.traffic_service import TrafficService from app.services.messaging import data_publisher, publish_traffic_updated from app.schemas.external import ( TrafficDataResponse, HistoricalTrafficRequest ) from shared.auth.decorators import ( get_current_user_dep ) router = APIRouter(tags=["traffic"]) traffic_service = TrafficService() logger = structlog.get_logger() @router.get("/tenants/{tenant_id}/traffic/current", response_model=TrafficDataResponse) async def get_current_traffic( latitude: float = Query(..., description="Latitude"), longitude: float = Query(..., description="Longitude"), tenant_id: UUID = Path(..., description="Tenant ID"), current_user: Dict[str, Any] = Depends(get_current_user_dep), ): """Get current traffic data for location""" try: logger.debug("API: Getting current traffic", lat=latitude, lon=longitude) traffic = await traffic_service.get_current_traffic(latitude, longitude) if not traffic: logger.warning("No traffic data available", lat=latitude, lon=longitude) raise HTTPException(status_code=404, detail="Traffic data not available") # Publish event (with error handling) try: await publish_traffic_updated({ "type": "current_requested", "latitude": latitude, "longitude": longitude, "timestamp": datetime.utcnow().isoformat() }) except Exception as pub_error: logger.warning("Failed to publish traffic event", error=str(pub_error)) # Continue processing - event publishing failure shouldn't break the API logger.debug("Successfully returning traffic data", volume=traffic.traffic_volume, congestion=traffic.congestion_level) return traffic except HTTPException: # Re-raise HTTP exceptions raise except Exception as e: logger.error("Unexpected error in traffic API", error=str(e)) import traceback logger.error("Traffic API traceback", traceback=traceback.format_exc()) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") @router.post("/tenants/{tenant_id}/traffic/historical") async def get_historical_traffic( request: HistoricalTrafficRequest, db: AsyncSession = Depends(get_db), tenant_id: UUID = Path(..., description="Tenant ID"), current_user: Dict[str, Any] = Depends(get_current_user_dep), ): """Get historical traffic data with date range in payload""" try: # Validate date range if request.end_date <= request.start_date: raise HTTPException(status_code=400, detail="End date must be after start date") if (request.end_date - request.start_date).days > 1000: raise HTTPException(status_code=400, detail="Date range cannot exceed 90 days") historical_data = await traffic_service.get_historical_traffic( request.latitude, request.longitude, request.start_date, request.end_date, db ) # Publish event (with error handling) try: await publish_traffic_updated({ "type": "historical_requested", "latitude": request.latitude, "longitude": request.longitude, "start_date": request.start_date.isoformat(), "end_date": request.end_date.isoformat(), "records_count": len(historical_data), "timestamp": datetime.utcnow().isoformat() }) except Exception as pub_error: logger.warning("Failed to publish historical traffic event", error=str(pub_error)) # Continue processing return historical_data except HTTPException: raise except Exception as e: logger.error("Unexpected error in historical traffic API", error=str(e)) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") @router.post("/tenants/{tenant_id}/traffic/stored") async def get_stored_traffic_for_training( request: HistoricalTrafficRequest, db: AsyncSession = Depends(get_db), tenant_id: UUID = Path(..., description="Tenant ID"), current_user: Dict[str, Any] = Depends(get_current_user_dep), ): """Get stored traffic data specifically for training/re-training purposes""" try: # Validate date range if request.end_date <= request.start_date: raise HTTPException(status_code=400, detail="End date must be after start date") # Allow longer date ranges for training (up to 3 years) if (request.end_date - request.start_date).days > 1095: raise HTTPException(status_code=400, detail="Date range cannot exceed 3 years for training data") logger.info("Retrieving stored traffic data for training", tenant_id=str(tenant_id), location=f"{request.latitude},{request.longitude}", date_range=f"{request.start_date} to {request.end_date}") # Use the dedicated method for training data retrieval stored_data = await traffic_service.get_stored_traffic_for_training( request.latitude, request.longitude, request.start_date, request.end_date, db ) # Log retrieval for audit purposes logger.info("Stored traffic data retrieved for training", records_count=len(stored_data), tenant_id=str(tenant_id), purpose="model_training") # Publish event for monitoring try: await publish_traffic_updated({ "type": "stored_data_retrieved_for_training", "latitude": request.latitude, "longitude": request.longitude, "start_date": request.start_date.isoformat(), "end_date": request.end_date.isoformat(), "records_count": len(stored_data), "tenant_id": str(tenant_id), "timestamp": datetime.utcnow().isoformat() }) except Exception as pub_error: logger.warning("Failed to publish stored traffic retrieval event", error=str(pub_error)) return stored_data except HTTPException: raise except Exception as e: logger.error("Unexpected error in stored traffic retrieval API", error=str(e)) raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")