Files
prop-data-guard/backend/app/models/project.py
T
hiderfong 3b50ccc7e1 feat: dashboard & report APIs with real DB stats
Backend:
- Add /dashboard/stats API (data_sources, tables, columns, labeled, sensitive, projects)
- Add /dashboard/distribution API (level/cat/source distribution, project progress, heatmap)
- Add /reports/stats API (total/auto/manual/reviewed counts + level distribution)
- Fix report download: add template relationship to ClassificationProject
- All stats computed from real DB queries

Frontend:
- Dashboard.vue: replace all hardcoded data with API-driven computed charts
- Report.vue: replace all hardcoded data with API-driven charts
- Add dashboard.ts and report.ts API clients
2026-04-23 11:10:16 +08:00

117 lines
4.7 KiB
Python

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)
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)