repository is now a presentation-ready development prototype

This commit is contained in:
2026-07-21 15:40:44 -06:00
parent 640165649d
commit 88161a6f16
24 changed files with 1989 additions and 946 deletions
+28 -79
View File
@@ -6,15 +6,14 @@ import {
renderAgeRisk,
renderCohortDotPlot,
renderCoverageHeatmap,
renderNoCalibration,
renderOutcomeTrend,
renderUtahCoverage,
} from "./charts.js";
const ROUTES = Object.freeze({
overview: "Overview",
reliability: "Reliability explorer",
estimator: "Next-test estimator",
cohorts: "Sample cohorts",
model: "Model & benchmark",
methods: "Data & methods",
});
@@ -66,13 +65,6 @@ function normalizeCounty(value) {
return String(value).trim().toLowerCase().replaceAll("_", " ").replace(/\s+/g, " ");
}
function displayCategory(value) {
return String(value)
.replaceAll("_", " ")
.toLowerCase()
.replace(/\b\w/g, (letter) => letter.toUpperCase());
}
function routeFromHash() {
const candidate = window.location.hash.replace(/^#/, "").toLowerCase();
return Object.hasOwn(ROUTES, candidate) ? candidate : "overview";
@@ -120,7 +112,7 @@ function setPreviewBanner(manifest) {
setText("preview-title", "Development preview");
setText(
"preview-copy",
"These suppressed sample aggregates are not population estimates and must not be used for individual decisions.",
"Private 10,000-vehicle development sample · suppressed aggregates only · not population estimates.",
);
}
@@ -144,25 +136,6 @@ function setUnavailable(error) {
setHeaderStatus("error", "Approved data unavailable");
}
function populateSelect(id, values, formatter = displayCategory) {
const select = byId(id);
if (!select) return;
for (const value of values) {
const option = document.createElement("option");
option.value = String(value);
option.textContent = formatter(value);
select.append(option);
}
}
function disableUnsupportedFilter(id, explanation) {
const select = byId(id);
if (!select) return;
select.disabled = true;
select.title = explanation;
select.options[0].textContent = explanation;
}
function renderCountyList(coveredCounties) {
const container = byId("county-coverage-list");
container.replaceChildren();
@@ -195,12 +168,12 @@ function renderOverview(data) {
setText("data-cutoff", latest ? `${latest.year} Q${latest.quarter}` : "Unavailable");
setText(
"model-version",
`Release ${data.manifest.release_id.slice(0, 8)} · ${data.manifest.model_versions.join(" / ")}`,
`Final: calibrated logistic · release ${data.manifest.release_id.slice(0, 8)}`,
);
renderOutcomeTrend(byId("outcome-trend-chart"), rows);
setText(
"outcome-trend-note",
"The current approved bundle publishes binary non-pass rates only. Four-class outcome mix and blank rates are not inferred or displayed.",
"Development-sample aggregates only, not a population trend. The bundle publishes binary non-pass rates; four-class outcome mix and blank rates are not inferred.",
);
renderAgeRisk(byId("age-risk-chart"), data.ageRisk.rows);
renderUtahCoverage(byId("utah-coverage-map"), counties);
@@ -230,7 +203,7 @@ function renderScorecardCards(rows) {
heading.textContent = scorecardLabel(row);
const meta = document.createElement("div");
meta.className = "cohort-card__meta";
meta.textContent = `${compactNumber(row.support_rounded)} rounded support`;
meta.textContent = `≈${compactNumber(row.support_rounded)} eligible sample episodes (rounded)`;
const bar = document.createElement("div");
bar.className = "risk-bar";
bar.setAttribute("aria-hidden", "true");
@@ -242,7 +215,7 @@ function renderScorecardCards(rows) {
const strong = document.createElement("strong");
strong.textContent = percent(row.nonpass_rate);
const small = document.createElement("small");
small.textContent = "Observed non-pass";
small.textContent = "Observed sample non-pass";
value.append(strong, small);
article.append(heading, meta, bar, value);
container.append(article);
@@ -259,33 +232,14 @@ function updateExplorer() {
visibleScorecards = sortedScorecards(matches, sortMode);
setText(
"result-summary",
`${visibleScorecards.length} supported cohort${visibleScorecards.length === 1 ? "" : "s"}; showing up to 18 cards and 12 chart rows.`,
`${visibleScorecards.length} supported sample cohort${visibleScorecards.length === 1 ? "" : "s"}; showing up to 18 cards and 12 chart rows.`,
);
byId("empty-results").hidden = visibleScorecards.length > 0;
renderCohortDotPlot(byId("cohort-dot-plot"), visibleScorecards);
renderScorecardCards(visibleScorecards);
}
function initializeExplorer(data) {
populateSelect("filter-county", data.filters.public_counties);
populateSelect("filter-age", data.filters.age_bands, (value) => String(value));
populateSelect(
"filter-period",
data.filters.periods.map((period) => period.year),
(value) => String(value),
);
// Current scorecards are make/model aggregates only. These planned controls
// remain visible but disabled so the UI never implies unsupported slicing.
disableUnsupportedFilter("filter-county", "Unavailable at current scorecard grain");
disableUnsupportedFilter("filter-age", "Unavailable at current scorecard grain");
disableUnsupportedFilter("filter-fuel", "Fuel not published in this bundle");
disableUnsupportedFilter("filter-program", "Program not published in this bundle");
disableUnsupportedFilter("filter-period", "Period not published in this scorecard");
const adjusted = document.querySelector('input[name="risk-view"][value="adjusted"]');
adjusted.disabled = true;
adjusted.parentElement.title = "Model-adjusted cohort scorecards are not published.";
function initializeExplorer() {
byId("cohort-search").addEventListener("input", updateExplorer);
byId("result-sort").addEventListener("change", updateExplorer);
byId("reset-filters").addEventListener("click", () => {
@@ -297,42 +251,37 @@ function initializeExplorer(data) {
updateExplorer();
}
function chooseDiagnostic(rows) {
const partitionRank = { calibrate: 3, tune: 2, train: 1 };
return [...rows].sort((left, right) => {
const preferredLeft = left.model === "logistic_platt" ? 1 : 0;
const preferredRight = right.model === "logistic_platt" ? 1 : 0;
return (
preferredRight - preferredLeft ||
(partitionRank[right.partition] ?? 0) - (partitionRank[left.partition] ?? 0)
);
})[0];
function findDiagnostic(rows, model) {
return rows.find((row) => row.model === model && row.partition === "calibrate");
}
function renderModel(data) {
const finalModel = findDiagnostic(data.diagnostics.rows, "logistic_platt");
const benchmark = findDiagnostic(data.diagnostics.rows, "hist_gradient_boosting_platt");
const metric = (row, key) => (row ? row[key].toFixed(3) : "—");
setText("final-ap", metric(finalModel, "average_precision"));
setText("final-roc", metric(finalModel, "roc_auc"));
setText("final-brier", metric(finalModel, "brier"));
setText("benchmark-ap", metric(benchmark, "average_precision"));
setText("benchmark-roc", metric(benchmark, "roc_auc"));
setText("benchmark-brier", metric(benchmark, "brier"));
}
function renderMethods(data) {
renderCoverageHeatmap(byId("coverage-heatmap"), data.coverage.rows);
const diagnostic = chooseDiagnostic(data.diagnostics.rows);
setText(
"diagnostic-scope-label",
diagnostic
? `${diagnostic.partition === "calibrate" ? "Calibration cohort" : "Development diagnostics"} · ${diagnostic.model}`
: "Development diagnostics · model unavailable",
);
setText("diagnostic-ap", diagnostic ? diagnostic.average_precision.toFixed(3) : "—");
setText("diagnostic-brier", diagnostic ? diagnostic.brier.toFixed(3) : "—");
setText("diagnostic-n", "Not published");
renderNoCalibration(byId("calibration-chart"));
}
function renderDashboard(data) {
dashboardData = data;
setPreviewBanner(data.manifest);
renderOverview(data);
initializeExplorer(data);
initializeExplorer();
renderModel(data);
renderMethods(data);
setHeaderStatus(
"ready",
data.manifest.development_preview ? "Validated development aggregates" : "Validated public aggregates",
data.manifest.development_preview ? "Validated sample aggregates" : "Validated public aggregates",
);
}
@@ -349,4 +298,4 @@ async function initialize() {
initialize();
export { displayCategory, normalizeCounty, routeFromHash };
export { normalizeCounty, routeFromHash };