"""Task model for the Tasks plugin.""" from __future__ import annotations import uuid from datetime import datetime from typing import Any from sqlalchemy import DateTime, ForeignKey, Index, Integer, String, Text from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.orm import Mapped, mapped_column from app.core.db import Base, TenantMixin from app.models.owned_mixin import OwnedMixin class Task(Base, TenantMixin, OwnedMixin): """Task entity — free activities (calls, notes, visits) linked to entities. Supports polymorphic assignment (user/agent/group), polymorphic entity links (contact/company/...), subtasks, dependencies, goals/milestones and agent subtasks. Legacy ``contact_id`` and ``assigned_to`` columns are kept for backward compatibility and mirrored into the polymorphic fields. """ __tablename__ = "tasks" __table_args__ = ( Index("ix_tasks_tenant_deleted", "tenant_id", "deleted_at"), Index("ix_tasks_tenant_status", "tenant_id", "status"), Index("ix_tasks_tenant_assigned", "tenant_id", "assigned_to"), Index("ix_tasks_tenant_due", "tenant_id", "due_date"), Index("ix_tasks_contact", "contact_id"), Index("ix_tasks_tenant_entity", "tenant_id", "entity_type", "entity_id"), Index("ix_tasks_tenant_assignee", "tenant_id", "assignee_type", "assignee_id"), Index("ix_tasks_tenant_parent", "tenant_id", "parent_task_id"), Index("ix_tasks_tenant_type", "tenant_id", "task_type"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) title: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) status: Mapped[str] = mapped_column( String(20), nullable=False, default="open" ) # open, in_progress, review, blocked, done, cancelled priority: Mapped[str] = mapped_column( String(10), nullable=False, default="medium" ) # low, medium, high, urgent due_date: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) assigned_to: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True ) contact_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("contacts.id", ondelete="SET NULL"), nullable=True ) created_by: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True ) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) # ── F.14 Unified Task System ──────────────────────────────────────────── # Polymorphic assignee (user/agent/group) assignee_type: Mapped[str] = mapped_column( String(20), nullable=False, default="user" ) # user, agent, group assignee_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), nullable=True ) # Polymorphic entity link (contact/company/...) entity_type: Mapped[str | None] = mapped_column(String(80), nullable=True) entity_id: Mapped[uuid.UUID | None] = mapped_column(PGUUID(as_uuid=True), nullable=True) # Polymorphic creator (user/agent/workflow/system) creator_type: Mapped[str] = mapped_column( String(20), nullable=False, default="user" ) # user, agent, workflow, system creator_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), nullable=True ) # Subtasks (self-reference) parent_task_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("tasks.id", ondelete="CASCADE"), nullable=True, ) # Task dependencies (blocked-by task IDs) depends_on: Mapped[list[Any]] = mapped_column(JSONB, nullable=False, default=list) # Task type task_type: Mapped[str] = mapped_column( String(30), nullable=False, default="todo" ) # todo, approval, follow_up, review, goal, milestone, agent_subtask # Goal / Milestone support success_criteria: Mapped[dict[str, Any] | None] = mapped_column(JSONB, nullable=True) target_date: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) progress: Mapped[int] = mapped_column(Integer, nullable=False, default=0) # 0-100