Improve backend
This commit is contained in:
147
services/forecasting/app/jobs/daily_validation.py
Normal file
147
services/forecasting/app/jobs/daily_validation.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# ================================================================
|
||||
# services/forecasting/app/jobs/daily_validation.py
|
||||
# ================================================================
|
||||
"""
|
||||
Daily Validation Job
|
||||
|
||||
Scheduled job to validate previous day's forecasts against actual sales.
|
||||
This job is called by the orchestrator as part of the daily workflow.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import structlog
|
||||
import uuid
|
||||
|
||||
from app.services.validation_service import ValidationService
|
||||
from app.core.database import database_manager
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
async def daily_validation_job(
|
||||
tenant_id: uuid.UUID,
|
||||
orchestration_run_id: Optional[uuid.UUID] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate yesterday's forecasts against actual sales
|
||||
|
||||
This function is designed to be called by the orchestrator as part of
|
||||
the daily workflow (Step 5: validate_previous_forecasts).
|
||||
|
||||
Args:
|
||||
tenant_id: Tenant identifier
|
||||
orchestration_run_id: Optional orchestration run ID for tracking
|
||||
|
||||
Returns:
|
||||
Dictionary with validation results
|
||||
"""
|
||||
async with database_manager.get_session() as db:
|
||||
try:
|
||||
logger.info(
|
||||
"Starting daily validation job",
|
||||
tenant_id=tenant_id,
|
||||
orchestration_run_id=orchestration_run_id
|
||||
)
|
||||
|
||||
validation_service = ValidationService(db)
|
||||
|
||||
# Validate yesterday's forecasts
|
||||
result = await validation_service.validate_yesterday(
|
||||
tenant_id=tenant_id,
|
||||
orchestration_run_id=orchestration_run_id,
|
||||
triggered_by="orchestrator"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Daily validation job completed",
|
||||
tenant_id=tenant_id,
|
||||
validation_run_id=result.get("validation_run_id"),
|
||||
forecasts_evaluated=result.get("forecasts_evaluated"),
|
||||
forecasts_with_actuals=result.get("forecasts_with_actuals"),
|
||||
overall_mape=result.get("overall_metrics", {}).get("mape")
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Daily validation job failed",
|
||||
tenant_id=tenant_id,
|
||||
orchestration_run_id=orchestration_run_id,
|
||||
error=str(e),
|
||||
error_type=type(e).__name__
|
||||
)
|
||||
return {
|
||||
"status": "failed",
|
||||
"error": str(e),
|
||||
"tenant_id": str(tenant_id),
|
||||
"orchestration_run_id": str(orchestration_run_id) if orchestration_run_id else None
|
||||
}
|
||||
|
||||
|
||||
async def validate_date_range_job(
|
||||
tenant_id: uuid.UUID,
|
||||
start_date: datetime,
|
||||
end_date: datetime,
|
||||
orchestration_run_id: Optional[uuid.UUID] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate forecasts for a specific date range
|
||||
|
||||
Useful for backfilling validation metrics when historical data is uploaded.
|
||||
|
||||
Args:
|
||||
tenant_id: Tenant identifier
|
||||
start_date: Start of validation period
|
||||
end_date: End of validation period
|
||||
orchestration_run_id: Optional orchestration run ID for tracking
|
||||
|
||||
Returns:
|
||||
Dictionary with validation results
|
||||
"""
|
||||
async with database_manager.get_session() as db:
|
||||
try:
|
||||
logger.info(
|
||||
"Starting date range validation job",
|
||||
tenant_id=tenant_id,
|
||||
start_date=start_date.isoformat(),
|
||||
end_date=end_date.isoformat(),
|
||||
orchestration_run_id=orchestration_run_id
|
||||
)
|
||||
|
||||
validation_service = ValidationService(db)
|
||||
|
||||
result = await validation_service.validate_date_range(
|
||||
tenant_id=tenant_id,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
orchestration_run_id=orchestration_run_id,
|
||||
triggered_by="scheduled"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Date range validation job completed",
|
||||
tenant_id=tenant_id,
|
||||
validation_run_id=result.get("validation_run_id"),
|
||||
forecasts_evaluated=result.get("forecasts_evaluated"),
|
||||
forecasts_with_actuals=result.get("forecasts_with_actuals")
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Date range validation job failed",
|
||||
tenant_id=tenant_id,
|
||||
start_date=start_date.isoformat(),
|
||||
end_date=end_date.isoformat(),
|
||||
error=str(e),
|
||||
error_type=type(e).__name__
|
||||
)
|
||||
return {
|
||||
"status": "failed",
|
||||
"error": str(e),
|
||||
"tenant_id": str(tenant_id),
|
||||
"orchestration_run_id": str(orchestration_run_id) if orchestration_run_id else None
|
||||
}
|
||||
Reference in New Issue
Block a user