diff --git a/app/api/v1/admin/code_quality.py b/app/api/v1/admin/code_quality.py index aac57ce2..1228eb3f 100644 --- a/app/api/v1/admin/code_quality.py +++ b/app/api/v1/admin/code_quality.py @@ -4,7 +4,7 @@ RESTful API for code quality validation and violation management Supports multiple validator types: architecture, security, performance """ -from datetime import UTC, datetime +from datetime import datetime from enum import Enum from fastapi import APIRouter, BackgroundTasks, Depends, Query @@ -214,15 +214,10 @@ async def trigger_scan( triggered_by = f"manual:{current_user.username}" for vtype in request.validator_types: - # Create scan record with pending status - scan = ArchitectureScan( - timestamp=datetime.now(UTC), - validator_type=vtype.value, - status="pending", - triggered_by=triggered_by, + # Create scan record with pending status via service + scan = code_quality_service.create_pending_scan( + db, validator_type=vtype.value, triggered_by=triggered_by ) - db.add(scan) - db.flush() # Get scan.id # Queue background task background_tasks.add_task(execute_code_quality_scan, scan.id) @@ -257,7 +252,7 @@ async def get_scan_status( Use this endpoint to poll for scan completion. """ - scan = db.query(ArchitectureScan).filter(ArchitectureScan.id == scan_id).first() + scan = code_quality_service.get_scan_by_id(db, scan_id) if not scan: raise ScanNotFoundException(scan_id) @@ -274,12 +269,7 @@ async def get_running_scans( Returns scans with status 'pending' or 'running'. """ - scans = ( - db.query(ArchitectureScan) - .filter(ArchitectureScan.status.in_(["pending", "running"])) - .order_by(ArchitectureScan.timestamp.desc()) - .all() - ) + scans = code_quality_service.get_running_scans(db) return [_scan_to_response(scan) for scan in scans] diff --git a/app/services/code_quality_service.py b/app/services/code_quality_service.py index 2c8e9abc..d3162e78 100644 --- a/app/services/code_quality_service.py +++ b/app/services/code_quality_service.py @@ -229,6 +229,44 @@ class CodeQualityService: """Get scan by ID""" return db.query(ArchitectureScan).filter(ArchitectureScan.id == scan_id).first() + def create_pending_scan( + self, db: Session, validator_type: str, triggered_by: str + ) -> ArchitectureScan: + """ + Create a new scan record with pending status. + + Args: + db: Database session + validator_type: Type of validator (architecture, security, performance) + triggered_by: Who triggered the scan (e.g., "manual:username") + + Returns: + The created ArchitectureScan record with ID populated + """ + scan = ArchitectureScan( + timestamp=datetime.now(UTC), + validator_type=validator_type, + status="pending", + triggered_by=triggered_by, + ) + db.add(scan) + db.flush() # Get scan.id + return scan + + def get_running_scans(self, db: Session) -> list[ArchitectureScan]: + """ + Get all currently running scans (pending or running status). + + Returns: + List of scans with status 'pending' or 'running', newest first + """ + return ( + db.query(ArchitectureScan) + .filter(ArchitectureScan.status.in_(["pending", "running"])) + .order_by(ArchitectureScan.timestamp.desc()) + .all() + ) + def get_scan_history( self, db: Session, limit: int = 30, validator_type: str = None ) -> list[ArchitectureScan]: diff --git a/app/templates/admin/code-quality-dashboard.html b/app/templates/admin/code-quality-dashboard.html index a27fa127..af59241c 100644 --- a/app/templates/admin/code-quality-dashboard.html +++ b/app/templates/admin/code-quality-dashboard.html @@ -14,7 +14,7 @@ {% block content %} {% call page_header_flex(title='Code Quality Dashboard', subtitle='Unified code quality tracking: architecture, security, and performance') %} {{ refresh_button(variant='secondary') }} - + {# Custom dropdown: disabled state + template switching not supported by macro #}