""" Database models for scheduled downloads. """ from sqlalchemy import Column, String, Integer, Boolean, DateTime, Time, ForeignKey, Text from sqlalchemy.orm import relationship from datetime import datetime, time as time_type import uuid import pytz from .database import Base def generate_uuid(): """Generate a UUID string""" return str(uuid.uuid4()) class ScheduledTask(Base): """ Represents a scheduled download task. Tasks run at a specified time each day. """ __tablename__ = "scheduled_tasks" # Primary key id = Column(String(36), primary_key=True, default=generate_uuid) # Task identification name = Column(String(255), nullable=False, index=True) enabled = Column(Boolean, default=True, nullable=False, index=True) # Download configuration source_type = Column(String(20), nullable=False) # "subreddit" or "user" source_name = Column(String(255), nullable=False, index=True) download_mode = Column(String(20), nullable=False) # "download", "archive", "clone" # Filter options limit = Column(Integer, default=25, nullable=False) sort = Column(String(20), default="hot", nullable=False) time_filter = Column(String(20), default="day", nullable=False) # Always "day" for daily tasks min_score = Column(Integer, nullable=True) no_dupes = Column(Boolean, default=True, nullable=False) # Always true for scheduled simple_check = Column(Boolean, default=False, nullable=False) # Scheduling schedule_frequency = Column(String(20), default="daily", nullable=False) run_time = Column(Time, nullable=False) # Time of day to run timezone = Column(String(50), default="UTC", nullable=False) # Metadata created_at = Column(DateTime, default=lambda: datetime.now(pytz.UTC), nullable=False) updated_at = Column(DateTime, default=lambda: datetime.now(pytz.UTC), onupdate=lambda: datetime.now(pytz.UTC), nullable=False) last_run_at = Column(DateTime, nullable=True, index=True) next_run_at = Column(DateTime, nullable=True, index=True) # Authentication auth_state = Column(String(255), nullable=True) # Relationships executions = relationship("TaskExecutionHistory", back_populates="task", cascade="all, delete-orphan") def to_dict(self): """Convert to dictionary for JSON serialization""" return { "id": self.id, "name": self.name, "enabled": self.enabled, "source_type": self.source_type, "source_name": self.source_name, "download_mode": self.download_mode, "limit": self.limit, "sort": self.sort, "time_filter": self.time_filter, "min_score": self.min_score, "no_dupes": self.no_dupes, "simple_check": self.simple_check, "schedule_frequency": self.schedule_frequency, "run_time": self.run_time.isoformat() if self.run_time else None, "timezone": self.timezone, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, "last_run_at": self.last_run_at.isoformat() if self.last_run_at else None, "next_run_at": self.next_run_at.isoformat() if self.next_run_at else None, "auth_state": self.auth_state } class TaskExecutionHistory(Base): """ Records each execution of a scheduled task. Tracks success/failure and metrics. """ __tablename__ = "task_execution_history" # Primary key id = Column(String(36), primary_key=True, default=generate_uuid) # Foreign key to task task_id = Column(String(36), ForeignKey("scheduled_tasks.id", ondelete="CASCADE"), nullable=False, index=True) # Execution details started_at = Column(DateTime, default=lambda: datetime.now(pytz.UTC), nullable=False, index=True) completed_at = Column(DateTime, nullable=True) status = Column(String(20), default="queued", nullable=False, index=True) # queued, running, success, failed, skipped # Results items_found = Column(Integer, default=0, nullable=False) items_downloaded = Column(Integer, default=0, nullable=False) error_message = Column(Text, nullable=True) # Link to download download_id = Column(String(255), nullable=True, index=True) # Relationships task = relationship("ScheduledTask", back_populates="executions") def to_dict(self): """Convert to dictionary for JSON serialization""" return { "id": self.id, "task_id": self.task_id, "started_at": self.started_at.isoformat() if self.started_at else None, "completed_at": self.completed_at.isoformat() if self.completed_at else None, "status": self.status, "items_found": self.items_found, "items_downloaded": self.items_downloaded, "error_message": self.error_message, "download_id": self.download_id }