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