from datetime import datetime from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, Float, Enum as SAEnum from sqlalchemy.orm import relationship import enum from app.core.database import Base class ProjectStatus(str, enum.Enum): CREATED = "created" SCANNING = "scanning" ASSIGNING = "assigning" LABELING = "labeling" REVIEWING = "reviewing" ACCEPTING = "accepting" PUBLISHED = "published" class TaskStatus(str, enum.Enum): PENDING = "pending" IN_PROGRESS = "in_progress" COMPLETED = "completed" REJECTED = "rejected" class ResultStatus(str, enum.Enum): AUTO = "auto" MANUAL = "manual" REVIEWED = "reviewed" PUBLISHED = "published" CONFLICT = "conflict" class ClassificationProject(Base): __tablename__ = "classification_project" id = Column(Integer, primary_key=True, index=True) name = Column(String(200), nullable=False) template_id = Column(Integer, ForeignKey("classification_template.id")) description = Column(Text) status = Column(String(20), default=ProjectStatus.CREATED.value) target_source_ids = Column(Text) # comma separated source ids target_database_ids = Column(Text) target_table_ids = Column(Text) planned_start = Column(DateTime) planned_end = Column(DateTime) created_by = Column(Integer, ForeignKey("sys_user.id"), nullable=False) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Async classification tracking celery_task_id = Column(String(100), nullable=True) scan_progress = Column(Text, nullable=True) # JSON: {"scanned": 0, "matched": 0, "total": 0} template = relationship("ClassificationTemplate") tasks = relationship("ClassificationTask", back_populates="project", cascade="all, delete-orphan") results = relationship("ClassificationResult", back_populates="project", cascade="all, delete-orphan") class ClassificationTask(Base): __tablename__ = "classification_task" id = Column(Integer, primary_key=True, index=True) project_id = Column(Integer, ForeignKey("classification_project.id"), nullable=False) name = Column(String(200)) assigner_id = Column(Integer, ForeignKey("sys_user.id")) assignee_id = Column(Integer, ForeignKey("sys_user.id")) target_type = Column(String(20), default="table") # table, column, file target_ids = Column(Text) # comma separated ids status = Column(String(20), default=TaskStatus.PENDING.value) deadline = Column(DateTime) completed_at = Column(DateTime) created_at = Column(DateTime, default=datetime.utcnow) project = relationship("ClassificationProject", back_populates="tasks") assigner = relationship("User", foreign_keys=[assigner_id]) assignee = relationship("User", foreign_keys=[assignee_id]) class ClassificationResult(Base): __tablename__ = "classification_result" id = Column(Integer, primary_key=True, index=True) project_id = Column(Integer, ForeignKey("classification_project.id"), nullable=False) column_id = Column(Integer, ForeignKey("meta_column.id"), nullable=True) file_id = Column(Integer, ForeignKey("unstructured_file.id"), nullable=True) category_id = Column(Integer, ForeignKey("category.id")) level_id = Column(Integer, ForeignKey("data_level.id")) source = Column(String(20), default="auto") # auto, manual, ml confidence = Column(Float, default=0.0) # 0-1 labeler_id = Column(Integer, ForeignKey("sys_user.id")) reviewer_id = Column(Integer, ForeignKey("sys_user.id")) status = Column(String(20), default=ResultStatus.AUTO.value) label_time = Column(DateTime) review_time = Column(DateTime) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) project = relationship("ClassificationProject", back_populates="results") column = relationship("DataColumn") category = relationship("Category") level = relationship("DataLevel") class ClassificationChange(Base): __tablename__ = "classification_change" id = Column(Integer, primary_key=True, index=True) result_id = Column(Integer, ForeignKey("classification_result.id"), nullable=False) change_type = Column(String(20), nullable=False) # category, level, both old_category_id = Column(Integer, ForeignKey("category.id")) new_category_id = Column(Integer, ForeignKey("category.id")) old_level_id = Column(Integer, ForeignKey("data_level.id")) new_level_id = Column(Integer, ForeignKey("data_level.id")) reason = Column(Text) applicant_id = Column(Integer, ForeignKey("sys_user.id")) approver_id = Column(Integer, ForeignKey("sys_user.id")) approval_status = Column(String(20), default="pending") # pending, approved, rejected approval_comment = Column(Text) created_at = Column(DateTime, default=datetime.utcnow)