from __future__ import annotations from enum import Enum from sqlalchemy import ForeignKey, Integer, JSON, String, Text, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.types import TypeDecorator from admin_app.db.models.base import AdminTimestampedModel class ToolArtifactStage(str, Enum): GENERATION = "generation" VALIDATION = "validation" class ToolArtifactKind(str, Enum): GENERATION_REQUEST = "generation_request" VALIDATION_REPORT = "validation_report" class ToolArtifactStorageKind(str, Enum): INLINE_JSON = "inline_json" class ToolArtifactStatus(str, Enum): PENDING = "pending" SUCCEEDED = "succeeded" FAILED = "failed" class ToolArtifactEnumType(TypeDecorator): impl = String(40) cache_ok = True def __init__(self, enum_cls: type[Enum], *, length: int = 40): super().__init__(length=length) self.enum_cls = enum_cls @property def python_type(self): return self.enum_cls def process_bind_param(self, value, dialect): if value is None: return None if isinstance(value, self.enum_cls): return value.value return self.enum_cls(str(value).strip().lower()).value def process_result_value(self, value, dialect): if value is None: return None return self.enum_cls(str(value).strip().lower()) class ToolArtifact(AdminTimestampedModel): __tablename__ = "tool_artifacts" __table_args__ = ( UniqueConstraint( "tool_version_id", "artifact_kind", name="uq_tool_artifacts_tool_version_kind", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) artifact_id: Mapped[str] = mapped_column(String(140), unique=True, index=True, nullable=False) draft_id: Mapped[int] = mapped_column( Integer, ForeignKey("tool_drafts.id"), nullable=False, index=True, ) tool_version_id: Mapped[int] = mapped_column( Integer, ForeignKey("tool_versions.id"), nullable=False, index=True, ) tool_name: Mapped[str] = mapped_column(String(64), index=True, nullable=False) version_number: Mapped[int] = mapped_column(Integer, nullable=False) artifact_stage: Mapped[ToolArtifactStage] = mapped_column( ToolArtifactEnumType(ToolArtifactStage), nullable=False, index=True, ) artifact_kind: Mapped[ToolArtifactKind] = mapped_column( ToolArtifactEnumType(ToolArtifactKind), nullable=False, index=True, ) artifact_status: Mapped[ToolArtifactStatus] = mapped_column( ToolArtifactEnumType(ToolArtifactStatus), nullable=False, default=ToolArtifactStatus.PENDING, index=True, ) storage_kind: Mapped[ToolArtifactStorageKind] = mapped_column( ToolArtifactEnumType(ToolArtifactStorageKind), nullable=False, default=ToolArtifactStorageKind.INLINE_JSON, ) summary: Mapped[str] = mapped_column(Text, nullable=False) payload_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict) checksum: Mapped[str | None] = mapped_column(String(64), nullable=True) author_staff_account_id: Mapped[int] = mapped_column( Integer, ForeignKey("staff_accounts.id"), nullable=False, index=True, ) author_display_name: Mapped[str] = mapped_column(String(150), nullable=False)