Shared rendering primitives for the nightly regression report and the JSON
artifact the dashboard reads back (_reports/{date}/report.json).
This module owns two things:
- the human-label helpers the dashboard and the email both draw with —
severity badges, metric/sample/platform prettifiers, the dashboard deep-link
builder;
- the
to_json/from_json round-trip for the EOS artifact.
The e-group email body (subject, "Needs attention", ranked candidate cards and
the bounded detailed report) is rendered by :mod:k4bench.regression.email,
which imports the helpers below. Keeping the JSON renderer and the dashboard's
imports here — stable — while the email evolves separately is deliberate: the
email is redesigned often, the on-disk report schema almost never.
Ordering vocabulary shared everywhere: hard failures first, then confirmed
regressions (any direction — "regression" here means any confirmed step beyond
the baseline, not just a bad one; none of this is judged good or bad), then a
short WATCH/OK summary — never a row per OK metric.
window_token
window_token(base_release: str | None, onset_release: str | None) -> str
A change window as a compact, URL-safe ?window= value —
2026-06-25..2026-06-27, or ..2026-06-27 when the older end is open.
Shared by the email (which emits the link) and the dashboard's Regressions
tab (which reads it back), so a deep link always selects the window whose
metrics and candidate PRs the mail was talking about. .. rather than the
displayed → keeps the value readable in a URL bar instead of
percent-encoded.
Source code in k4bench/regression/render.py
| def window_token(base_release: str | None, onset_release: str | None) -> str:
"""A change window as a compact, URL-safe ``?window=`` value —
``2026-06-25..2026-06-27``, or ``..2026-06-27`` when the older end is open.
Shared by the email (which emits the link) and the dashboard's Regressions
tab (which reads it back), so a deep link always selects the window whose
metrics and candidate PRs the mail was talking about. ``..`` rather than the
displayed ``→`` keeps the value readable in a URL bar instead of
percent-encoded.
"""
return f"{base_release or ''}..{onset_release or ''}"
|
window_href
window_href(dashboard_url: str | None, *, detector: str, platform: str, sample: str, base_release: str | None, onset_release: str | None, stack: str | None = None, report_night: str = '') -> str | None
The Regressions view scoped to one change window — the link that lands
the reader on exactly the metrics and candidate PRs that window is about,
rather than on whichever window the tab would open by default.
Shared by every renderer that points at a window (the nightly email, the
pull-request comments in :mod:k4bench.blame.comment), so one link shape is
defined once beside the ?window= vocabulary it uses.
Source code in k4bench/regression/render.py
| def window_href(
dashboard_url: str | None,
*,
detector: str,
platform: str,
sample: str,
base_release: str | None,
onset_release: str | None,
stack: str | None = None,
report_night: str = "",
) -> str | None:
"""The Regressions view scoped to one change window — the link that lands
the reader on exactly the metrics and candidate PRs that window is about,
rather than on whichever window the tab would open by default.
Shared by every renderer that points at a window (the nightly email, the
pull-request comments in :mod:`k4bench.blame.comment`), so one link shape is
defined once beside the ``?window=`` vocabulary it uses.
"""
if not dashboard_url or not onset_release:
return None
params = dict(
detector=detector, platform=platform, sample=sample,
window=window_token(base_release, onset_release),
)
if stack:
params["stack"] = stack
if report_night:
params["report"] = report_night
return _dashboard_link(dashboard_url, tab="Regressions", **params)
|
stack_changes_href
stack_changes_href(dashboard_url: str | None, *, detector: str, platform: str, sample: str, base_release: str | None, onset_release: str | None) -> str | None
The Stack Changes view for one exact release window — the package diff
behind a change window, where a reader goes to see what actually moved.
The param names to/from must match what the dashboard's Stack
Changes tab reads back (PARAM_TO/PARAM_FROM in
dashboard/tabs/stack_changes.py) — a literal mismatch here silently
breaks the deep link instead of raising.
Source code in k4bench/regression/render.py
| def stack_changes_href(
dashboard_url: str | None,
*,
detector: str,
platform: str,
sample: str,
base_release: str | None,
onset_release: str | None,
) -> str | None:
"""The Stack Changes view for one exact release window — the package diff
behind a change window, where a reader goes to see what actually moved.
The param names ``to``/``from`` must match what the dashboard's Stack
Changes tab reads back (``PARAM_TO``/``PARAM_FROM`` in
dashboard/tabs/stack_changes.py) — a literal mismatch here silently
breaks the deep link instead of raising.
"""
if not dashboard_url or not onset_release:
return None
params = {
"tab": "Stack Changes",
"detector": detector,
"platform": platform,
"sample": sample,
"to": onset_release,
}
if base_release:
params["from"] = base_release
return _dashboard_link(dashboard_url, **params)
|
regression_href
regression_href(dashboard_url: str | None, *, verdict: MetricVerdict, base_release: str | None, onset_release: str | None) -> str | None
The Stack Changes view for one exact regression — the package diff for
its window with that metric already selected below it, which renders its
trend, its onset and the ranked candidates in the same view.
This is the one destination that answers "did my change do this?" without a
second click, so it is what the pull-request comments point every row at.
Where :func:stack_changes_href opens the range and leaves the reader to
find their metric, this pins it: the reg_* params name a verdict
exactly (see _query_verdict in dashboard/tabs/stack_changes.py), and a
verdict the tab cannot match falls back to the same unpinned range.
Like :func:stack_changes_href, the param names are literals the dashboard
reads back (PARAM_REG_*); a mismatch here loses the selection silently.
Returns None when the verdict carries no onset identity — the tab needs
one to tell two onsets of the same release apart, and a link that would
quietly select the wrong step is worse than the unpinned one.
Source code in k4bench/regression/render.py
| def regression_href(
dashboard_url: str | None,
*,
verdict: MetricVerdict,
base_release: str | None,
onset_release: str | None,
) -> str | None:
"""The Stack Changes view for one exact regression — the package diff for
its window with *that* metric already selected below it, which renders its
trend, its onset and the ranked candidates in the same view.
This is the one destination that answers "did my change do this?" without a
second click, so it is what the pull-request comments point every row at.
Where :func:`stack_changes_href` opens the range and leaves the reader to
find their metric, this pins it: the ``reg_*`` params name a verdict
exactly (see ``_query_verdict`` in dashboard/tabs/stack_changes.py), and a
verdict the tab cannot match falls back to the same unpinned range.
Like :func:`stack_changes_href`, the param names are literals the dashboard
reads back (``PARAM_REG_*``); a mismatch here loses the selection silently.
Returns ``None`` when the verdict carries no onset identity — the tab needs
one to tell two onsets of the same release apart, and a link that would
quietly select the wrong step is worse than the unpinned one.
"""
base = stack_changes_href(
dashboard_url,
detector=verdict.detector, platform=verdict.platform,
sample=verdict.sample,
base_release=base_release, onset_release=onset_release,
)
onset = verdict.onset_run_id or verdict.onset_run_date
if not base or not onset:
return None
params = {
"reg_detector": verdict.detector,
"reg_sample": verdict.sample,
"reg_config": verdict.label,
"reg_metric": verdict.metric,
"reg_onset": onset,
}
if verdict.sub_detector:
params["reg_region"] = verdict.sub_detector
return _dashboard_link(base, **params)
|
to_json
Plain-dict form of the report for json.dump.
The summary block duplicates the convenience properties so the CI
workflow (and the dashboard banner) can gate on the report without
re-deriving anything.
Source code in k4bench/regression/render.py
| def to_json(report: NightlyReport) -> dict:
"""Plain-dict form of the report for ``json.dump``.
The ``summary`` block duplicates the convenience properties so the CI
workflow (and the dashboard banner) can gate on the report without
re-deriving anything.
"""
data = _sanitize(dataclasses.asdict(report))
data["summary"] = {
"report_night": report.report_night,
"n_detectors": len(report.by_detector()),
"n_regressions": len(report.regressions),
"n_new": len(report.new_regressions),
"n_reconfirmed": len(report.reconfirmed_regressions),
"n_watches": len(report.watches),
"n_failures": len(report.failures) + len(report.job_failures),
"has_alertable": report.has_alertable,
}
return data
|
from_json
Rebuild a :class:NightlyReport from :func:to_json output (used by
the dashboard when reading _reports/{date}/report.json off EOS).
Source code in k4bench/regression/render.py
| def from_json(data: dict) -> NightlyReport:
"""Rebuild a :class:`NightlyReport` from :func:`to_json` output (used by
the dashboard when reading ``_reports/{date}/report.json`` off EOS)."""
groups = []
for g in data.get("groups", []):
verdicts = [
MetricVerdict(**{
**{k: val for k, val in v.items() if k in _VERDICT_FIELDS},
"severity": Severity(v["severity"]),
"direction": Direction(v["direction"]),
"history": _history(v.get("history")),
"region_deltas": _region_deltas(v.get("region_deltas")),
})
for v in g.get("verdicts", [])
]
groups.append(RunGroupReport(
detector=g["detector"], platform=g["platform"], sample=g["sample"],
k4h_release=g.get("k4h_release", ""), run_date=g.get("run_date", ""),
run_id=g.get("run_id", ""), verdicts=verdicts,
job_failures=list(g.get("job_failures", [])),
notes=list(g.get("notes", [])),
reliable=g.get("reliable"),
github_run_url=g.get("github_run_url"),
geometry_path=str(g.get("geometry_path") or ""),
))
return NightlyReport(generated_at=data.get("generated_at", ""), groups=groups)
|
to_html
Render the HTML email via :mod:k4bench.regression.email.
New code should import from k4bench.regression.email directly; this
wrapper keeps existing integrations working after the renderer split.
Source code in k4bench/regression/render.py
| def to_html(report: NightlyReport, **kwargs) -> str:
"""Render the HTML email via :mod:`k4bench.regression.email`.
New code should import from ``k4bench.regression.email`` directly; this
wrapper keeps existing integrations working after the renderer split.
"""
from k4bench.regression.email import to_html as render_email_html
return render_email_html(report, **kwargs)
|
to_markdown
Render the text alternative via :mod:k4bench.regression.email.
See :func:to_html for why this compatibility wrapper is lazy.
Source code in k4bench/regression/render.py
| def to_markdown(report: NightlyReport, **kwargs) -> str:
"""Render the text alternative via :mod:`k4bench.regression.email`.
See :func:`to_html` for why this compatibility wrapper is lazy.
"""
from k4bench.regression.email import to_markdown as render_email_markdown
return render_email_markdown(report, **kwargs)
|