refactor(arch): eliminate all cross-module model imports in service layer
Some checks failed
CI / ruff (push) Successful in 9s
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / pytest (push) Has been cancelled

Enforce MOD-025/MOD-026 rules: zero top-level cross-module model imports
remain in any service file. All 66 files migrated using deferred import
patterns (method-body, _get_model() helpers, instance-cached self._Model)
and new cross-module service methods in tenancy. Documentation updated
with Pattern 6 (deferred imports), migration plan marked complete, and
violations status reflects 84→0 service-layer violations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 06:13:15 +01:00
parent e3a52f6536
commit 86e85a98b8
66 changed files with 2242 additions and 1295 deletions

View File

@@ -4,13 +4,16 @@ Background Tasks Service
Service for monitoring background tasks across the system
"""
from __future__ import annotations
from datetime import UTC, datetime
from typing import TYPE_CHECKING
from sqlalchemy import case, desc, func
from sqlalchemy.orm import Session
from app.modules.dev_tools.models import ArchitectureScan, TestRun
from app.modules.marketplace.models import MarketplaceImportJob
if TYPE_CHECKING:
from app.modules.dev_tools.models import ArchitectureScan, TestRun
class BackgroundTasksService:
@@ -18,100 +21,86 @@ class BackgroundTasksService:
def get_import_jobs(
self, db: Session, status: str | None = None, limit: int = 50
) -> list[MarketplaceImportJob]:
) -> list:
"""Get import jobs with optional status filter"""
query = db.query(MarketplaceImportJob)
if status:
query = query.filter(MarketplaceImportJob.status == status)
return query.order_by(desc(MarketplaceImportJob.created_at)).limit(limit).all()
from app.modules.marketplace.services.marketplace_import_job_service import (
marketplace_import_job_service,
)
jobs, _ = marketplace_import_job_service.get_all_import_jobs_paginated(
db, status=status, limit=limit,
)
return jobs
def get_test_runs(
self, db: Session, status: str | None = None, limit: int = 50
) -> list[TestRun]:
"""Get test runs with optional status filter"""
query = db.query(TestRun)
if status:
query = query.filter(TestRun.status == status)
return query.order_by(desc(TestRun.timestamp)).limit(limit).all()
from app.modules.dev_tools.models import TestRun as TestRunModel
def get_running_imports(self, db: Session) -> list[MarketplaceImportJob]:
query = db.query(TestRunModel)
if status:
query = query.filter(TestRunModel.status == status)
return query.order_by(desc(TestRunModel.timestamp)).limit(limit).all()
def get_running_imports(self, db: Session) -> list:
"""Get currently running import jobs"""
return (
db.query(MarketplaceImportJob)
.filter(MarketplaceImportJob.status == "processing")
.all()
from app.modules.marketplace.services.marketplace_import_job_service import (
marketplace_import_job_service,
)
jobs, _ = marketplace_import_job_service.get_all_import_jobs_paginated(
db, status="processing", limit=100,
)
return jobs
def get_running_test_runs(self, db: Session) -> list[TestRun]:
"""Get currently running test runs"""
from app.modules.dev_tools.models import TestRun as TestRunModel
# SVC-005 - Platform-level, TestRuns not store-scoped
return db.query(TestRun).filter(TestRun.status == "running").all() # SVC-005
return db.query(TestRunModel).filter(TestRunModel.status == "running").all() # SVC-005
def get_import_stats(self, db: Session) -> dict:
"""Get import job statistics"""
today_start = datetime.now(UTC).replace(
hour=0, minute=0, second=0, microsecond=0
)
stats = db.query(
func.count(MarketplaceImportJob.id).label("total"),
func.sum(
case((MarketplaceImportJob.status == "processing", 1), else_=0)
).label("running"),
func.sum(
case(
(
MarketplaceImportJob.status.in_(
["completed", "completed_with_errors"]
),
1,
),
else_=0,
)
).label("completed"),
func.sum(
case((MarketplaceImportJob.status == "failed", 1), else_=0)
).label("failed"),
).first()
today_count = (
db.query(func.count(MarketplaceImportJob.id))
.filter(MarketplaceImportJob.created_at >= today_start)
.scalar()
or 0
from app.modules.marketplace.services.marketplace_import_job_service import (
marketplace_import_job_service,
)
stats = marketplace_import_job_service.get_import_job_stats(db)
return {
"total": stats.total or 0,
"running": stats.running or 0,
"completed": stats.completed or 0,
"failed": stats.failed or 0,
"today": today_count,
"total": stats.get("total", 0),
"running": stats.get("processing", 0),
"completed": stats.get("completed", 0),
"failed": stats.get("failed", 0),
"today": stats.get("today", 0),
}
def get_test_run_stats(self, db: Session) -> dict:
"""Get test run statistics"""
from app.modules.dev_tools.models import TestRun as TestRunModel
today_start = datetime.now(UTC).replace(
hour=0, minute=0, second=0, microsecond=0
)
stats = db.query(
func.count(TestRun.id).label("total"),
func.sum(case((TestRun.status == "running", 1), else_=0)).label(
func.count(TestRunModel.id).label("total"),
func.sum(case((TestRunModel.status == "running", 1), else_=0)).label(
"running"
),
func.sum(case((TestRun.status == "passed", 1), else_=0)).label(
func.sum(case((TestRunModel.status == "passed", 1), else_=0)).label(
"completed"
),
func.sum(
case((TestRun.status.in_(["failed", "error"]), 1), else_=0)
case((TestRunModel.status.in_(["failed", "error"]), 1), else_=0)
).label("failed"),
func.avg(TestRun.duration_seconds).label("avg_duration"),
func.avg(TestRunModel.duration_seconds).label("avg_duration"),
).first()
today_count = (
db.query(func.count(TestRun.id))
.filter(TestRun.timestamp >= today_start)
db.query(func.count(TestRunModel.id))
.filter(TestRunModel.timestamp >= today_start)
.scalar()
or 0
)
@@ -129,36 +118,42 @@ class BackgroundTasksService:
self, db: Session, status: str | None = None, limit: int = 50
) -> list[ArchitectureScan]:
"""Get code quality scans with optional status filter"""
query = db.query(ArchitectureScan)
from app.modules.dev_tools.models import ArchitectureScan as ScanModel
query = db.query(ScanModel)
if status:
query = query.filter(ArchitectureScan.status == status)
return query.order_by(desc(ArchitectureScan.timestamp)).limit(limit).all()
query = query.filter(ScanModel.status == status)
return query.order_by(desc(ScanModel.timestamp)).limit(limit).all()
def get_running_scans(self, db: Session) -> list[ArchitectureScan]:
"""Get currently running code quality scans"""
from app.modules.dev_tools.models import ArchitectureScan as ScanModel
return (
db.query(ArchitectureScan)
.filter(ArchitectureScan.status.in_(["pending", "running"]))
db.query(ScanModel)
.filter(ScanModel.status.in_(["pending", "running"]))
.all()
)
def get_scan_stats(self, db: Session) -> dict:
"""Get code quality scan statistics"""
from app.modules.dev_tools.models import ArchitectureScan as ScanModel
today_start = datetime.now(UTC).replace(
hour=0, minute=0, second=0, microsecond=0
)
stats = db.query(
func.count(ArchitectureScan.id).label("total"),
func.count(ScanModel.id).label("total"),
func.sum(
case(
(ArchitectureScan.status.in_(["pending", "running"]), 1), else_=0
(ScanModel.status.in_(["pending", "running"]), 1), else_=0
)
).label("running"),
func.sum(
case(
(
ArchitectureScan.status.in_(
ScanModel.status.in_(
["completed", "completed_with_warnings"]
),
1,
@@ -167,14 +162,14 @@ class BackgroundTasksService:
)
).label("completed"),
func.sum(
case((ArchitectureScan.status == "failed", 1), else_=0)
case((ScanModel.status == "failed", 1), else_=0)
).label("failed"),
func.avg(ArchitectureScan.duration_seconds).label("avg_duration"),
func.avg(ScanModel.duration_seconds).label("avg_duration"),
).first()
today_count = (
db.query(func.count(ArchitectureScan.id))
.filter(ArchitectureScan.timestamp >= today_start)
db.query(func.count(ScanModel.id))
.filter(ScanModel.timestamp >= today_start)
.scalar()
or 0
)