rnd-v1
This commit is contained in:
80
bgtopo_poc/report.py
Normal file
80
bgtopo_poc/report.py
Normal file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
Reference in New Issue
Block a user