120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
"""HTML export for quick local review of human reports."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Sequence
|
|
|
|
from .models import PlatformHumanReport, ReportBundle
|
|
from .quality import PlatformQualityReport
|
|
|
|
|
|
def esc(value: object) -> str:
|
|
return (
|
|
str(value)
|
|
.replace("&", "&")
|
|
.replace("<", "<")
|
|
.replace(">", ">")
|
|
.replace('"', """)
|
|
)
|
|
|
|
|
|
def html_shell(title: str, body: str) -> str:
|
|
return f"""<!doctype html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{esc(title)}</title>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; margin: 0; color: #18202a; background: #f8fafc; }}
|
|
header {{ padding: 28px 36px; background: #0f172a; color: #f8fafc; }}
|
|
main {{ padding: 24px 36px 56px; max-width: 1180px; margin: 0 auto; }}
|
|
table {{ border-collapse: collapse; width: 100%; background: white; }}
|
|
th, td {{ border: 1px solid #dbe3ef; padding: 8px 10px; text-align: left; vertical-align: top; }}
|
|
th {{ background: #e8eef7; }}
|
|
.grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }}
|
|
.card {{ background: white; border: 1px solid #dbe3ef; border-radius: 8px; padding: 14px; }}
|
|
.score {{ font-weight: 700; font-size: 22px; }}
|
|
.ok {{ color: #1f7a4d; }}
|
|
.warn {{ color: #a16207; }}
|
|
.blocker {{ color: #b42318; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header><h1>{esc(title)}</h1></header>
|
|
<main>{body}</main>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
def report_card(report: PlatformHumanReport, quality: PlatformQualityReport | None) -> str:
|
|
quality_text = ""
|
|
if quality is not None:
|
|
status_class = "ok" if quality.human_ready else "blocker" if quality.blocker_count else "warn"
|
|
quality_text = (
|
|
f'<p class="{status_class}">technical_ready={quality.technical_ready}; '
|
|
f"human_ready={quality.human_ready}; blockers={quality.blocker_count}; warnings={quality.warning_count}</p>"
|
|
)
|
|
gaps = "".join(f"<li>{esc(gap)}</li>" for gap in report.missing_for_humans[:4])
|
|
return (
|
|
'<section class="card">'
|
|
f"<h2>{esc(report.platform.title)}</h2>"
|
|
f'<div class="score">{report.average_score}</div>'
|
|
f"<p>{esc(report.summary)}</p>"
|
|
+ quality_text
|
|
+ "<ul>"
|
|
+ gaps
|
|
+ "</ul>"
|
|
"</section>"
|
|
)
|
|
|
|
|
|
def index_html(reports: Sequence[PlatformHumanReport], qualities: Sequence[PlatformQualityReport], bundle: ReportBundle | None = None) -> str:
|
|
quality_by_platform = {quality.platform_id: quality for quality in qualities}
|
|
cards = "".join(report_card(report, quality_by_platform.get(report.platform.platform_id)) for report in reports)
|
|
rows = []
|
|
for report in sorted(reports, key=lambda item: item.platform.platform_id):
|
|
quality = quality_by_platform.get(report.platform.platform_id)
|
|
rows.append(
|
|
"<tr>"
|
|
f"<td>{esc(report.platform.platform_id)}</td>"
|
|
f"<td>{report.average_score}</td>"
|
|
f"<td>{report.scan.code_lines}</td>"
|
|
f"<td>{len(report.scan.evidence)}</td>"
|
|
f"<td>{esc(quality.human_ready if quality else 'n/a')}</td>"
|
|
f"<td>{esc('; '.join(report.scan.warnings[:3]))}</td>"
|
|
"</tr>"
|
|
)
|
|
intro = ""
|
|
if bundle is not None:
|
|
intro = (
|
|
f"<p>Gerados {len(bundle.generated_files)} arquivos, "
|
|
f"{bundle.matrix_cells} celulas de matriz e "
|
|
f"{bundle.total_code_lines_analyzed} linhas de codigo analisadas.</p>"
|
|
)
|
|
body = (
|
|
intro
|
|
+ '<section class="grid">'
|
|
+ cards
|
|
+ "</section>"
|
|
+ "<h2>Resumo tabular</h2>"
|
|
+ "<table><thead><tr><th>Plataforma</th><th>Score</th><th>Linhas</th><th>Evidencias</th><th>Human ready</th><th>Avisos</th></tr></thead><tbody>"
|
|
+ "".join(rows)
|
|
+ "</tbody></table>"
|
|
)
|
|
return html_shell("Mais Humana - indice operacional", body)
|
|
|
|
|
|
def write_index_html(
|
|
path: Path,
|
|
reports: Sequence[PlatformHumanReport],
|
|
qualities: Sequence[PlatformQualityReport],
|
|
bundle: ReportBundle | None = None,
|
|
) -> Path:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(index_html(reports, qualities, bundle), encoding="utf-8")
|
|
return path
|
|
|