import enum, uuid from datetime import datetime from sqlalchemy import String, Enum, ForeignKey, Integer, DateTime, JSON from sqlalchemy import LargeBinary, Index from sqlalchemy.orm import relationship, Mapped, mapped_column from sqlalchemy.dialects.postgresql import UUID, TIMESTAMP from sqlalchemy.sql import func from app.db import Base class JobStatus(str, enum.Enum): PENDING = "pending" QUEUED = "queued" RUNNING = "running" SUCCESS = "success" FAILED = "failed" class TaskStatus(str, enum.Enum): PENDING = "pending" QUEUED = "queued" RUNNING = "running" SUCCESS = "success" FAILED = "failed" class Job(Base): __tablename__ = "jobs" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) job_type: Mapped[str] = mapped_column(String(64), index=True) status: Mapped[JobStatus] = mapped_column(Enum(JobStatus), default=JobStatus.PENDING, index=True) domain: Mapped[str] = mapped_column(String(255), index=True) payload: Mapped[dict | None] = mapped_column(JSON, nullable=True) created_at: Mapped[str] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[str] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) tasks: Mapped[list["Task"]] = relationship("Task", back_populates="job", cascade="all, delete-orphan", lazy="selectin") class Task(Base): __tablename__ = "tasks" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) job_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="CASCADE"), index=True) name: Mapped[str] = mapped_column(String(64), index=True) status: Mapped[TaskStatus] = mapped_column(Enum(TaskStatus), default=TaskStatus.PENDING, index=True) routing_key: Mapped[str] = mapped_column(String(128), index=True) retries: Mapped[int] = mapped_column(Integer, default=0) max_retries: Mapped[int] = mapped_column(Integer, default=3) worker_id: Mapped[str | None] = mapped_column(String(64), nullable=True) result: Mapped[dict | None] = mapped_column(JSON, nullable=True) error: Mapped[str | None] = mapped_column(String(1024), nullable=True) created_at: Mapped[str] = mapped_column(DateTime(timezone=True), server_default=func.now()) started_at: Mapped[str | None] = mapped_column(DateTime(timezone=True), nullable=True) finished_at: Mapped[str | None] = mapped_column(DateTime(timezone=True), nullable=True) job: Mapped["Job"] = relationship("Job", back_populates="tasks") class TaskDependency(Base): __tablename__ = "task_dependencies" task_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("tasks.id", ondelete="CASCADE"), primary_key=True) depends_on_task_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("tasks.id", ondelete="CASCADE"), primary_key=True) class Message(Base): __tablename__ = "messages" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) mailbox_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False, index=True) message_id: Mapped[str | None]= mapped_column(String(512), index=True, nullable=True) subject: Mapped[str | None] = mapped_column(String(2048)) from_addr: Mapped[str | None] = mapped_column(String(2048)) to_addr: Mapped[str | None] = mapped_column(String(4096)) date_header: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True), nullable=True) received_at: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=True), default=datetime.utcnow, nullable=False) raw_eml: Mapped[bytes] = mapped_column(LargeBinary, nullable=False) Index("ix_messages_date", Message.date_header) Index("ix_messages_message_id", Message.message_id)