from __future__ import annotations from datetime import datetime from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, JSON, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.db.base import Base, TimestampMixin, UUIDPrimaryKeyMixin class EvaluationIndicator(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "evaluation_indicators" mine_id: Mapped[str | None] = mapped_column( String(32), ForeignKey("mines.id", ondelete="CASCADE"), nullable=True, index=True ) parent_id: Mapped[str | None] = mapped_column( String(32), ForeignKey("evaluation_indicators.id", ondelete="SET NULL"), nullable=True ) code: Mapped[str] = mapped_column(String(64), index=True) name: Mapped[str] = mapped_column(String(128)) dimension: Mapped[str] = mapped_column(String(32), index=True) indicator_type: Mapped[str] = mapped_column(String(32), default="quantitative") unit: Mapped[str] = mapped_column(String(32), default="") formula: Mapped[str] = mapped_column(Text, default="") data_source: Mapped[str] = mapped_column(String(255), default="") weight: Mapped[float] = mapped_column(Float, default=0) min_value: Mapped[float | None] = mapped_column(Float, nullable=True) max_value: Mapped[float | None] = mapped_column(Float, nullable=True) target_value: Mapped[float | None] = mapped_column(Float, nullable=True) weight_method: Mapped[str] = mapped_column(String(32), default="static") enabled: Mapped[bool] = mapped_column(Boolean, default=True) sort_order: Mapped[int] = mapped_column(Integer, default=0) config: Mapped[dict] = mapped_column(JSON, default=dict) class EvaluationRun(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "evaluation_runs" mine_id: Mapped[str] = mapped_column( String(32), ForeignKey("mines.id", ondelete="CASCADE"), index=True ) run_no: Mapped[str] = mapped_column(String(64), unique=True, index=True) period_start: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) period_end: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) status: Mapped[str] = mapped_column(String(32), default="pending", index=True) total_score: Mapped[float | None] = mapped_column(Float, nullable=True) grade: Mapped[str] = mapped_column(String(8), default="") summary: Mapped[str] = mapped_column(Text, default="") recommendations: Mapped[list] = mapped_column(JSON, default=list) weight_snapshot: Mapped[dict] = mapped_column(JSON, default=dict) input_snapshot: Mapped[dict] = mapped_column(JSON, default=dict) model_output: Mapped[dict] = mapped_column(JSON, default=dict) started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_by: Mapped[str] = mapped_column(String(128), default="") class EvaluationScore(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "evaluation_scores" run_id: Mapped[str] = mapped_column( String(32), ForeignKey("evaluation_runs.id", ondelete="CASCADE"), index=True ) indicator_id: Mapped[str] = mapped_column( String(32), ForeignKey("evaluation_indicators.id", ondelete="CASCADE"), index=True ) raw_value: Mapped[float | None] = mapped_column(Float, nullable=True) score: Mapped[float | None] = mapped_column(Float, nullable=True) weighted_score: Mapped[float | None] = mapped_column(Float, nullable=True) weight: Mapped[float | None] = mapped_column(Float, nullable=True) level: Mapped[str] = mapped_column(String(16), default="") evidence: Mapped[str] = mapped_column(Text, default="") metadata_json: Mapped[dict] = mapped_column("metadata", JSON, default=dict)