Milestone: open/freeze/undergoing/completed/closed (was open/pending/deferred/progressing/closed) Task: open/pending/undergoing/completed/closed (was open/pending/progressing/closed) - Add MilestoneStatusEnum to schemas with typed validation - Add started_at field to Milestone model - Update all router/CLI references from progressing->undergoing - Add completed status handling in task transition logic
104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, Boolean, JSON, Time
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.sql import func
|
|
from app.core.config import Base
|
|
from app.models.role_permission import Role
|
|
import enum
|
|
|
|
|
|
class TaskType(str, enum.Enum):
|
|
"""Task type enum — 'issue' is a subtype of task, not the other way around."""
|
|
ISSUE = "issue"
|
|
MAINTENANCE = "maintenance"
|
|
RESEARCH = "research"
|
|
REVIEW = "review"
|
|
STORY = "story"
|
|
TEST = "test"
|
|
RESOLUTION = "resolution"
|
|
TASK = "task"
|
|
|
|
|
|
class TaskStatus(str, enum.Enum):
|
|
OPEN = "open"
|
|
PENDING = "pending"
|
|
UNDERGOING = "undergoing"
|
|
COMPLETED = "completed"
|
|
CLOSED = "closed"
|
|
|
|
|
|
class TaskPriority(str, enum.Enum):
|
|
LOW = "low"
|
|
MEDIUM = "medium"
|
|
HIGH = "high"
|
|
CRITICAL = "critical"
|
|
|
|
|
|
class Comment(Base):
|
|
__tablename__ = "comments"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
content = Column(Text, nullable=False)
|
|
task_id = Column(Integer, ForeignKey("tasks.id"), nullable=False)
|
|
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
author = relationship("User", back_populates="comments")
|
|
|
|
|
|
class Project(Base):
|
|
__tablename__ = "projects"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
name = Column(String(100), unique=True, nullable=False)
|
|
project_code = Column(String(16), unique=True, index=True, nullable=True)
|
|
owner_name = Column(String(128), nullable=False)
|
|
sub_projects = Column(String(512), nullable=True)
|
|
related_projects = Column(String(512), nullable=True)
|
|
repo = Column(String(512), nullable=True)
|
|
description = Column(Text, nullable=True)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
|
|
members = relationship("ProjectMember", back_populates="project", cascade="all, delete-orphan")
|
|
owner = relationship("User", back_populates="owned_projects")
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
username = Column(String(50), unique=True, nullable=False)
|
|
email = Column(String(100), unique=True, nullable=False)
|
|
hashed_password = Column(String(255), nullable=True)
|
|
full_name = Column(String(100), nullable=True)
|
|
is_active = Column(Boolean, default=True)
|
|
is_admin = Column(Boolean, default=False)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
owned_projects = relationship("Project", back_populates="owner")
|
|
comments = relationship("Comment", back_populates="author")
|
|
project_memberships = relationship("ProjectMember", back_populates="user")
|
|
|
|
|
|
class ProjectMember(Base):
|
|
__tablename__ = "project_members"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
role_id = Column(Integer, ForeignKey("roles.id"), nullable=False)
|
|
role = relationship("Role")
|
|
|
|
project = relationship("Project", back_populates="members")
|
|
user = relationship("User", back_populates="project_memberships")
|
|
|
|
|
|
class ProjectCodeCounter(Base):
|
|
__tablename__ = "project_code_counters"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
prefix = Column(String(16), unique=True, index=True, nullable=False)
|
|
next_value = Column(Integer, default=0)
|