133 lines
4.3 KiB
Python
133 lines
4.3 KiB
Python
"""JSON reporter for machine-readable output."""
|
|
|
|
import json
|
|
|
|
from repohealth.analyzers.risk_analyzer import DiversificationSuggestion, Hotspot
|
|
from repohealth.models.file_stats import FileAnalysis
|
|
from repohealth.models.result import RepositoryResult
|
|
|
|
|
|
class JSONReporter:
|
|
"""Reporter for JSON output."""
|
|
|
|
def __init__(self, indent: int = 2):
|
|
"""Initialize the reporter.
|
|
|
|
Args:
|
|
indent: JSON indentation level.
|
|
"""
|
|
self.indent = indent
|
|
|
|
def generate(self, result: RepositoryResult) -> str:
|
|
"""Generate JSON output from a result.
|
|
|
|
Args:
|
|
result: RepositoryResult to convert.
|
|
|
|
Returns:
|
|
JSON string.
|
|
"""
|
|
output = {
|
|
"version": "1.0",
|
|
"repository": result.repository_path,
|
|
"analyzed_at": result.analyzed_at.isoformat(),
|
|
"files_analyzed": result.files_analyzed,
|
|
"summary": {
|
|
"files_analyzed": result.files_analyzed,
|
|
"total_commits": result.total_commits,
|
|
"unique_authors": result.unique_authors,
|
|
"overall_bus_factor": round(result.overall_bus_factor, 2),
|
|
"gini_coefficient": round(result.gini_coefficient, 3),
|
|
"overall_risk": result.risk_summary.get("overall_risk", "unknown")
|
|
},
|
|
"risk_summary": result.risk_summary,
|
|
"files": result.files,
|
|
"hotspots": result.hotspots,
|
|
"suggestions": result.suggestions,
|
|
"metadata": result.metadata
|
|
}
|
|
|
|
indent = self.indent if self.indent else None
|
|
return json.dumps(output, indent=indent, default=str)
|
|
|
|
def save(self, result: RepositoryResult, file_path: str) -> None:
|
|
"""Save JSON output to a file.
|
|
|
|
Args:
|
|
result: RepositoryResult to save.
|
|
file_path: Path to output file.
|
|
"""
|
|
json_str = self.generate(result)
|
|
|
|
with open(file_path, 'w') as f:
|
|
f.write(json_str)
|
|
|
|
def generate_file_dict(self, analysis: FileAnalysis) -> dict:
|
|
"""Convert a FileAnalysis to a dictionary.
|
|
|
|
Args:
|
|
analysis: FileAnalysis to convert.
|
|
|
|
Returns:
|
|
Dictionary representation.
|
|
"""
|
|
return {
|
|
"path": analysis.path,
|
|
"total_commits": analysis.total_commits,
|
|
"num_authors": analysis.num_authors,
|
|
"author_commits": analysis.author_commits,
|
|
"gini_coefficient": round(analysis.gini_coefficient, 3),
|
|
"bus_factor": round(analysis.bus_factor, 2),
|
|
"risk_level": analysis.risk_level,
|
|
"top_author_share": round(analysis.top_author_share, 3),
|
|
"module": analysis.module,
|
|
"extension": analysis.extension,
|
|
"first_commit": (
|
|
analysis.first_commit.isoformat()
|
|
if analysis.first_commit else None
|
|
),
|
|
"last_commit": (
|
|
analysis.last_commit.isoformat()
|
|
if analysis.last_commit else None
|
|
)
|
|
}
|
|
|
|
def generate_hotspot_dict(self, hotspot: Hotspot) -> dict:
|
|
"""Convert a Hotspot to a dictionary.
|
|
|
|
Args:
|
|
hotspot: Hotspot to convert.
|
|
|
|
Returns:
|
|
Dictionary representation.
|
|
"""
|
|
return {
|
|
"file_path": hotspot.file_path,
|
|
"risk_level": hotspot.risk_level,
|
|
"bus_factor": round(hotspot.bus_factor, 2),
|
|
"top_author": hotspot.top_author,
|
|
"top_author_share": round(hotspot.top_author_share, 3),
|
|
"total_commits": hotspot.total_commits,
|
|
"num_authors": hotspot.num_authors,
|
|
"module": hotspot.module,
|
|
"suggestion": hotspot.suggestion
|
|
}
|
|
|
|
def generate_suggestion_dict(self, suggestion: DiversificationSuggestion) -> dict:
|
|
"""Convert a DiversificationSuggestion to a dictionary.
|
|
|
|
Args:
|
|
suggestion: Suggestion to convert.
|
|
|
|
Returns:
|
|
Dictionary representation.
|
|
"""
|
|
return {
|
|
"file_path": suggestion.file_path,
|
|
"current_author": suggestion.current_author,
|
|
"suggested_authors": suggestion.suggested_authors,
|
|
"priority": suggestion.priority,
|
|
"reason": suggestion.reason,
|
|
"action": suggestion.action
|
|
}
|