# [DEF:backend.src.models.task:Module] # # @TIER: TRIVIAL # @SEMANTICS: database, task, record, sqlalchemy, sqlite # @PURPOSE: Defines the database schema for task execution records. # @LAYER: Domain # @RELATION: DEPENDS_ON -> sqlalchemy # # @INVARIANT: All primary keys are UUID strings. # [SECTION: IMPORTS] from sqlalchemy import Column, String, DateTime, JSON, ForeignKey, Text, Integer, Index from sqlalchemy.sql import func from .mapping import Base import uuid # [/SECTION] # [DEF:TaskRecord:Class] # @TIER: TRIVIAL # @PURPOSE: Represents a persistent record of a task execution. class TaskRecord(Base): __tablename__ = "task_records" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) type = Column(String, nullable=False) # e.g., "backup", "migration" status = Column(String, nullable=False) # Enum: "PENDING", "RUNNING", "SUCCESS", "FAILED" environment_id = Column(String, ForeignKey("environments.id"), nullable=True) started_at = Column(DateTime(timezone=True), nullable=True) finished_at = Column(DateTime(timezone=True), nullable=True) logs = Column(JSON, nullable=True) # Store structured logs as JSON (legacy, kept for backward compatibility) error = Column(String, nullable=True) result = Column(JSON, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) params = Column(JSON, nullable=True) # [/DEF:TaskRecord:Class] # [DEF:TaskLogRecord:Class] # @PURPOSE: Represents a single persistent log entry for a task. # @TIER: CRITICAL # @RELATION: DEPENDS_ON -> TaskRecord # @INVARIANT: Each log entry belongs to exactly one task. class TaskLogRecord(Base): __tablename__ = "task_logs" id = Column(Integer, primary_key=True, autoincrement=True) task_id = Column(String, ForeignKey("task_records.id", ondelete="CASCADE"), nullable=False, index=True) timestamp = Column(DateTime(timezone=True), nullable=False, index=True) level = Column(String(16), nullable=False) # INFO, WARNING, ERROR, DEBUG source = Column(String(64), nullable=False, default="system") # plugin, superset_api, git, etc. message = Column(Text, nullable=False) metadata_json = Column(Text, nullable=True) # JSON string for additional metadata # Composite indexes for efficient filtering __table_args__ = ( Index('ix_task_logs_task_timestamp', 'task_id', 'timestamp'), Index('ix_task_logs_task_level', 'task_id', 'level'), Index('ix_task_logs_task_source', 'task_id', 'source'), ) # [/DEF:TaskLogRecord:Class] # [/DEF:backend.src.models.task:Module]