Files
git-insights-cli/src/formatters/html_formatter.py
7000pctAUTO a0a44b93ad
Some checks failed
CI / test (push) Has been cancelled
Add models, analyzers, and formatters
2026-02-01 07:58:05 +00:00

152 lines
6.2 KiB
Python

from datetime import datetime
from typing import Any
from jinja2 import Template
from src.formatters.base import BaseFormatter
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Git Insights Report</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; background: #f5f5f5; }
.container { max-width: 900px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
h1 { color: #333; border-bottom: 2px solid #4a90d9; padding-bottom: 10px; }
h2 { color: #4a90d9; margin-top: 30px; }
.metric { display: inline-block; background: #e8f4fd; padding: 15px 25px; border-radius: 6px; margin: 5px; }
.metric-value { font-size: 28px; font-weight: bold; color: #4a90d9; }
.metric-label { font-size: 12px; color: #666; text-transform: uppercase; }
table { width: 100%; border-collapse: collapse; margin: 15px 0; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; }
th { background: #f8f9fa; font-weight: 600; }
.trend-increasing { color: #28a745; }
.trend-decreasing { color: #dc3545; }
.trend-stable { color: #6c757d; }
.section { margin: 25px 0; padding: 20px; background: #fafafa; border-radius: 6px; }
.timestamp { color: #999; font-size: 14px; }
</style>
</head>
<body>
<div class="container">
<h1>Git Insights Report</h1>
<p class="timestamp">Generated: {{ timestamp }}</p>
{% if commit_analysis %}
<div class="section">
<h2>Commit Analysis</h2>
<div>
<div class="metric">
<div class="metric-value">{{ commit_analysis.total_commits }}</div>
<div class="metric-label">Total Commits</div>
</div>
<div class="metric">
<div class="metric-value">{{ commit_analysis.unique_authors }}</div>
<div class="metric-label">Authors</div>
</div>
<div class="metric">
<div class="metric-value">{{ "%.1f"|format(commit_analysis.average_commits_per_day) }}</div>
<div class="metric-label">Avg/Day</div>
</div>
</div>
<h3>Top Contributors</h3>
<table>
<tr><th>Author</th><th>Commits</th><th>Lines +</th><th>Lines -</th></tr>
{% for author in commit_analysis.top_authors[:5] %}
<tr>
<td>{{ author.name }}</td>
<td>{{ author.commit_count }}</td>
<td style="color: #28a745;">{{ author.lines_added }}</td>
<td style="color: #dc3545;">{{ author.lines_deleted }}</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% if velocity_analysis %}
<div class="section">
<h2>Velocity Analysis</h2>
<div>
<div class="metric">
<div class="metric-value">{{ velocity_analysis.commits_per_day }}</div>
<div class="metric-label">Commits/Day</div>
</div>
<div class="metric">
<div class="metric-value">{{ velocity_analysis.commits_per_week }}</div>
<div class="metric-label">Commits/Week</div>
</div>
<div class="metric">
<div class="metric-value trend-{{ velocity_analysis.velocity_trend }}">{{ velocity_analysis.velocity_trend|capitalize }}</div>
<div class="metric-label">Trend</div>
</div>
</div>
</div>
{% endif %}
{% if code_churn_analysis %}
<div class="section">
<h2>Code Churn</h2>
<div>
<div class="metric">
<div class="metric-value" style="color: #28a745;">+{{ code_churn_analysis.total_lines_added }}</div>
<div class="metric-label">Lines Added</div>
</div>
<div class="metric">
<div class="metric-value" style="color: #dc3545;">-{{ code_churn_analysis.total_lines_deleted }}</div>
<div class="metric-label">Lines Deleted</div>
</div>
<div class="metric">
<div class="metric-value">{{ code_churn_analysis.net_change }}</div>
<div class="metric-label">Net Change</div>
</div>
</div>
</div>
{% endif %}
{% if risky_commit_analysis and risky_commit_analysis.total_risky > 0 %}
<div class="section">
<h2>Risky Commits</h2>
<p>{{ risky_commit_analysis.total_risky }} potentially risky commits detected</p>
{% if risky_commit_analysis.large_commits %}
<h3>Large Commits ({{ risky_commit_analysis.large_commits|length }})</h3>
<table>
<tr><th>SHA</th><th>Author</th><th>Changes</th></tr>
{% for commit in risky_commit_analysis.large_commits[:5] %}
<tr>
<td>{{ commit.sha }}</td>
<td>{{ commit.author_name }}</td>
<td>+{{ commit.additions }} / -{{ commit.deletions }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
{% endif %}
</div>
</body>
</html>
"""
class HTMLFormatter(BaseFormatter):
"""HTML output formatter."""
@staticmethod
def format(data: Any) -> str:
"""Format data as HTML."""
template = Template(HTML_TEMPLATE)
return template.render(
timestamp=datetime.now().isoformat(),
commit_analysis=getattr(data, "commit_analysis", None),
velocity_analysis=getattr(data, "velocity_analysis", None),
code_churn_analysis=getattr(data, "code_churn_analysis", None),
risky_commit_analysis=getattr(data, "risky_commit_analysis", None),
)