""" JSON Reporter — машинно-читаемый отчёт в формате JSON. Подходит для интеграции с CI/CD конвейерами и внешними инструментами. """ import json import logging from dataclasses import asdict, is_dataclass from datetime import datetime from pathlib import Path from typing import Any from models import AnalysisReport, ModernizationStrategy, RiskLevel logger = logging.getLogger(__name__) def _serialize(obj: Any) -> Any: """Рекурсивный сериализатор для dataclasses, Enum и set.""" if is_dataclass(obj) and not isinstance(obj, type): return {k: _serialize(v) for k, v in asdict(obj).items()} if isinstance(obj, (RiskLevel, ModernizationStrategy)): return obj.name if isinstance(obj, set): return sorted(obj) if isinstance(obj, list): return [_serialize(i) for i in obj] if isinstance(obj, dict): return {k: _serialize(v) for k, v in obj.items()} return obj class JsonReporter: """Формирует JSON-отчёт по результатам анализа.""" def write(self, report: AnalysisReport, output_dir: str) -> str: """ Записывает отчёт в файл report.json внутри output_dir. :returns: путь к созданному файлу """ out = Path(output_dir) out.mkdir(parents=True, exist_ok=True) file_path = out / "report.json" payload = { "generated_at": datetime.now().isoformat(), "project_root": report.project_root, "summary": { "total_files": report.total_files, "total_nloc": report.total_nloc, "total_functions": report.total_functions, "avg_system_ccn": round(report.avg_system_ccn, 2), "system_risk_level": report.system_risk_level.name, "recommended_system_strategy": report.recommended_system_strategy.name, "dependency_cycles_count": len(report.dependency_cycles), }, "dependency_cycles": report.dependency_cycles, "decisions": [ { "module": d.module_name, "risk_level": d.risk_level.name, "strategy": d.strategy.name, "priority": d.priority, "reasons": d.reasons, } for d in report.decisions ], "modules": { name: { "files": mod.files, "language": mod.language, "total_nloc": mod.total_nloc, "total_functions": mod.total_functions, "avg_ccn": round(mod.avg_ccn, 2), "max_ccn": mod.max_ccn, "heavy_functions_count": mod.heavy_functions_count, "heavy_functions_ratio": round(mod.heavy_functions_ratio, 3), "coupling_out": mod.coupling_out, "coupling_in": mod.coupling_in, "instability": round(mod.instability, 3), "has_cycles": mod.has_cycles, "dependencies": sorted(mod.dependencies), "dependents": sorted(mod.dependents), "functions": [ { "name": f.name, "file": f.file, "line": f.line, "ccn": f.ccn, "nloc": f.nloc, "params": f.params, } for f in sorted(mod.functions, key=lambda x: x.ccn, reverse=True) ], } for name, mod in report.modules.items() }, } with open(file_path, "w", encoding="utf-8") as fh: json.dump(payload, fh, ensure_ascii=False, indent=2) logger.info("JSON отчёт записан: %s", file_path) return str(file_path)