# [DEF:TaskManagerModels:Module] # @TIER: STANDARD # @SEMANTICS: task, models, pydantic, enum, state # @PURPOSE: Defines the data models and enumerations used by the Task Manager. # @LAYER: Core # @RELATION: Used by TaskManager and API routes. # @INVARIANT: Task IDs are immutable once created. # @CONSTRAINT: Must use Pydantic for data validation. # [SECTION: IMPORTS] import uuid from datetime import datetime from enum import Enum from typing import Dict, Any, List, Optional from pydantic import BaseModel, Field # [/SECTION] # [DEF:TaskStatus:Enum] # @TIER: TRIVIAL # @SEMANTICS: task, status, state, enum # @PURPOSE: Defines the possible states a task can be in during its lifecycle. class TaskStatus(str, Enum): PENDING = "PENDING" RUNNING = "RUNNING" SUCCESS = "SUCCESS" FAILED = "FAILED" AWAITING_MAPPING = "AWAITING_MAPPING" AWAITING_INPUT = "AWAITING_INPUT" # [/DEF:TaskStatus:Enum] # [DEF:LogLevel:Enum] # @SEMANTICS: log, level, severity, enum # @PURPOSE: Defines the possible log levels for task logging. # @TIER: STANDARD class LogLevel(str, Enum): DEBUG = "DEBUG" INFO = "INFO" WARNING = "WARNING" ERROR = "ERROR" # [/DEF:LogLevel:Enum] # [DEF:LogEntry:Class] # @SEMANTICS: log, entry, record, pydantic # @PURPOSE: A Pydantic model representing a single, structured log entry associated with a task. # @TIER: CRITICAL # @INVARIANT: Each log entry has a unique timestamp and source. class LogEntry(BaseModel): timestamp: datetime = Field(default_factory=datetime.utcnow) level: str = Field(default="INFO") message: str source: str = Field(default="system") # Component attribution: plugin, superset_api, git, etc. context: Optional[Dict[str, Any]] = None # Legacy field, kept for backward compatibility metadata: Optional[Dict[str, Any]] = None # Structured metadata (e.g., dashboard_id, progress) # [/DEF:LogEntry:Class] # [DEF:TaskLog:Class] # @SEMANTICS: task, log, persistent, pydantic # @PURPOSE: A Pydantic model representing a persisted log entry from the database. # @TIER: STANDARD # @RELATION: MAPS_TO -> TaskLogRecord class TaskLog(BaseModel): id: int task_id: str timestamp: datetime level: str source: str message: str metadata: Optional[Dict[str, Any]] = None class Config: from_attributes = True # [/DEF:TaskLog:Class] # [DEF:LogFilter:Class] # @SEMANTICS: log, filter, query, pydantic # @PURPOSE: Filter parameters for querying task logs. # @TIER: STANDARD class LogFilter(BaseModel): level: Optional[str] = None # Filter by log level source: Optional[str] = None # Filter by source component search: Optional[str] = None # Text search in message offset: int = Field(default=0, ge=0) limit: int = Field(default=100, ge=1, le=1000) # [/DEF:LogFilter:Class] # [DEF:LogStats:Class] # @SEMANTICS: log, stats, aggregation, pydantic # @PURPOSE: Statistics about log entries for a task. # @TIER: STANDARD class LogStats(BaseModel): total_count: int by_level: Dict[str, int] # {"INFO": 10, "ERROR": 2} by_source: Dict[str, int] # {"plugin": 5, "superset_api": 7} # [/DEF:LogStats:Class] # [DEF:Task:Class] # @TIER: STANDARD # @SEMANTICS: task, job, execution, state, pydantic # @PURPOSE: A Pydantic model representing a single execution instance of a plugin, including its status, parameters, and logs. class Task(BaseModel): id: str = Field(default_factory=lambda: str(uuid.uuid4())) plugin_id: str status: TaskStatus = TaskStatus.PENDING started_at: Optional[datetime] = None finished_at: Optional[datetime] = None user_id: Optional[str] = None logs: List[LogEntry] = Field(default_factory=list) params: Dict[str, Any] = Field(default_factory=dict) input_required: bool = False input_request: Optional[Dict[str, Any]] = None # Result payload can be dict/list/scalar depending on plugin and legacy records. result: Optional[Any] = None # [DEF:__init__:Function] # @PURPOSE: Initializes the Task model and validates input_request for AWAITING_INPUT status. # @PRE: If status is AWAITING_INPUT, input_request must be provided. # @POST: Task instance is created or ValueError is raised. # @PARAM: **data - Keyword arguments for model initialization. def __init__(self, **data): super().__init__(**data) if self.status == TaskStatus.AWAITING_INPUT and not self.input_request: raise ValueError("input_request is required when status is AWAITING_INPUT") # [/DEF:__init__:Function] # [/DEF:Task:Class] # [/DEF:TaskManagerModels:Module]