121 lines
4.9 KiB
Python
121 lines
4.9 KiB
Python
"""
|
||
Доменные модели инструментария.
|
||
Все типы данных, которыми обмениваются модули системы.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
from dataclasses import dataclass, field
|
||
from enum import Enum, auto
|
||
from pathlib import Path
|
||
from typing import Dict, List, Optional, Set
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Метрики
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@dataclass
|
||
class FunctionMetrics:
|
||
"""Метрики одной функции / метода."""
|
||
name: str
|
||
file: str
|
||
line: int
|
||
ccn: int # Цикломатическая сложность (McCabe)
|
||
nloc: int # Строк кода без комментариев
|
||
params: int # Число параметров
|
||
language: str # "cpp" | "js"
|
||
|
||
@property
|
||
def is_complex(self) -> bool:
|
||
return self.ccn > 10
|
||
|
||
@property
|
||
def is_large(self) -> bool:
|
||
return self.nloc > 50
|
||
|
||
|
||
@dataclass
|
||
class ModuleMetrics:
|
||
"""Агрегированные метрики модуля (директории или файла)."""
|
||
name: str # Имя модуля (путь относительно корня)
|
||
files: List[str] = field(default_factory=list)
|
||
functions: List[FunctionMetrics] = field(default_factory=list)
|
||
language: str = "mixed" # "cpp" | "js" | "mixed"
|
||
|
||
# Агрегированные показатели (заполняются MetricsEngine)
|
||
total_nloc: int = 0
|
||
avg_ccn: float = 0.0
|
||
max_ccn: int = 0
|
||
total_functions: int = 0
|
||
heavy_functions_count: int = 0 # Функций с CCN > порога
|
||
heavy_functions_ratio: float = 0.0
|
||
|
||
# Граф зависимостей (заполняется DependencyAnalyzer)
|
||
coupling_out: int = 0 # Исходящая связность (fan-out)
|
||
coupling_in: int = 0 # Входящая связность (fan-in)
|
||
instability: float = 0.0 # C_out / (C_in + C_out), метрика Р. Мартина
|
||
has_cycles: bool = False # Участвует в циклической зависимости
|
||
|
||
# Зависимости
|
||
dependencies: Set[str] = field(default_factory=set) # Модули, от которых зависит этот
|
||
dependents: Set[str] = field(default_factory=set) # Модули, зависящие от этого
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Классификация риска и стратегия
|
||
# ---------------------------------------------------------------------------
|
||
|
||
class RiskLevel(Enum):
|
||
"""Уровень архитектурного риска модуля."""
|
||
LOW = "low"
|
||
MEDIUM = "medium"
|
||
HIGH = "high"
|
||
CRITICAL = "critical"
|
||
|
||
|
||
class ModernizationStrategy(Enum):
|
||
"""Рекомендуемая стратегия модернизации (Lehman & Belady, 1985)."""
|
||
KEEP = auto() # Оставить без изменений
|
||
REFACTOR = auto() # Локальный рефакторинг (Fowler, 2018)
|
||
REENGINEER = auto() # Реинжиниринг архитектуры
|
||
REPLACE = auto() # Полная замена
|
||
|
||
|
||
@dataclass
|
||
class ModuleDecision:
|
||
"""Решение DecisionEngine по конкретному модулю."""
|
||
module_name: str
|
||
risk_level: RiskLevel
|
||
strategy: ModernizationStrategy
|
||
reasons: List[str] = field(default_factory=list) # Причины в человекочитаемом виде
|
||
priority: int = 0 # Приоритет в плане рефакторинга (1 = высший)
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Итоговый отчёт
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@dataclass
|
||
class AnalysisReport:
|
||
"""Итоговый отчёт по всей системе."""
|
||
project_root: str
|
||
modules: Dict[str, ModuleMetrics] = field(default_factory=dict)
|
||
decisions: List[ModuleDecision] = field(default_factory=list)
|
||
dependency_cycles: List[List[str]] = field(default_factory=list)
|
||
|
||
# Системные агрегаты
|
||
total_files: int = 0
|
||
total_nloc: int = 0
|
||
total_functions: int = 0
|
||
avg_system_ccn: float = 0.0
|
||
system_risk_level: RiskLevel = RiskLevel.LOW
|
||
recommended_system_strategy: ModernizationStrategy = ModernizationStrategy.KEEP
|
||
|
||
@property
|
||
def critical_modules(self) -> List[ModuleDecision]:
|
||
return [d for d in self.decisions if d.risk_level == RiskLevel.CRITICAL]
|
||
|
||
@property
|
||
def high_risk_modules(self) -> List[ModuleDecision]:
|
||
return [d for d in self.decisions if d.risk_level in (RiskLevel.HIGH, RiskLevel.CRITICAL)]
|