arch-researcher/json_reporter.py
2026-04-27 23:44:22 +05:00

107 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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)