2025-10-06 15:27:01 +02:00
|
|
|
# services/external/app/api/traffic_data.py
|
|
|
|
|
"""
|
|
|
|
|
Traffic Data API - Atomic CRUD operations on TrafficData model
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Path
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
from datetime import date
|
|
|
|
|
from uuid import UUID
|
|
|
|
|
import structlog
|
|
|
|
|
|
|
|
|
|
from app.schemas.traffic import TrafficDataResponse
|
|
|
|
|
from app.services.traffic_service import TrafficService
|
|
|
|
|
from shared.routing.route_builder import RouteBuilder
|
2025-10-15 16:12:49 +02:00
|
|
|
from shared.auth.decorators import get_current_user_dep
|
|
|
|
|
from shared.auth.access_control import analytics_tier_required
|
2025-10-06 15:27:01 +02:00
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
from app.core.database import get_db
|
|
|
|
|
|
|
|
|
|
route_builder = RouteBuilder('external')
|
|
|
|
|
router = APIRouter(tags=["traffic-data"])
|
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_traffic_service():
|
|
|
|
|
"""Dependency injection for TrafficService"""
|
|
|
|
|
return TrafficService()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_base_route("traffic-data"),
|
|
|
|
|
response_model=List[TrafficDataResponse]
|
|
|
|
|
)
|
2025-10-15 16:12:49 +02:00
|
|
|
@analytics_tier_required
|
2025-10-06 15:27:01 +02:00
|
|
|
async def list_traffic_data(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
start_date: Optional[date] = Query(None),
|
|
|
|
|
end_date: Optional[date] = Query(None),
|
|
|
|
|
latitude: Optional[float] = Query(None),
|
|
|
|
|
longitude: Optional[float] = Query(None),
|
|
|
|
|
limit: int = Query(100, ge=1, le=1000),
|
2025-10-15 16:12:49 +02:00
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
2025-10-06 15:27:01 +02:00
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
traffic_service: TrafficService = Depends(get_traffic_service)
|
|
|
|
|
):
|
2025-10-15 16:12:49 +02:00
|
|
|
"""List stored traffic data records (Professional+ tier required)"""
|
2025-10-06 15:27:01 +02:00
|
|
|
try:
|
|
|
|
|
logger.info("Listing traffic data", tenant_id=tenant_id)
|
|
|
|
|
|
|
|
|
|
traffic_records = await traffic_service.get_stored_traffic_data(
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
|
start_date=start_date,
|
|
|
|
|
end_date=end_date,
|
|
|
|
|
latitude=latitude,
|
|
|
|
|
longitude=longitude,
|
|
|
|
|
limit=limit,
|
|
|
|
|
db=db
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return traffic_records
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Failed to list traffic data", error=str(e), tenant_id=tenant_id)
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve traffic data")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
route_builder.build_resource_detail_route("traffic-data", "traffic_id"),
|
|
|
|
|
response_model=TrafficDataResponse
|
|
|
|
|
)
|
2025-10-15 16:12:49 +02:00
|
|
|
@analytics_tier_required
|
2025-10-06 15:27:01 +02:00
|
|
|
async def get_traffic_data(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
traffic_id: UUID = Path(..., description="Traffic data ID"),
|
2025-10-15 16:12:49 +02:00
|
|
|
current_user: dict = Depends(get_current_user_dep),
|
2025-10-06 15:27:01 +02:00
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
traffic_service: TrafficService = Depends(get_traffic_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get a specific traffic data record"""
|
|
|
|
|
try:
|
|
|
|
|
logger.info("Getting traffic data", tenant_id=tenant_id, traffic_id=traffic_id)
|
|
|
|
|
|
|
|
|
|
traffic_record = await traffic_service.get_traffic_data_by_id(
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
|
traffic_id=traffic_id,
|
|
|
|
|
db=db
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not traffic_record:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Traffic data not found")
|
|
|
|
|
|
|
|
|
|
return traffic_record
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Failed to get traffic data", error=str(e), tenant_id=tenant_id)
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve traffic data")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete(
|
|
|
|
|
route_builder.build_resource_detail_route("traffic-data", "traffic_id")
|
|
|
|
|
)
|
|
|
|
|
async def delete_traffic_data(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
traffic_id: UUID = Path(..., description="Traffic data ID"),
|
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
traffic_service: TrafficService = Depends(get_traffic_service)
|
|
|
|
|
):
|
|
|
|
|
"""Delete a traffic data record"""
|
|
|
|
|
try:
|
|
|
|
|
logger.info("Deleting traffic data", tenant_id=tenant_id, traffic_id=traffic_id)
|
|
|
|
|
|
|
|
|
|
success = await traffic_service.delete_traffic_data(
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
|
traffic_id=traffic_id,
|
|
|
|
|
db=db
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not success:
|
|
|
|
|
raise HTTPException(status_code=404, detail="Traffic data not found")
|
|
|
|
|
|
|
|
|
|
return {"message": "Traffic data deleted successfully"}
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Failed to delete traffic data", error=str(e), tenant_id=tenant_id)
|
|
|
|
|
raise HTTPException(status_code=500, detail="Failed to delete traffic data")
|