From cd1e8bd59141316c09a27a49dfe2d8947295501f Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 4 Sep 2026 17:50:47 +0000
Subject: [PATCH 1/2] Make browser summary reports single-file
Co-authored-by: uriahf <11351434+uriahf@users.noreply.github.com>
---
src/rtichoke/_report_browser.py | 7 +++---
tests/test_calibration_v2.py | 11 ++++++++-
tests/test_report_browser.py | 35 +++++++++++++++++++++++++---
tests/test_summary_report_browser.py | 16 +++++++++----
4 files changed, 58 insertions(+), 11 deletions(-)
diff --git a/src/rtichoke/_report_browser.py b/src/rtichoke/_report_browser.py
index d3d628db..a4441022 100644
--- a/src/rtichoke/_report_browser.py
+++ b/src/rtichoke/_report_browser.py
@@ -33,20 +33,21 @@ def write_html(self, path: str | Path) -> Path:
output.parent.mkdir(parents=True, exist_ok=True)
vendor = files("rtichoke").joinpath("_vendor", "rtichoke_viz")
- for asset in ("rtichoke-viz.js", "rtichoke-viz.css"):
- (output.parent / asset).write_bytes(vendor.joinpath(asset).read_bytes())
sanitized_spec = _sanitize_nan_values(self.spec)
spec_json = json.dumps(sanitized_spec, separators=(",", ":")).replace(
"", "<\\/"
)
viz_js = vendor.joinpath("rtichoke-viz.js").read_text(encoding="utf-8")
+ viz_css = vendor.joinpath("rtichoke-viz.css").read_text(encoding="utf-8")
html = f"""
-
+
rtichoke report
diff --git a/tests/test_calibration_v2.py b/tests/test_calibration_v2.py
index 22d8d094..9abf80b8 100644
--- a/tests/test_calibration_v2.py
+++ b/tests/test_calibration_v2.py
@@ -17,6 +17,8 @@
create_calibration_curve,
)
from rtichoke.performance_data.performance_data import prepare_performance_data
+from importlib.resources import files
+
from rtichoke.processing.evaluation_semantics import (
_SHARED_POPULATION,
_build_evaluation_metadata,
@@ -280,7 +282,9 @@ def test_real_performance_table_roc_calibration_report_uses_shared_renderer(tmp_
tmp_path / "report.html"
)
html = output.read_text(encoding="utf-8")
- viz_js = (tmp_path / "rtichoke-viz.js").read_text(encoding="utf-8")
+ vendor = files("rtichoke").joinpath("_vendor", "rtichoke_viz")
+ viz_js = vendor.joinpath("rtichoke-viz.js").read_text(encoding="utf-8")
+ viz_css = vendor.joinpath("rtichoke-viz.css").read_text(encoding="utf-8")
assert [item["id"] for item in report["sections"][0]["items"]] == [
"performance-table",
@@ -291,6 +295,11 @@ def test_real_performance_table_roc_calibration_report_uses_shared_renderer(tmp_
assert _embedded_report(html) == report
assert 'import { renderReport } from "./rtichoke-viz.js";' not in html
assert viz_js in html
+ assert viz_css in html
+ assert '' not in html
+ assert [f.name for f in sorted(tmp_path.iterdir())] == ["report.html"]
+ assert not (tmp_path / "rtichoke-viz.js").exists()
+ assert not (tmp_path / "rtichoke-viz.css").exists()
assert 'sectionGroupPresentation: "tabs"' in html
diff --git a/tests/test_report_browser.py b/tests/test_report_browser.py
index ca41d2e5..9ed6d997 100644
--- a/tests/test_report_browser.py
+++ b/tests/test_report_browser.py
@@ -1,4 +1,5 @@
import json
+from importlib.resources import files
from typing import Any, cast
import numpy as np
@@ -54,10 +55,14 @@ def test_browser_report_uses_real_producers_and_only_shared_render_report(tmp_pa
tmp_path / "report.html"
)
html = output.read_text(encoding="utf-8")
- viz_js = (tmp_path / "rtichoke-viz.js").read_text(encoding="utf-8")
+ vendor = files("rtichoke").joinpath("_vendor", "rtichoke_viz")
+ viz_js = vendor.joinpath("rtichoke-viz.js").read_text(encoding="utf-8")
+ viz_css = vendor.joinpath("rtichoke-viz.css").read_text(encoding="utf-8")
assert 'import { renderReport } from "./rtichoke-viz.js";' not in html
assert viz_js in html
+ assert viz_css in html
+ assert '' not in html
assert 'sectionGroupPresentation: "tabs"' in html
assert 'groupPresentation: "stacked"' in html
assert _embedded_report(html) == report
@@ -66,8 +71,9 @@ def test_browser_report_uses_real_producers_and_only_shared_render_report(tmp_pa
"roc",
"gains",
]
- assert (tmp_path / "rtichoke-viz.js").exists()
- assert (tmp_path / "rtichoke-viz.css").exists()
+ assert [f.name for f in sorted(tmp_path.iterdir())] == ["report.html"]
+ assert not (tmp_path / "rtichoke-viz.js").exists()
+ assert not (tmp_path / "rtichoke-viz.css").exists()
def test_browser_report_preserves_component_local_evaluation_ids_and_time_context(
@@ -126,3 +132,26 @@ def test_browser_report_preserves_component_local_evaluation_ids_and_time_contex
tmp_path / "time-report.html"
)
assert _embedded_report(output.read_text(encoding="utf-8")) == report
+
+
+def test_browser_report_does_not_delete_or_overwrite_preexisting_assets(tmp_path):
+ existing_js = tmp_path / "rtichoke-viz.js"
+ existing_css = tmp_path / "rtichoke-viz.css"
+ existing_js.write_text("// custom preexisting js", encoding="utf-8")
+ existing_css.write_text("/* custom preexisting css */", encoding="utf-8")
+
+ probs = {"Model A": np.array([0.05, 0.2, 0.7, 0.95])}
+ reals = np.array([0, 0, 1, 1])
+ performance_data = prepare_performance_data(probs, reals, by=0.25)
+ metadata = _build_evaluation_metadata(probs, reals, np.array([]))
+ table = _performance_table_spec_from_performance_data(performance_data, metadata)
+ report = _build_report_spec_v11(
+ [{"id": "sec-1", "components": [{"id": "performance-table", "spec": table}]}]
+ )
+
+ RtichokeBrowserReport(cast(dict[str, Any], report)).write_html(
+ tmp_path / "report.html"
+ )
+
+ assert existing_js.read_text(encoding="utf-8") == "// custom preexisting js"
+ assert existing_css.read_text(encoding="utf-8") == "/* custom preexisting css */"
diff --git a/tests/test_summary_report_browser.py b/tests/test_summary_report_browser.py
index 1586f0af..968a5608 100644
--- a/tests/test_summary_report_browser.py
+++ b/tests/test_summary_report_browser.py
@@ -17,6 +17,8 @@
from rtichoke._report_browser import RtichokeBrowserReport
from rtichoke._report_spec import _build_report_spec_v11
from rtichoke.performance_data.performance_data import prepare_performance_data
+from importlib.resources import files
+
from rtichoke.processing.evaluation_semantics import _build_evaluation_metadata
from rtichoke.summary_report import summary_report as summary_report_module
from rtichoke.summary_report.summary_report import create_summary_report
@@ -170,9 +172,9 @@ def test_browser_summary_report_is_opt_in_and_uses_real_canonical_components(
assert result == output
assert output.exists()
- viz_js_path = tmp_path / "rtichoke-viz.js"
- assert viz_js_path.exists()
- assert (tmp_path / "rtichoke-viz.css").exists()
+ assert [f.name for f in sorted(tmp_path.iterdir())] == ["canonical-summary.html"]
+ assert not (tmp_path / "rtichoke-viz.js").exists()
+ assert not (tmp_path / "rtichoke-viz.css").exists()
html = output.read_text(encoding="utf-8")
report = _embedded_report(html)
@@ -187,7 +189,13 @@ def test_browser_summary_report_is_opt_in_and_uses_real_canonical_components(
"performance-table",
]
assert 'import { renderReport } from "./rtichoke-viz.js";' not in html
- assert viz_js_path.read_text(encoding="utf-8") in html
+ vendor = files("rtichoke").joinpath("_vendor", "rtichoke_viz")
+ viz_js = vendor.joinpath("rtichoke-viz.js").read_text(encoding="utf-8")
+ viz_css = vendor.joinpath("rtichoke-viz.css").read_text(encoding="utf-8")
+ assert viz_js in html
+ assert viz_css in html
+ assert '' not in html
+ assert '