2025-10-30 21:08:07 +01:00
|
|
|
# ================================================================
|
|
|
|
|
# services/orchestrator/app/models/orchestration_run.py
|
|
|
|
|
# ================================================================
|
|
|
|
|
"""
|
|
|
|
|
Orchestration Run Models - Audit trail for orchestration executions
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
import enum
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
from sqlalchemy import Column, String, DateTime, Integer, Text, Boolean, Enum as SQLEnum
|
|
|
|
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
|
|
|
|
from sqlalchemy.sql import func
|
|
|
|
|
|
|
|
|
|
from shared.database.base import Base
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OrchestrationStatus(enum.Enum):
|
|
|
|
|
"""Orchestration run status"""
|
|
|
|
|
pending = "pending"
|
|
|
|
|
running = "running"
|
|
|
|
|
completed = "completed"
|
|
|
|
|
partial_success = "partial_success"
|
|
|
|
|
failed = "failed"
|
|
|
|
|
cancelled = "cancelled"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OrchestrationRun(Base):
|
|
|
|
|
"""Audit trail for orchestration executions"""
|
|
|
|
|
__tablename__ = "orchestration_runs"
|
|
|
|
|
|
|
|
|
|
# Primary identification
|
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
|
run_number = Column(String(50), nullable=False, unique=True, index=True)
|
|
|
|
|
|
|
|
|
|
# Run details
|
|
|
|
|
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
|
|
|
status = Column(SQLEnum(OrchestrationStatus), nullable=False, default=OrchestrationStatus.pending, index=True)
|
|
|
|
|
run_type = Column(String(50), nullable=False, default="scheduled") # scheduled, manual, test
|
|
|
|
|
priority = Column(String(20), nullable=False, default="normal") # normal, high, critical
|
|
|
|
|
|
|
|
|
|
# Timing
|
|
|
|
|
started_at = Column(DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
|
|
|
|
|
completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
duration_seconds = Column(Integer, nullable=True)
|
|
|
|
|
|
|
|
|
|
# Step tracking
|
|
|
|
|
forecasting_started_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
forecasting_completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
forecasting_status = Column(String(20), nullable=True) # success, failed, skipped
|
|
|
|
|
forecasting_error = Column(Text, nullable=True)
|
|
|
|
|
|
|
|
|
|
production_started_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
production_completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
production_status = Column(String(20), nullable=True) # success, failed, skipped
|
|
|
|
|
production_error = Column(Text, nullable=True)
|
|
|
|
|
|
|
|
|
|
procurement_started_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
procurement_completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
procurement_status = Column(String(20), nullable=True) # success, failed, skipped
|
|
|
|
|
procurement_error = Column(Text, nullable=True)
|
|
|
|
|
|
|
|
|
|
notification_started_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
notification_completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
notification_status = Column(String(20), nullable=True) # success, failed, skipped
|
|
|
|
|
notification_error = Column(Text, nullable=True)
|
|
|
|
|
|
2025-11-05 13:33:13 +00:00
|
|
|
# AI Insights tracking
|
|
|
|
|
ai_insights_started_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
ai_insights_completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
ai_insights_status = Column(String(20), nullable=True) # success, failed, skipped
|
|
|
|
|
ai_insights_error = Column(Text, nullable=True)
|
|
|
|
|
ai_insights_generated = Column(Integer, nullable=False, default=0)
|
|
|
|
|
ai_insights_posted = Column(Integer, nullable=False, default=0)
|
|
|
|
|
|
2025-10-30 21:08:07 +01:00
|
|
|
# Results summary
|
|
|
|
|
forecasts_generated = Column(Integer, nullable=False, default=0)
|
|
|
|
|
production_batches_created = Column(Integer, nullable=False, default=0)
|
|
|
|
|
procurement_plans_created = Column(Integer, nullable=False, default=0)
|
|
|
|
|
purchase_orders_created = Column(Integer, nullable=False, default=0)
|
|
|
|
|
notifications_sent = Column(Integer, nullable=False, default=0)
|
|
|
|
|
|
|
|
|
|
# Forecast data passed between services
|
|
|
|
|
forecast_data = Column(JSONB, nullable=True) # Store forecast results for downstream services
|
|
|
|
|
|
|
|
|
|
# Error handling
|
|
|
|
|
retry_count = Column(Integer, nullable=False, default=0)
|
|
|
|
|
max_retries_reached = Column(Boolean, nullable=False, default=False)
|
|
|
|
|
error_message = Column(Text, nullable=True)
|
|
|
|
|
error_details = Column(JSONB, nullable=True)
|
|
|
|
|
|
|
|
|
|
# External references
|
2025-11-05 13:33:13 +00:00
|
|
|
forecast_id = Column(UUID(as_uuid=True), nullable=True)
|
2025-10-30 21:08:07 +01:00
|
|
|
production_schedule_id = Column(UUID(as_uuid=True), nullable=True)
|
|
|
|
|
procurement_plan_id = Column(UUID(as_uuid=True), nullable=True)
|
|
|
|
|
|
2025-11-05 13:33:13 +00:00
|
|
|
# Saga tracking
|
|
|
|
|
saga_steps_total = Column(Integer, nullable=False, default=0)
|
|
|
|
|
saga_steps_completed = Column(Integer, nullable=False, default=0)
|
|
|
|
|
|
2025-10-30 21:08:07 +01:00
|
|
|
# Audit fields
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
|
|
|
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
|
|
|
|
triggered_by = Column(String(100), nullable=True) # scheduler, user_id, api
|
|
|
|
|
|
|
|
|
|
# Performance metrics
|
|
|
|
|
fulfillment_rate = Column(Integer, nullable=True) # Percentage as integer (0-100)
|
|
|
|
|
on_time_delivery_rate = Column(Integer, nullable=True) # Percentage as integer (0-100)
|
|
|
|
|
cost_accuracy = Column(Integer, nullable=True) # Percentage as integer (0-100)
|
|
|
|
|
quality_score = Column(Integer, nullable=True) # Rating as integer (0-100)
|
|
|
|
|
|
|
|
|
|
# Metadata
|
|
|
|
|
run_metadata = Column(JSONB, nullable=True)
|