175 lines
6.6 KiB
Python

"""
SQLAlchemy ORM models for Foxy Dev Team.
"""
import enum
from datetime import datetime, timezone
from typing import Optional, List
from sqlalchemy import (
String, Text, Integer, DateTime, ForeignKey, Enum, JSON
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
# ─── Enums ─────────────────────────────────────────────────────────────────────
class ProjectStatus(str, enum.Enum):
PENDING = "PENDING"
AWAITING_CONDUCTOR = "AWAITING_CONDUCTOR"
CONDUCTOR_RUNNING = "CONDUCTOR_RUNNING"
AWAITING_ARCHITECT = "AWAITING_ARCHITECT"
ARCHITECT_RUNNING = "ARCHITECT_RUNNING"
AWAITING_DEV = "AWAITING_DEV"
DEV_RUNNING = "DEV_RUNNING"
AWAITING_UIUX = "AWAITING_UIUX"
UIUX_RUNNING = "UIUX_RUNNING"
AWAITING_QA = "AWAITING_QA"
QA_RUNNING = "QA_RUNNING"
AWAITING_DEPLOY = "AWAITING_DEPLOY"
DEPLOY_RUNNING = "DEPLOY_RUNNING"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
PAUSED = "PAUSED"
class WorkflowType(str, enum.Enum):
SOFTWARE_DESIGN = "SOFTWARE_DESIGN"
SYSADMIN_DEBUG = "SYSADMIN_DEBUG"
DEVOPS_SETUP = "DEVOPS_SETUP"
SYSADMIN_ADJUST = "SYSADMIN_ADJUST"
class TaskStatus(str, enum.Enum):
PENDING = "PENDING"
IN_PROGRESS = "IN_PROGRESS"
IN_REVIEW = "IN_REVIEW"
REJECTED = "REJECTED"
READY_FOR_DEPLOY = "READY_FOR_DEPLOY"
DONE = "DONE"
BLOCKED = "BLOCKED"
class TaskType(str, enum.Enum):
BACKEND = "BACKEND"
FRONTEND = "FRONTEND"
INFRA = "INFRA"
TEST = "TEST"
DESIGN = "DESIGN"
class TaskPriority(str, enum.Enum):
P1 = "P1"
P2 = "P2"
P3 = "P3"
class AgentExecutionStatus(str, enum.Enum):
RUNNING = "RUNNING"
SUCCESS = "SUCCESS"
FAILED = "FAILED"
TIMEOUT = "TIMEOUT"
def _utcnow() -> datetime:
return datetime.now(timezone.utc)
# ─── Models ────────────────────────────────────────────────────────────────────
class Project(Base):
__tablename__ = "projects"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(200), nullable=False)
slug: Mapped[str] = mapped_column(String(200), unique=True, nullable=False, index=True)
description: Mapped[str] = mapped_column(Text, default="")
status: Mapped[ProjectStatus] = mapped_column(
Enum(ProjectStatus), default=ProjectStatus.PENDING, nullable=False
)
workflow_type: Mapped[WorkflowType] = mapped_column(
Enum(WorkflowType), default=WorkflowType.SOFTWARE_DESIGN, nullable=False
)
test_mode: Mapped[bool] = mapped_column(default=False)
gitea_repo: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
deployment_target: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
)
# Relationships
tasks: Mapped[List["Task"]] = relationship(
back_populates="project", cascade="all, delete-orphan", lazy="selectin"
)
audit_logs: Mapped[List["AuditLog"]] = relationship(
back_populates="project", cascade="all, delete-orphan", lazy="selectin"
)
agent_executions: Mapped[List["AgentExecution"]] = relationship(
back_populates="project", cascade="all, delete-orphan", lazy="selectin"
)
class Task(Base):
__tablename__ = "tasks"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), nullable=False)
task_id: Mapped[str] = mapped_column(String(50), nullable=False) # e.g. TASK-001
type: Mapped[TaskType] = mapped_column(Enum(TaskType), default=TaskType.BACKEND)
title: Mapped[str] = mapped_column(String(500), nullable=False)
priority: Mapped[TaskPriority] = mapped_column(Enum(TaskPriority), default=TaskPriority.P3)
assigned_to: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
status: Mapped[TaskStatus] = mapped_column(
Enum(TaskStatus), default=TaskStatus.PENDING, nullable=False
)
dependencies: Mapped[Optional[dict]] = mapped_column(JSON, default=list)
acceptance_criteria: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
agent_payloads: Mapped[Optional[dict]] = mapped_column(JSON, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
)
# Relationships
project: Mapped["Project"] = relationship(back_populates="tasks")
class AgentExecution(Base):
__tablename__ = "agent_executions"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), nullable=False)
agent_name: Mapped[str] = mapped_column(String(100), nullable=False)
status: Mapped[AgentExecutionStatus] = mapped_column(
Enum(AgentExecutionStatus), default=AgentExecutionStatus.RUNNING
)
pid: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
finished_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
exit_code: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
error_output: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Relationships
project: Mapped["Project"] = relationship(back_populates="agent_executions")
class AuditLog(Base):
__tablename__ = "audit_logs"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), nullable=False)
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
agent: Mapped[str] = mapped_column(String(100), nullable=False)
action: Mapped[str] = mapped_column(String(100), nullable=False)
target: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
source: Mapped[str] = mapped_column(String(100), default="api")
# Relationships
project: Mapped["Project"] = relationship(back_populates="audit_logs")