Skip to content

Commit df3186e

Browse files
dashboard
1 parent 1b92b1c commit df3186e

4 files changed

Lines changed: 52 additions & 10 deletions

File tree

cybench/runs/analysis/index_map_lib.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,35 @@ def country_display_name(cc: str) -> str:
7070
return _COUNTRY_NAMES.get(cc.lower(), cc.upper())
7171

7272

73+
def _version_from_slug(slug: str) -> int:
74+
match = re.search(r"_v(\d+)$", slug, re.IGNORECASE)
75+
return int(match.group(1)) if match else 0
76+
77+
7378
def _horizon_key_from_slug(slug: str) -> str | None:
7479
match = _SLUG_RE.match(slug)
7580
if not match:
7681
return None
7782
hz = match.group(2).lower()
7883
if hz in {"mid", "mid_season"}:
7984
return "mid"
85+
if hz in {"qtr", "quarter_season"}:
86+
return "qtr"
8087
return hz
8188

8289

8390
def group_walk_forward_entries(entries: list[IndexEntry]) -> list[dict[str, Any]]:
8491
"""Group walk-forward index entries by ISO2 country code."""
92+
walk_forward = [
93+
e
94+
for e in entries
95+
if e.kind == "walk_forward" and e.country_code
96+
]
97+
# Higher vN last so it wins when the same country×horizon appears twice.
98+
walk_forward.sort(key=lambda e: _version_from_slug(e.slug))
99+
85100
by_cc: dict[str, dict[str, Any]] = {}
86-
for entry in entries:
87-
if entry.kind != "walk_forward" or not entry.country_code:
88-
continue
101+
for entry in walk_forward:
89102
cc = entry.country_code.upper()
90103
hz = _horizon_key_from_slug(entry.slug)
91104
if hz is None:
@@ -98,6 +111,7 @@ def group_walk_forward_entries(entries: list[IndexEntry]) -> list[dict[str, Any]
98111
"name": country_display_name(cc),
99112
"eos": None,
100113
"mid": None,
114+
"qtr": None,
101115
},
102116
)
103117
row[hz] = entry.href

cybench/runs/viz/index_map_template.html

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,10 @@ <h1>CY-Bench dashboards</h1>
133133
<div class="card">
134134
<div class="toolbar">
135135
<span class="muted">Horizon</span>
136-
<button type="button" data-horizon="both" class="active">Both</button>
136+
<button type="button" data-horizon="all" class="active">All</button>
137+
<button type="button" data-horizon="mid">Mid-season (~50%)</button>
138+
<button type="button" data-horizon="qtr">Late season (~75%)</button>
137139
<button type="button" data-horizon="eos">End of season</button>
138-
<button type="button" data-horizon="mid">Mid-season</button>
139140
</div>
140141
<div id="map-wrap"><p class="muted">Loading map…</p></div>
141142
<div class="legend muted">
@@ -154,7 +155,7 @@ <h3>Select a country</h3>
154155
const DATA = __MAP_DATA_JSON__;
155156
const byCc = new Map(DATA.countries.map(c => [c.cc, c]));
156157
const byMapIso = new Map(DATA.countries.map(c => [c.map_cc || c.cc, c]));
157-
let horizon = "both";
158+
let horizon = "all";
158159
let selectedCc = null;
159160

160161
const topLinks = document.getElementById("top-links");
@@ -170,7 +171,7 @@ <h3>Select a country</h3>
170171
topLinks.innerHTML += `<span class="pill muted">${DATA.n_countries} countries published</span>`;
171172

172173
function hasHorizon(c, hz) {
173-
if (hz === "both") return Boolean(c.eos || c.mid);
174+
if (hz === "all" || hz === "both") return Boolean(c.eos || c.mid || c.qtr);
174175
return Boolean(c[hz]);
175176
}
176177

@@ -190,11 +191,13 @@ <h3>Select a country</h3>
190191
const c = byCc.get(cc);
191192
const eosCls = c.eos ? "" : " disabled";
192193
const midCls = c.mid ? "" : " disabled";
194+
const qtrCls = c.qtr ? "" : " disabled";
193195
el.innerHTML = `
194196
<h3>${c.name}</h3>
195197
<div class="cc">${c.cc}</div>
196-
<a class="dash-link${eosCls}" href="${c.eos || "#"}">End-of-season walk-forward</a>
197-
<a class="dash-link${midCls}" href="${c.mid || "#"}">Mid-season walk-forward</a>`;
198+
<a class="dash-link${midCls}" href="${c.mid || "#"}">Mid-season walk-forward</a>
199+
<a class="dash-link${qtrCls}" href="${c.qtr || "#"}">Late-season walk-forward (~75% observed)</a>
200+
<a class="dash-link${eosCls}" href="${c.eos || "#"}">End-of-season walk-forward</a>`;
198201
}
199202

200203
function selectCountry(cc) {

tests/runs/test_index_map.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,30 @@ def test_group_walk_forward_entries():
5454
assert el["eos"]
5555

5656

57+
def test_group_walk_forward_entries_qtr_and_latest_version():
58+
entries = [
59+
IndexEntry(
60+
href="de_walk_forward_qtr_v1/dashboard.html",
61+
slug="de_walk_forward_qtr_v1",
62+
title="Germany",
63+
subtitle="qtr",
64+
country_code="DE",
65+
kind="walk_forward",
66+
),
67+
IndexEntry(
68+
href="de_walk_forward_qtr_v2/dashboard.html",
69+
slug="de_walk_forward_qtr_v2",
70+
title="Germany",
71+
subtitle="qtr",
72+
country_code="DE",
73+
kind="walk_forward",
74+
),
75+
]
76+
grouped = group_walk_forward_entries(entries)
77+
de = next(r for r in grouped if r["cc"] == "DE")
78+
assert de["qtr"] == "de_walk_forward_qtr_v2/dashboard.html"
79+
80+
5781
def test_export_world_geojson_includes_france(tmp_path: Path):
5882
try:
5983
world_shape_path("110")

tests/runs/test_publish_pipeline.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def test_publish_target_names():
5555
)
5656
assert qtr.batch_name == "baselines_DE_qtr_v2"
5757
assert qtr.collect_dir == Path("/tmp/output/paper_walk_forward_de_qtr_v2")
58-
assert "quarter-season" in qtr.default_title().lower()
58+
assert "late season" in qtr.default_title().lower()
59+
assert "75%" in qtr.default_title()
5960

6061

6162
def test_needs_collect_skips_when_state_matches(tmp_path: Path):

0 commit comments

Comments
 (0)