from __future__ import annotations from datetime import datetime from enum import Enum from typing import TYPE_CHECKING from sqlalchemy import ( JSON, DateTime, Enum as SQLEnum, ForeignKey, Integer, Numeric, String, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from config.database import Base if TYPE_CHECKING: # pragma: no cover from .scenario import Scenario class DistributionType(str, Enum): """Supported stochastic distribution families for simulations.""" NORMAL = "normal" TRIANGULAR = "triangular" UNIFORM = "uniform" LOGNORMAL = "lognormal" CUSTOM = "custom" class SimulationParameter(Base): """Probability distribution settings for scenario simulations.""" __tablename__ = "simulation_parameters" id: Mapped[int] = mapped_column(Integer, primary_key=True) scenario_id: Mapped[int] = mapped_column( ForeignKey("scenarios.id", ondelete="CASCADE"), nullable=False, index=True ) name: Mapped[str] = mapped_column(String(255), nullable=False) distribution: Mapped[DistributionType] = mapped_column( SQLEnum(DistributionType), nullable=False ) mean_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) standard_deviation: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) minimum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) maximum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) unit: Mapped[str | None] = mapped_column(String(32), nullable=True) metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() ) scenario: Mapped["Scenario"] = relationship( "Scenario", back_populates="simulation_parameters" ) def __repr__(self) -> str: # pragma: no cover return ( f"SimulationParameter(id={self.id!r}, scenario_id={self.scenario_id!r}, " f"name={self.name!r})" )