Initial upload of ai-code-audit-cli project
Some checks failed
Some checks failed
This commit is contained in:
149
src/reporting/formatter.py
Normal file
149
src/reporting/formatter.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""Report formatting for AI Code Audit CLI."""
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from ..cli.options import OutputFormat, ScanOptions
|
||||
from .confidence import ConfidenceScorer
|
||||
from ..core.models import ScanResult, Issue, IssueCategory, SeverityLevel
|
||||
|
||||
|
||||
class ReportFormatter:
|
||||
"""Format scan results into various output formats."""
|
||||
|
||||
def __init__(self, options: ScanOptions):
|
||||
"""Initialize the report formatter."""
|
||||
self.options = options
|
||||
|
||||
def format_json(
|
||||
self, result: ScanResult, confidence_scorer: ConfidenceScorer
|
||||
) -> str:
|
||||
"""Format results as JSON."""
|
||||
score = confidence_scorer.calculate(result)
|
||||
breakdown = confidence_scorer.get_score_breakdown(result)
|
||||
|
||||
report = {
|
||||
"audit_report": {
|
||||
"generated_at": datetime.now().isoformat(),
|
||||
"target_path": result.target_path,
|
||||
"summary": {
|
||||
"files_scanned": result.files_scanned,
|
||||
"total_issues": len(result.issues),
|
||||
"confidence_score": score,
|
||||
"score_grade": confidence_scorer.get_score_grade(score),
|
||||
},
|
||||
"score_breakdown": breakdown,
|
||||
"issues_by_severity": self._count_by_severity(result),
|
||||
"issues_by_category": self._count_by_category(result),
|
||||
"issues": [self._format_issue(issue) for issue in result.issues],
|
||||
"warnings": result.warnings,
|
||||
}
|
||||
}
|
||||
|
||||
return json.dumps(report, indent=2)
|
||||
|
||||
def format_markdown(
|
||||
self, result: ScanResult, confidence_scorer: ConfidenceScorer
|
||||
) -> str:
|
||||
"""Format results as Markdown."""
|
||||
score = confidence_scorer.calculate(result)
|
||||
|
||||
lines = [
|
||||
"# AI Code Audit Report",
|
||||
"",
|
||||
f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
||||
f"**Target:** {result.target_path}",
|
||||
f"**Files Scanned:** {result.files_scanned}",
|
||||
"",
|
||||
"## Summary",
|
||||
"",
|
||||
f"| Metric | Value |",
|
||||
f"|--------|-------|",
|
||||
f"| Confidence Score | **{score}/100** |",
|
||||
f"| Grade | {confidence_scorer.get_score_grade(score)} |",
|
||||
f"| Total Issues | {len(result.issues)} |",
|
||||
f"| Warnings | {len(result.warnings)} |",
|
||||
"",
|
||||
"### Issues by Severity",
|
||||
"",
|
||||
self._markdown_severity_table(result),
|
||||
"",
|
||||
"### Issues by Category",
|
||||
"",
|
||||
self._markdown_category_table(result),
|
||||
"",
|
||||
"## Issues Detail",
|
||||
"",
|
||||
]
|
||||
|
||||
if result.issues:
|
||||
for i, issue in enumerate(result.issues, 1):
|
||||
lines.append(f"### {i}. [{issue.severity.value.upper()}] {issue.category.value}")
|
||||
lines.append(f"- **File:** `{issue.file_path}`")
|
||||
lines.append(f"- **Line:** {issue.line_number}")
|
||||
lines.append(f"- **Message:** {issue.message}")
|
||||
if issue.suggestion:
|
||||
lines.append(f"- **Suggestion:** {issue.suggestion}")
|
||||
lines.append("")
|
||||
else:
|
||||
lines.append("No issues found! Great job!")
|
||||
lines.append("")
|
||||
|
||||
if result.warnings:
|
||||
lines.append("## Warnings")
|
||||
lines.append("")
|
||||
for warning in result.warnings:
|
||||
lines.append(f"- {warning}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("---")
|
||||
lines.append("*Generated by AI Code Audit CLI*")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def _markdown_severity_table(self, result: ScanResult) -> str:
|
||||
"""Generate markdown table for severity counts."""
|
||||
counts = self._count_by_severity(result)
|
||||
table = "| Severity | Count |\n|---------|-------|\n"
|
||||
for severity in SeverityLevel:
|
||||
count = counts.get(severity.value, 0)
|
||||
table += f"| {severity.value.capitalize()} | {count} |\n"
|
||||
return table
|
||||
|
||||
def _markdown_category_table(self, result: ScanResult) -> str:
|
||||
"""Generate markdown table for category counts."""
|
||||
counts = self._count_by_category(result)
|
||||
table = "| Category | Count |\n|---------|-------|\n"
|
||||
for category in IssueCategory:
|
||||
count = counts.get(category.value, 0)
|
||||
table += f"| {category.value.replace('_', ' ').title()} | {count} |\n"
|
||||
return table
|
||||
|
||||
def _count_by_severity(self, result: ScanResult) -> dict:
|
||||
"""Count issues by severity."""
|
||||
counts = {}
|
||||
for issue in result.issues:
|
||||
sev = issue.severity.value
|
||||
counts[sev] = counts.get(sev, 0) + 1
|
||||
return counts
|
||||
|
||||
def _count_by_category(self, result: ScanResult) -> dict:
|
||||
"""Count issues by category."""
|
||||
counts = {}
|
||||
for issue in result.issues:
|
||||
cat = issue.category.value
|
||||
counts[cat] = counts.get(cat, 0) + 1
|
||||
return counts
|
||||
|
||||
def _format_issue(self, issue: Issue) -> dict:
|
||||
"""Format an issue for JSON output."""
|
||||
return {
|
||||
"severity": issue.severity.value,
|
||||
"category": issue.category.value,
|
||||
"file_path": issue.file_path,
|
||||
"line_number": issue.line_number,
|
||||
"message": issue.message,
|
||||
"suggestion": issue.suggestion,
|
||||
"scanner": issue.scanner_name,
|
||||
}
|
||||
Reference in New Issue
Block a user