From 97e921f967fe928ae427b8abb3fdd93e5da07173 Mon Sep 17 00:00:00 2001 From: Anay Dhawan Date: Sun, 23 Aug 2026 17:57:43 +0530 Subject: [PATCH] fix: HTML reports drop imbalance/missing/skew meta line and reference section to_html() (faircode/report.py) and buildHtmlReport() (assets/profiler-ui.js) both rendered the group table and flags for each dimension but omitted fields shown everywhere else (terminal output, on-screen web results): - to_html() never rendered the per-dimension imbalance_ratio/missing_pct/ skewness meta line that to_terminal() already builds. Ported the same format into a .meta span next to the dimension score. - buildHtmlReport()'s downloadable HTML report never rendered the reference-baseline section that dimCard() (live on-page results) and faircode/report.py::to_html() already include. Ported the same reference-table rendering and matching CSS. Extends test_to_html_renders_key_figures to assert the meta line appears. Closes #283, closes #272 --- assets/profiler-ui.js | 22 +++++++++++++++++++++- faircode/report.py | 17 ++++++++++++++++- tests/test_report.py | 3 +++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/assets/profiler-ui.js b/assets/profiler-ui.js index 881318f..8da8ca3 100644 --- a/assets/profiler-ui.js +++ b/assets/profiler-ui.js @@ -497,10 +497,26 @@ '' + g.count.toLocaleString() + '' + ''; }).join(''); + + var referenceHtml = ''; + if (d.reference) { + var refRows = d.reference.groups.slice(0, DISPLAY_GROUPS).map(function (g) { + return '' + esc(g.label) + '' + + '' + (g.expected * 100).toFixed(1) + '%' + + '' + (g.actual * 100).toFixed(1) + '%' + + '' + (g.delta > 0 ? '+' : '') + (g.delta * 100).toFixed(1) + ' pp'; + }).join(''); + referenceHtml = '

Reference ' + + 'deviation ' + (d.reference.deviation * 100).toFixed(1) + '%

' + + '' + + '' + + refRows + '
ExpectedActualDelta
'; + } + return '

' + esc(d.name) + ' ' + esc(d.kind) + ' ' + '' + d.dimension_score + '/100

' + - '' + rows + '
'; + '' + rows + '
' + referenceHtml + ''; }).join(''); var flagHtml = ''; @@ -530,6 +546,10 @@ ' tr.under td.bar span { background:var(--accent); }\n' + ' tr.under td:first-child::after { content:\' (under-represented)\'; color:var(--accent); font-size:11px; }\n' + ' tr.small-group td:first-child::before { content:\'⚠ small group \'; color:var(--accent); }\n' + + ' .reference { margin-top:10px; padding-top:10px; border-top:1px dashed var(--border); }\n' + + ' .reference h3 { font-size:.75em; margin:0 0 6px; }\n' + + ' .reference th { text-align:right; font-size:11px; color:var(--muted); font-weight:normal; }\n' + + ' .reference th:first-child { text-align:left; }\n' + ' .flags ul { list-style:none; padding:0; }\n' + ' .flags li { background:#fbeae3; border-left:3px solid var(--accent); padding:8px 12px; margin:6px 0; border-radius:0 4px 4px 0; }\n' + ' .head { border-bottom:2px solid var(--accent); padding-bottom:12px; }\n' + diff --git a/faircode/report.py b/faircode/report.py index 0001326..086b325 100644 --- a/faircode/report.py +++ b/faircode/report.py @@ -197,10 +197,24 @@ def esc(s) -> str: f'{ref_rows}' ) + meta_parts = [] + if d["imbalance_ratio"] is not None: + meta_parts.append(f"imbalance {d['imbalance_ratio']:.1f}x") + elif d["n_groups"] > 1: + meta_parts.append("imbalance inf (empty subgroup)") + if d["missing_pct"] > 0: + meta_parts.append(f"missing {d['missing_pct'] * 100:.1f}%") + if d["skewness"] is not None: + meta_parts.append(f"skew {d['skewness']:+.2f}") + meta_html = ( + f' ({esc(" ".join(meta_parts))})' + if meta_parts else "" + ) + dim_blocks.append( f'

{esc(d["name"])} ' f'{esc(d["kind"])} ' - f'{d["dimension_score"]}/100

' + f'{d["dimension_score"]}/100{meta_html}' f'' f'' f'' @@ -238,6 +252,7 @@ def esc(s) -> str: h1 {{ font-family:Georgia,serif; }} .score {{ color:var(--accent3); font-size:.7em; font-weight:600; }} .kind {{ color:var(--muted); font-size:.6em; text-transform:uppercase; letter-spacing:.08em; }} + .meta {{ color:var(--muted); font-size:.6em; }} .dim {{ background:var(--surface); border:1px solid var(--border); border-radius:8px; padding:16px 20px; margin:16px 0; }} table {{ width:100%; border-collapse:collapse; }} diff --git a/tests/test_report.py b/tests/test_report.py index 9fc70cd..e158271 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -86,6 +86,9 @@ def test_to_html_renders_key_figures(mock_profile_result): assert "88/100" in html_out assert "B+" in html_out assert "Gender" in html_out + assert "imbalance 1.6x" in html_out + assert "missing 2.0%" in html_out + assert "skew +0.30" in html_out def test_to_html_renders_proxy_hints(mock_profile_result):
Group breakdown - {esc(d["name"])}
GroupShare95% CICount