Files
garmin-img-format-parsing/bgtopo_poc/report.py
2026-05-03 21:58:47 +03:00

81 lines
2.6 KiB
Python

from __future__ import annotations
from pathlib import Path
import pandas as pd
from jinja2 import Template
from .utils import ensure_dir
REPORT_TEMPLATE = """
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>BGtopoVJ Blue Box PoC Report</title>
<style>
body { font-family: Arial, sans-serif; max-width: 1200px; margin: 24px auto; line-height: 1.45; }
table { border-collapse: collapse; width: 100%; margin: 16px 0; }
th, td { border: 1px solid #ddd; padding: 8px; font-size: 14px; }
th { background: #f3f3f3; text-align: left; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; }
.card { border: 1px solid #ddd; border-radius: 12px; padding: 12px; }
img { max-width: 100%; border: 1px solid #ccc; }
code { background: #f6f6f6; padding: 2px 4px; }
</style>
</head>
<body>
<h1>BGtopoVJ Blue Rectangle/Square PoC Report</h1>
<p>This is a weak-label mining report. Treat candidates as review targets, not truth.</p>
<h2>Candidate summary</h2>
<table>
<tr><th>Metric</th><th>Value</th></tr>
<tr><td>Total candidates</td><td>{{ total }}</td></tr>
<tr><td>Average score</td><td>{{ avg_score }}</td></tr>
<tr><td>Median score</td><td>{{ med_score }}</td></tr>
</table>
<h2>By inferred fill style</h2>
{{ style_table }}
<h2>QA overlays</h2>
<div class="grid">
{% for overlay in overlays %}
<div class="card">
<p><code>{{ overlay.name }}</code></p>
<img src="{{ overlay.rel }}" />
</div>
{% endfor %}
</div>
</body>
</html>
"""
def build_report(candidate_csvs: list[str | Path], overlays: list[str | Path], out_html: str | Path) -> Path:
frames = [pd.read_csv(p) for p in candidate_csvs if Path(p).exists()]
df = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
out_html = Path(out_html)
ensure_dir(out_html.parent)
style_table = "<p>No candidates.</p>"
if not df.empty:
style_table = df.groupby("fill_style").agg(count=("fill_style", "size"), avg_score=("score", "mean")).reset_index().to_html(index=False)
overlay_items = []
for ov in overlays:
ov = Path(ov)
try:
rel = ov.relative_to(out_html.parent)
except ValueError:
rel = ov
overlay_items.append({"name": ov.name, "rel": str(rel).replace("\\", "/")})
html = Template(REPORT_TEMPLATE).render(
total=0 if df.empty else len(df),
avg_score="" if df.empty else f"{df['score'].mean():.3f}",
med_score="" if df.empty else f"{df['score'].median():.3f}",
style_table=style_table,
overlays=overlay_items,
)
out_html.write_text(html, encoding="utf-8")
return out_html