feat: complete dev_tools module self-containment
Migrate dev_tools module to self-contained structure: - routes/api/ - API endpoints - models/architecture_scan.py - Architecture scan models - models/test_run.py - Test run models - schemas/ - Pydantic schemas - services/ - Business logic services - tasks/ - Celery background tasks - exceptions.py - Module exceptions Updated definition.py with self-contained paths. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
206
app/modules/dev_tools/models/architecture_scan.py
Normal file
206
app/modules/dev_tools/models/architecture_scan.py
Normal file
@@ -0,0 +1,206 @@
|
||||
# app/modules/dev_tools/models/architecture_scan.py
|
||||
"""
|
||||
Architecture Scan Models
|
||||
|
||||
Database models for tracking code quality scans and violations.
|
||||
This is the canonical location - models are re-exported from the legacy location
|
||||
for backward compatibility.
|
||||
"""
|
||||
|
||||
from sqlalchemy import (
|
||||
JSON,
|
||||
Boolean,
|
||||
Column,
|
||||
DateTime,
|
||||
Float,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
String,
|
||||
Text,
|
||||
)
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class ArchitectureScan(Base):
|
||||
"""Represents a single run of a code quality validator"""
|
||||
|
||||
__tablename__ = "architecture_scans"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
timestamp = Column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False, index=True
|
||||
)
|
||||
validator_type = Column(
|
||||
String(20), nullable=False, index=True, default="architecture"
|
||||
) # 'architecture', 'security', 'performance'
|
||||
|
||||
# Background task status fields (harmonized architecture)
|
||||
status = Column(
|
||||
String(30), nullable=False, default="pending", index=True
|
||||
) # 'pending', 'running', 'completed', 'failed', 'completed_with_warnings'
|
||||
started_at = Column(DateTime(timezone=True), nullable=True)
|
||||
completed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
error_message = Column(Text, nullable=True)
|
||||
progress_message = Column(String(255), nullable=True) # Current step description
|
||||
|
||||
# Scan results
|
||||
total_files = Column(Integer, default=0)
|
||||
total_violations = Column(Integer, default=0)
|
||||
errors = Column(Integer, default=0)
|
||||
warnings = Column(Integer, default=0)
|
||||
duration_seconds = Column(Float, default=0.0)
|
||||
triggered_by = Column(String(100)) # 'manual:username', 'scheduled', 'ci/cd'
|
||||
git_commit_hash = Column(String(40))
|
||||
|
||||
# Celery task tracking (optional - for USE_CELERY=true)
|
||||
celery_task_id = Column(String(255), nullable=True, index=True)
|
||||
|
||||
# Relationship to violations
|
||||
violations = relationship(
|
||||
"ArchitectureViolation", back_populates="scan", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ArchitectureScan(id={self.id}, violations={self.total_violations}, errors={self.errors})>"
|
||||
|
||||
|
||||
class ArchitectureViolation(Base):
|
||||
"""Represents a single code quality violation found during a scan"""
|
||||
|
||||
__tablename__ = "architecture_violations"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
scan_id = Column(
|
||||
Integer, ForeignKey("architecture_scans.id"), nullable=False, index=True
|
||||
)
|
||||
validator_type = Column(
|
||||
String(20), nullable=False, index=True, default="architecture"
|
||||
) # 'architecture', 'security', 'performance'
|
||||
rule_id = Column(String(20), nullable=False, index=True) # e.g., 'API-001', 'SEC-001', 'PERF-001'
|
||||
rule_name = Column(String(200), nullable=False)
|
||||
severity = Column(
|
||||
String(10), nullable=False, index=True
|
||||
) # 'error', 'warning', 'info'
|
||||
file_path = Column(String(500), nullable=False, index=True)
|
||||
line_number = Column(Integer, nullable=False)
|
||||
message = Column(Text, nullable=False)
|
||||
context = Column(Text) # Code snippet
|
||||
suggestion = Column(Text)
|
||||
status = Column(
|
||||
String(20), default="open", index=True
|
||||
) # 'open', 'assigned', 'resolved', 'ignored', 'technical_debt'
|
||||
assigned_to = Column(Integer, ForeignKey("users.id"))
|
||||
resolved_at = Column(DateTime(timezone=True))
|
||||
resolved_by = Column(Integer, ForeignKey("users.id"))
|
||||
resolution_note = Column(Text)
|
||||
created_at = Column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
|
||||
# Relationships
|
||||
scan = relationship("ArchitectureScan", back_populates="violations")
|
||||
assigned_user = relationship(
|
||||
"User", foreign_keys=[assigned_to], backref="assigned_violations"
|
||||
)
|
||||
resolver = relationship(
|
||||
"User", foreign_keys=[resolved_by], backref="resolved_violations"
|
||||
)
|
||||
assignments = relationship(
|
||||
"ViolationAssignment", back_populates="violation", cascade="all, delete-orphan"
|
||||
)
|
||||
comments = relationship(
|
||||
"ViolationComment", back_populates="violation", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ArchitectureViolation(id={self.id}, rule={self.rule_id}, file={self.file_path}:{self.line_number})>"
|
||||
|
||||
|
||||
class ArchitectureRule(Base):
|
||||
"""Code quality rules configuration (from YAML with database overrides)"""
|
||||
|
||||
__tablename__ = "architecture_rules"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
rule_id = Column(
|
||||
String(20), unique=True, nullable=False, index=True
|
||||
) # e.g., 'API-001', 'SEC-001', 'PERF-001'
|
||||
validator_type = Column(
|
||||
String(20), nullable=False, index=True, default="architecture"
|
||||
) # 'architecture', 'security', 'performance'
|
||||
category = Column(
|
||||
String(50), nullable=False
|
||||
) # 'api_endpoint', 'service_layer', 'authentication', 'database', etc.
|
||||
name = Column(String(200), nullable=False)
|
||||
description = Column(Text)
|
||||
severity = Column(String(10), nullable=False) # Can override default from YAML
|
||||
enabled = Column(Boolean, default=True, nullable=False)
|
||||
custom_config = Column(JSON) # For rule-specific settings
|
||||
created_at = Column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
updated_at = Column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ArchitectureRule(id={self.rule_id}, name={self.name}, enabled={self.enabled})>"
|
||||
|
||||
|
||||
class ViolationAssignment(Base):
|
||||
"""Tracks assignment of violations to developers"""
|
||||
|
||||
__tablename__ = "violation_assignments"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
violation_id = Column(
|
||||
Integer, ForeignKey("architecture_violations.id"), nullable=False, index=True
|
||||
)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
assigned_at = Column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
assigned_by = Column(Integer, ForeignKey("users.id"))
|
||||
due_date = Column(DateTime(timezone=True))
|
||||
priority = Column(
|
||||
String(10), default="medium"
|
||||
) # 'low', 'medium', 'high', 'critical'
|
||||
|
||||
# Relationships
|
||||
violation = relationship("ArchitectureViolation", back_populates="assignments")
|
||||
user = relationship("User", foreign_keys=[user_id], backref="violation_assignments")
|
||||
assigner = relationship(
|
||||
"User", foreign_keys=[assigned_by], backref="assigned_by_me"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ViolationAssignment(id={self.id}, violation_id={self.violation_id}, user_id={self.user_id})>"
|
||||
|
||||
|
||||
class ViolationComment(Base):
|
||||
"""Comments on violations for collaboration"""
|
||||
|
||||
__tablename__ = "violation_comments"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
violation_id = Column(
|
||||
Integer, ForeignKey("architecture_violations.id"), nullable=False, index=True
|
||||
)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
comment = Column(Text, nullable=False)
|
||||
created_at = Column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
|
||||
# Relationships
|
||||
violation = relationship("ArchitectureViolation", back_populates="comments")
|
||||
user = relationship("User", backref="violation_comments")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ViolationComment(id={self.id}, violation_id={self.violation_id}, user_id={self.user_id})>"
|
||||
Reference in New Issue
Block a user