Files
orion/app/models/architecture_scan.py
Samir Boulahtit 9db0da25ec feat: implement code quality dashboard with architecture violation tracking
Implement comprehensive code quality dashboard (Phase 2-4) to track and manage
architecture violations found by the validation script.

Backend Implementation:
- Add JSON output support to validate_architecture.py script
- Create CodeQualityService with scan management, violation tracking, and statistics
- Implement REST API endpoints for code quality management:
  * POST /admin/code-quality/scan - trigger new architecture scan
  * GET /admin/code-quality/scans - list scan history
  * GET /admin/code-quality/violations - list violations with filtering/pagination
  * GET /admin/code-quality/violations/{id} - get violation details
  * POST /admin/code-quality/violations/{id}/assign - assign to developer
  * POST /admin/code-quality/violations/{id}/resolve - mark as resolved
  * POST /admin/code-quality/violations/{id}/ignore - mark as ignored
  * POST /admin/code-quality/violations/{id}/comments - add comments
  * GET /admin/code-quality/stats - dashboard statistics
- Fix architecture_scan model imports to use app.core.database

Frontend Implementation:
- Create code quality dashboard page (code-quality-dashboard.html)
  * Summary cards for total violations, errors, warnings, health score
  * Status breakdown (open, assigned, resolved, ignored)
  * Trend visualization for last 7 scans
  * Top violating files list
  * Violations by rule and module
  * Quick action links
- Create violations list page (code-quality-violations.html)
  * Filterable table by severity, status, rule ID, file path
  * Pagination support
  * Violation detail view links
- Add Alpine.js components (code-quality-dashboard.js, code-quality-violations.js)
  * Dashboard state management and scan triggering
  * Violations list with filtering and pagination
  * API integration with authentication
- Add "Code Quality" navigation link in admin sidebar (Developer Tools section)

Routes:
- GET /admin/code-quality - dashboard page
- GET /admin/code-quality/violations - violations list
- GET /admin/code-quality/violations/{id} - violation details

Features:
- Real-time scan execution from UI
- Technical debt score calculation (0-100 scale)
- Violation workflow: open → assigned → resolved/ignored
- Trend tracking across multiple scans
- File and module-level insights
- Assignment system with priorities and due dates
- Collaborative comments on violations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 09:40:14 +01:00

121 lines
5.8 KiB
Python

"""
Architecture Scan Models
Database models for tracking code quality scans and violations
"""
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, Boolean, ForeignKey, JSON
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
class ArchitectureScan(Base):
"""Represents a single run of the architecture 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)
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', 'scheduled', 'ci/cd'
git_commit_hash = Column(String(40))
# 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 architectural 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)
rule_id = Column(String(20), nullable=False, index=True) # e.g., 'API-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):
"""Architecture 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'
category = Column(String(50), nullable=False) # 'api_endpoint', 'service_layer', 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})>"