""" Forecast API endpoints. Responsabilidad: Manejar requests de forecasting y delegar a use cases. """ from fastapi import APIRouter, Depends, HTTPException, status from typing import List from app.api.dependencies import ( get_forecast_univariate_use_case, get_forecast_multi_series_use_case ) from app.application.use_cases.forecast_use_case import ( ForecastUnivariateUseCase, ForecastMultiSeriesUseCase ) from app.application.dtos.forecast_dtos import ( ForecastUnivariateRequestDTO, ForecastUnivariateResponseDTO, ForecastMultiSeriesRequestDTO, ForecastMultiSeriesResponseDTO ) from app.utils.logger import setup_logger logger = setup_logger(__name__) router = APIRouter(prefix="/forecast", tags=["Forecast"]) @router.post( "/univariate", response_model=ForecastUnivariateResponseDTO, status_code=status.HTTP_200_OK, summary="Pronóstico univariado", description="Genera pronóstico para una serie temporal sin covariables" ) async def forecast_univariate( request: ForecastUnivariateRequestDTO, use_case: ForecastUnivariateUseCase = Depends(get_forecast_univariate_use_case) ): """ Pronóstico univariado. Genera pronóstico probabilístico para una serie temporal simple, sin variables exógenas. Args: request: Datos de la serie y parámetros de predicción use_case: Caso de uso inyectado Returns: Pronóstico con mediana y cuantiles Raises: HTTPException: Si hay error en la predicción Example: ```json { "values": [100, 102, 105, 103, 108, 112], "prediction_length": 3, "freq": "D", "quantile_levels": [0.1, 0.5, 0.9] } ``` """ try: logger.info( f"Forecast univariate request: {len(request.values)} values, " f"{request.prediction_length} steps ahead" ) # Ejecutar use case response = use_case.execute(request) logger.info(f"Forecast completed: {len(response.timestamps)} predictions") return response except ValueError as e: logger.error(f"Validation error: {e}") raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) except Exception as e: logger.error(f"Unexpected error in forecast: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno al generar pronóstico" ) @router.post( "/multi-series", response_model=ForecastMultiSeriesResponseDTO, status_code=status.HTTP_200_OK, summary="Pronóstico multi-series", description="Genera pronósticos para múltiples series simultáneamente" ) async def forecast_multi_series( request: ForecastMultiSeriesRequestDTO, use_case: ForecastMultiSeriesUseCase = Depends(get_forecast_multi_series_use_case) ): """ Pronóstico para múltiples series. Genera pronósticos independientes para varias series temporales en una sola llamada. Args: request: Lista de series y parámetros use_case: Caso de uso inyectado Returns: Lista de pronósticos, uno por cada serie Example: ```json { "series_list": [ {"series_id": "sales", "values": [100, 102, 105]}, {"series_id": "revenue", "values": [200, 205, 210]} ], "prediction_length": 3, "freq": "D" } ``` """ try: logger.info( f"Forecast multi-series request: {len(request.series_list)} series" ) # Ejecutar use case response = use_case.execute(request) logger.info( f"Multi-series forecast completed: " f"{len(response.forecasts)} forecasts" ) return response except ValueError as e: logger.error(f"Validation error: {e}") raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) except Exception as e: logger.error( f"Unexpected error in multi-series forecast: {e}", exc_info=True ) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno al generar pronósticos" )