various edits
This commit is contained in:
parent
88161a6f16
commit
c1e453d962
@ -57,6 +57,12 @@ contains a denied identifier-like field, the dashboard shows an unavailable
|
|||||||
state and no estimates. Regenerate assets with the repository's private local
|
state and no estimates. Regenerate assets with the repository's private local
|
||||||
pipeline; never hand-edit public JSON to bypass suppression.
|
pipeline; never hand-edit public JSON to bypass suppression.
|
||||||
|
|
||||||
|
Previously published cohort definitions are frozen against cross-release
|
||||||
|
differencing. New canonical model families may be added only when the combined
|
||||||
|
family clears every publication threshold; aliases or trims must not be folded
|
||||||
|
into an existing public cell. Increment `PUBLICATION_POLICY_VERSION` whenever
|
||||||
|
an approved aggregation policy changes.
|
||||||
|
|
||||||
The site intentionally contains no vehicle lookup, individual estimator,
|
The site intentionally contains no vehicle lookup, individual estimator,
|
||||||
operational connection, or row-level prediction output. VINs, plates, ZIPs,
|
operational connection, or row-level prediction output. VINs, plates, ZIPs,
|
||||||
stations, technician identifiers, raw source JSON, credentials, and operational
|
stations, technician identifiers, raw source JSON, credentials, and operational
|
||||||
|
|||||||
@ -126,7 +126,7 @@
|
|||||||
</header>
|
</header>
|
||||||
<div id="utah-coverage-map" class="utah-map" role="img" aria-label="Utah county-feed coverage"></div>
|
<div id="utah-coverage-map" class="utah-map" role="img" aria-label="Utah county-feed coverage"></div>
|
||||||
<div id="county-coverage-list" class="county-list" aria-label="County availability list"></div>
|
<div id="county-coverage-list" class="county-list" aria-label="County availability list"></div>
|
||||||
<p class="chart-note">Availability only—this is not a county outcome ranking. Teal feeds are represented in the sample; gray counties are unavailable, not zero.</p>
|
<p class="chart-note">Availability only—this is not a county outcome ranking. Teal markers show counties represented in the sample; unmarked counties are unavailable, not zero.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="panel">
|
<article class="panel">
|
||||||
@ -177,7 +177,7 @@
|
|||||||
<span>Make or model</span>
|
<span>Make or model</span>
|
||||||
<input id="cohort-search" type="search" autocomplete="off" placeholder="Search supported cohorts">
|
<input id="cohort-search" type="search" autocomplete="off" placeholder="Search supported cohorts">
|
||||||
</label>
|
</label>
|
||||||
<p class="filter-panel__note"><strong>Published grain:</strong> prior make and model only. County, age, fuel, program, and period slices are not available for these scorecards.</p>
|
<p class="filter-panel__note"><strong>Published grain:</strong> prior make and model. <strong>Combined model families:</strong> Chevrolet Silverado 1500; Dodge Ram 1500; Hyundai Elantra; Nissan Altima; Subaru Outback; and Toyota 4Runner, Corolla, and Tacoma. <strong>Exact source-label cohorts:</strong> Ford F150; Honda Accord and Civic; and Toyota Camry. County, age, fuel, program, and period slices are not available for these scorecards.</p>
|
||||||
</form>
|
</form>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
@ -200,7 +200,7 @@
|
|||||||
<div><p class="eyebrow">Descriptive sample comparison</p><h2>Observed cohort associations</h2></div>
|
<div><p class="eyebrow">Descriptive sample comparison</p><h2>Observed cohort associations</h2></div>
|
||||||
</header>
|
</header>
|
||||||
<div id="cohort-dot-plot" class="chart chart--scorecard" role="img" aria-label="Observed development-sample cohort non-pass rates"></div>
|
<div id="cohort-dot-plot" class="chart chart--scorecard" role="img" aria-label="Observed development-sample cohort non-pass rates"></div>
|
||||||
<p class="chart-note">Ordering is a viewing aid within this sample, not a reliability ranking or population comparison.</p>
|
<p class="chart-note">The filter note distinguishes combined model families from exact source-label cohorts. Ordering is a viewing aid within this sample, not a reliability ranking or population comparison.</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<div id="cohort-cards" class="cohort-cards"></div>
|
<div id="cohort-cards" class="cohort-cards"></div>
|
||||||
|
|||||||
@ -230,9 +230,16 @@ function updateExplorer() {
|
|||||||
scorecardLabel(row).toLowerCase().includes(query),
|
scorecardLabel(row).toLowerCase().includes(query),
|
||||||
);
|
);
|
||||||
visibleScorecards = sortedScorecards(matches, sortMode);
|
visibleScorecards = sortedScorecards(matches, sortMode);
|
||||||
|
const cohortCount = visibleScorecards.length;
|
||||||
|
let summary = "0 supported sample cohorts.";
|
||||||
|
if (cohortCount > 0 && cohortCount <= 12) {
|
||||||
|
summary = `${cohortCount} supported sample cohort${cohortCount === 1 ? "" : "s"}; all shown.`;
|
||||||
|
} else if (cohortCount > 12) {
|
||||||
|
summary = `${cohortCount} supported sample cohorts; showing ${Math.min(cohortCount, 18)} cards and 12 chart rows.`;
|
||||||
|
}
|
||||||
setText(
|
setText(
|
||||||
"result-summary",
|
"result-summary",
|
||||||
`${visibleScorecards.length} supported sample cohort${visibleScorecards.length === 1 ? "" : "s"}; showing up to 18 cards and 12 chart rows.`,
|
summary,
|
||||||
);
|
);
|
||||||
byId("empty-results").hidden = visibleScorecards.length > 0;
|
byId("empty-results").hidden = visibleScorecards.length > 0;
|
||||||
renderCohortDotPlot(byId("cohort-dot-plot"), visibleScorecards);
|
renderCohortDotPlot(byId("cohort-dot-plot"), visibleScorecards);
|
||||||
|
|||||||
@ -205,37 +205,50 @@ export function renderCohortDotPlot(container, rows) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simplified from the Census 2025 1:20m cartographic boundary for Utah
|
||||||
|
// (GEOID 49). Utah's defining step is on the northeast edge, not both sides.
|
||||||
|
const UTAH_OUTLINE_PATH = "M44 20H153V67H226V255H44Z";
|
||||||
|
|
||||||
|
// Census county centers projected into the same Utah map frame.
|
||||||
const COUNTY_POINTS = {
|
const COUNTY_POINTS = {
|
||||||
cache: [154, 40],
|
cache: [128, 33],
|
||||||
weber: [137, 86],
|
weber: [123, 54],
|
||||||
davis: [126, 108],
|
davis: [111, 65],
|
||||||
salt_lake: [133, 133],
|
"salt lake": [121, 83],
|
||||||
"salt lake": [133, 133],
|
utah: [131, 108],
|
||||||
utah: [140, 170],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function normalizeCountyName(county) {
|
||||||
|
return String(county).trim().toLowerCase().replaceAll("_", " ").replace(/\s+/g, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayCountyName(county) {
|
||||||
|
return county.replace(/\b\w/g, (letter) => letter.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
export function renderUtahCoverage(container, coveredCounties) {
|
export function renderUtahCoverage(container, coveredCounties) {
|
||||||
const normalized = new Set(coveredCounties.map((county) => county.toLowerCase()));
|
const normalized = new Set(coveredCounties.map(normalizeCountyName));
|
||||||
const points = [...normalized]
|
const points = [...normalized]
|
||||||
.map((county) => ({ county, coordinates: COUNTY_POINTS[county] }))
|
.map((county) => ({ county, coordinates: COUNTY_POINTS[county] }))
|
||||||
.filter((item) => item.coordinates);
|
.filter((item) => item.coordinates);
|
||||||
|
const countyNames = [...normalized].map(displayCountyName);
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<svg viewBox="0 0 270 300" aria-hidden="true" focusable="false">
|
<svg viewBox="0 0 270 300" aria-hidden="true" focusable="false">
|
||||||
<path d="M79 18h101v54l18 18v183H52V116l27-27V18Z" fill="var(--gray-100)" stroke="var(--gray-300)" stroke-width="3"></path>
|
<path data-map-feature="utah-outline" d="${UTAH_OUTLINE_PATH}" fill="var(--gray-100)" stroke="var(--gray-300)" stroke-width="3" stroke-linejoin="round" vector-effect="non-scaling-stroke"></path>
|
||||||
<path d="M80 20h98v53l17 18v179H55V117l25-27V20Z" fill="none" stroke="var(--sand-200)" stroke-width="1.5" stroke-dasharray="4 5"></path>
|
|
||||||
${points
|
${points
|
||||||
.map(
|
.map(
|
||||||
({ county, coordinates }) => `
|
({ county, coordinates }) => `
|
||||||
<circle cx="${coordinates[0]}" cy="${coordinates[1]}" r="8" fill="var(--teal-700)" stroke="var(--paper)" stroke-width="3"><title>${escapeText(county.replace("_", " "))} feed available</title></circle>`,
|
<circle cx="${coordinates[0]}" cy="${coordinates[1]}" r="8" fill="var(--teal-700)" stroke="var(--paper)" stroke-width="3" vector-effect="non-scaling-stroke"><title>${escapeText(displayCountyName(county))} County feed available</title></circle>`,
|
||||||
)
|
)
|
||||||
.join("")}
|
.join("")}
|
||||||
|
<text x="135" y="205" text-anchor="middle" class="state-label">UTAH</text>
|
||||||
<text x="135" y="288" text-anchor="middle" class="axis-label">Participating feeds highlighted</text>
|
<text x="135" y="288" text-anchor="middle" class="axis-label">Participating feeds highlighted</text>
|
||||||
</svg>`;
|
</svg>`;
|
||||||
container.setAttribute(
|
container.setAttribute(
|
||||||
"aria-label",
|
"aria-label",
|
||||||
coveredCounties.length
|
countyNames.length
|
||||||
? `Development-sample feed availability includes ${coveredCounties.join(", ")}. This is not an outcome ranking; other counties are unavailable.`
|
? `Outline of Utah showing development-sample feed availability for ${countyNames.join(", ")} County feeds. This is not an outcome ranking; other counties are unavailable.`
|
||||||
: "No county feed coverage is available.",
|
: "Outline of Utah. No county feed coverage is available.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"development_preview":true,"population_estimate_allowed":false,"rows":[{"nonpass_rate":0.116,"prior_make":"FORD","prior_model":"F150","support_rounded":1100},{"nonpass_rate":0.09,"prior_make":"HONDA","prior_model":"ACCORD","support_rounded":400},{"nonpass_rate":0.073,"prior_make":"HONDA","prior_model":"CIVIC","support_rounded":300},{"nonpass_rate":0.086,"prior_make":"TOYOTA","prior_model":"CAMRY","support_rounded":500}],"schema_version":"dashboard_data_v1"}
|
{"development_preview":true,"population_estimate_allowed":false,"rows":[{"nonpass_rate":0.123,"prior_make":"CHEVROLET","prior_model":"SILVERADO 1500","support_rounded":500},{"nonpass_rate":0.117,"prior_make":"DODGE","prior_model":"RAM 1500","support_rounded":600},{"nonpass_rate":0.116,"prior_make":"FORD","prior_model":"F150","support_rounded":1100},{"nonpass_rate":0.09,"prior_make":"HONDA","prior_model":"ACCORD","support_rounded":400},{"nonpass_rate":0.073,"prior_make":"HONDA","prior_model":"CIVIC","support_rounded":300},{"nonpass_rate":0.094,"prior_make":"HYUNDAI","prior_model":"ELANTRA","support_rounded":500},{"nonpass_rate":0.128,"prior_make":"NISSAN","prior_model":"ALTIMA","support_rounded":500},{"nonpass_rate":0.084,"prior_make":"SUBARU","prior_model":"OUTBACK","support_rounded":600},{"nonpass_rate":0.08,"prior_make":"TOYOTA","prior_model":"4RUNNER","support_rounded":600},{"nonpass_rate":0.086,"prior_make":"TOYOTA","prior_model":"CAMRY","support_rounded":500},{"nonpass_rate":0.082,"prior_make":"TOYOTA","prior_model":"COROLLA","support_rounded":800},{"nonpass_rate":0.08,"prior_make":"TOYOTA","prior_model":"TACOMA","support_rounded":600}],"schema_version":"dashboard_data_v1"}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"assets":["age_risk_curve.json","cohort_scorecard.json","coverage_quality.json","filter_catalog.json","model_diagnostics.json","overview_period_county.json"],"data_scope":{"first_year":2016,"last_year":2024,"model_names":["hist_gradient_boosting_platt","hist_gradient_boosting_raw","logistic_platt","logistic_raw","previous_episode_literal","training_prevalence"],"partitions":["train","tune","calibrate"]},"definitions":{"episode_gap_days":30,"locked_test_metrics_published":false,"support_rounding":100,"suppression_min_distinct_nonpass_vehicles":10,"suppression_min_distinct_pass_vehicles":10,"suppression_min_distinct_vehicles":100,"suppression_min_nonpass":10,"suppression_min_pass":10,"suppression_min_support":100,"target":"first-attempt next-episode binary non-pass rate"},"development_preview":true,"model_versions":["baseline_v1","hist_gradient_boosting_v1"],"population_estimate_allowed":false,"release_id":"7c1af8d22d3841b84d2cf4cd5ba7bce6a43578c78bc3d32a25d200b4b1986704","schema_version":"dashboard_data_v1"}
|
{"assets":["age_risk_curve.json","cohort_scorecard.json","coverage_quality.json","filter_catalog.json","model_diagnostics.json","overview_period_county.json"],"data_scope":{"first_year":2016,"last_year":2024,"model_names":["hist_gradient_boosting_platt","hist_gradient_boosting_raw","logistic_platt","logistic_raw","previous_episode_literal","training_prevalence"],"partitions":["train","tune","calibrate"]},"definitions":{"episode_gap_days":30,"locked_test_metrics_published":false,"support_rounding":100,"suppression_min_distinct_nonpass_vehicles":10,"suppression_min_distinct_pass_vehicles":10,"suppression_min_distinct_vehicles":100,"suppression_min_nonpass":10,"suppression_min_pass":10,"suppression_min_support":100,"target":"first-attempt next-episode binary non-pass rate"},"development_preview":true,"model_versions":["baseline_v1","hist_gradient_boosting_v1"],"population_estimate_allowed":false,"release_id":"7d207baa6389ff26d6d9fa39da8fd917500bca5f7bdc0ecd090e9ef1a7ca105e","schema_version":"dashboard_data_v1"}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"age_bands":["0-3","4-7","8-11","12-15","16-20","21+"],"development_preview":true,"models":["hist_gradient_boosting_platt","hist_gradient_boosting_raw","logistic_platt","logistic_raw","previous_episode_literal","training_prevalence"],"partitions":["train","tune","calibrate"],"periods":[{"quarters":[1,2,3,4],"year":2016},{"quarters":[1,2,3,4],"year":2017},{"quarters":[1,2,3,4],"year":2018},{"quarters":[1,2,3,4],"year":2019},{"quarters":[1,2,3,4],"year":2020},{"quarters":[1,2,3,4],"year":2021},{"quarters":[1,2,3,4],"year":2022},{"quarters":[1,2,3,4],"year":2023},{"quarters":[1,2,3,4],"year":2024}],"population_estimate_allowed":false,"prior_make_models":[{"prior_make":"FORD","prior_model":"F150"},{"prior_make":"HONDA","prior_model":"ACCORD"},{"prior_make":"HONDA","prior_model":"CIVIC"},{"prior_make":"TOYOTA","prior_model":"CAMRY"}],"public_counties":["salt_lake","utah","weber"],"schema_version":"dashboard_data_v1"}
|
{"age_bands":["0-3","4-7","8-11","12-15","16-20","21+"],"development_preview":true,"models":["hist_gradient_boosting_platt","hist_gradient_boosting_raw","logistic_platt","logistic_raw","previous_episode_literal","training_prevalence"],"partitions":["train","tune","calibrate"],"periods":[{"quarters":[1,2,3,4],"year":2016},{"quarters":[1,2,3,4],"year":2017},{"quarters":[1,2,3,4],"year":2018},{"quarters":[1,2,3,4],"year":2019},{"quarters":[1,2,3,4],"year":2020},{"quarters":[1,2,3,4],"year":2021},{"quarters":[1,2,3,4],"year":2022},{"quarters":[1,2,3,4],"year":2023},{"quarters":[1,2,3,4],"year":2024}],"population_estimate_allowed":false,"prior_make_models":[{"prior_make":"CHEVROLET","prior_model":"SILVERADO 1500"},{"prior_make":"DODGE","prior_model":"RAM 1500"},{"prior_make":"FORD","prior_model":"F150"},{"prior_make":"HONDA","prior_model":"ACCORD"},{"prior_make":"HONDA","prior_model":"CIVIC"},{"prior_make":"HYUNDAI","prior_model":"ELANTRA"},{"prior_make":"NISSAN","prior_model":"ALTIMA"},{"prior_make":"SUBARU","prior_model":"OUTBACK"},{"prior_make":"TOYOTA","prior_model":"4RUNNER"},{"prior_make":"TOYOTA","prior_model":"CAMRY"},{"prior_make":"TOYOTA","prior_model":"COROLLA"},{"prior_make":"TOYOTA","prior_model":"TACOMA"}],"public_counties":["salt_lake","utah","weber"],"schema_version":"dashboard_data_v1"}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"development_preview":true,"files":[{"name":"age_risk_curve.json","sha256":"5445d52365ad3494ba05f097e7f4f5e41fffeecfe50fbb2952d72044f496c925"},{"name":"cohort_scorecard.json","sha256":"2ae83d9d55aa1f2ccae6420c66350fb491405eedfa90374aa3df2782a4aa0bf1"},{"name":"coverage_quality.json","sha256":"35b7dbed50e1b8260aac18c269f5045010ea23649a649f984a4693f3f00a1e83"},{"name":"data_manifest.json","sha256":"b7196b04eb223683c928cfb5375ed618e1230fa03382bd8a889de24c589557a0"},{"name":"filter_catalog.json","sha256":"9e45d428cd38853002ebd0bda089eb46d0832cd1b7b6b05ac953bc8472b01eb5"},{"name":"model_diagnostics.json","sha256":"ebf99eb32436a5eabb7d754cd88109f2cc0981575dd28966757031ac7faafa57"},{"name":"overview_period_county.json","sha256":"fe2a4233563b47fa31fc87859b100983d3bfb574c5a7fbc7241b9cfb4661b038"}],"population_estimate_allowed":false,"schema_version":"dashboard_data_v1"}
|
{"development_preview":true,"files":[{"name":"age_risk_curve.json","sha256":"5445d52365ad3494ba05f097e7f4f5e41fffeecfe50fbb2952d72044f496c925"},{"name":"cohort_scorecard.json","sha256":"3acd3aa5a32f53e8f0a2baaad6fec8bb0610efaac8151d8ec4a88285faca23fd"},{"name":"coverage_quality.json","sha256":"35b7dbed50e1b8260aac18c269f5045010ea23649a649f984a4693f3f00a1e83"},{"name":"data_manifest.json","sha256":"8ed1593dd223ddd2fbdcd808040ee6a9f37f078011e821a058a1c7a6d94694d2"},{"name":"filter_catalog.json","sha256":"e78e65ae463edf4ea1b5c757e1a002b5fd953beb75c31381f8264e267295f48e"},{"name":"model_diagnostics.json","sha256":"ebf99eb32436a5eabb7d754cd88109f2cc0981575dd28966757031ac7faafa57"},{"name":"overview_period_county.json","sha256":"fe2a4233563b47fa31fc87859b100983d3bfb574c5a7fbc7241b9cfb4661b038"}],"population_estimate_allowed":false,"schema_version":"dashboard_data_v1"}
|
||||||
|
|||||||
@ -658,6 +658,22 @@ main {
|
|||||||
margin: 0 auto 1rem;
|
margin: 0 auto 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.utah-map .axis-label,
|
||||||
|
.utah-map .state-label {
|
||||||
|
fill: var(--ink-650);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
.utah-map .axis-label {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.utah-map .state-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
}
|
||||||
|
|
||||||
.county-list {
|
.county-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -716,6 +732,10 @@ main {
|
|||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.explorer-results {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-panel {
|
.filter-panel {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 155px;
|
top: 155px;
|
||||||
@ -1522,6 +1542,15 @@ fieldset:disabled .field select {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart--scorecard {
|
||||||
|
overflow-x: auto;
|
||||||
|
overscroll-behavior-inline: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart--scorecard svg {
|
||||||
|
min-width: 720px;
|
||||||
|
}
|
||||||
|
|
||||||
.locked-panel {
|
.locked-panel {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
loadDashboardData,
|
loadDashboardData,
|
||||||
validateAssetSet,
|
validateAssetSet,
|
||||||
} from "../js/data.js";
|
} from "../js/data.js";
|
||||||
|
import { renderUtahCoverage } from "../js/charts.js";
|
||||||
|
|
||||||
const DASHBOARD_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
const DASHBOARD_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
const PUBLIC_DATA = path.join(DASHBOARD_ROOT, "public", "data");
|
const PUBLIC_DATA = path.join(DASHBOARD_ROOT, "public", "data");
|
||||||
@ -84,13 +85,41 @@ test("narrow layouts constrain body content while preserving local nav scrolling
|
|||||||
assert.match(css, /html\s*{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*clip;/s);
|
assert.match(css, /html\s*{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*clip;/s);
|
||||||
assert.match(css, /body\s*{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*clip;/s);
|
assert.match(css, /body\s*{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*clip;/s);
|
||||||
assert.match(css, /\.primary-nav__inner\s*{[^}]*overflow-x:\s*auto;/s);
|
assert.match(css, /\.primary-nav__inner\s*{[^}]*overflow-x:\s*auto;/s);
|
||||||
|
assert.match(css, /\.explorer-results\s*{[^}]*min-width:\s*0;/s);
|
||||||
const mobile = css.match(/@media \(max-width: 660px\)\s*{[\s\S]*?(?=@media|$)/)?.[0];
|
const mobile = css.match(/@media \(max-width: 660px\)\s*{[\s\S]*?(?=@media|$)/)?.[0];
|
||||||
assert.ok(mobile, "mobile breakpoint is present");
|
assert.ok(mobile, "mobile breakpoint is present");
|
||||||
assert.match(mobile, /\.page-shell\s*{[^}]*width:\s*auto;[^}]*max-width:\s*calc\(100% - 2rem\);/s);
|
assert.match(mobile, /\.page-shell\s*{[^}]*width:\s*auto;[^}]*max-width:\s*calc\(100% - 2rem\);/s);
|
||||||
assert.match(mobile, /\.preview-banner\s*{[^}]*justify-content:\s*flex-start;/s);
|
assert.match(mobile, /\.preview-banner\s*{[^}]*justify-content:\s*flex-start;/s);
|
||||||
|
assert.match(mobile, /\.chart--scorecard\s*{[^}]*overflow-x:\s*auto;/s);
|
||||||
|
assert.match(mobile, /\.chart--scorecard svg\s*{[^}]*min-width:\s*720px;/s);
|
||||||
assert.match(mobile, /overflow-wrap:\s*anywhere;/);
|
assert.match(mobile, /overflow-wrap:\s*anywhere;/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("county coverage renders Utah's northeast step and geographically aligned feeds", () => {
|
||||||
|
const attributes = new Map();
|
||||||
|
const container = {
|
||||||
|
innerHTML: "",
|
||||||
|
setAttribute(name, value) {
|
||||||
|
attributes.set(name, value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
renderUtahCoverage(container, ["weber", "salt_lake", "utah"]);
|
||||||
|
|
||||||
|
assert.match(
|
||||||
|
container.innerHTML,
|
||||||
|
/data-map-feature="utah-outline" d="M44 20H153V67H226V255H44Z"/,
|
||||||
|
);
|
||||||
|
assert.doesNotMatch(container.innerHTML, /stroke-dasharray/);
|
||||||
|
assert.equal(container.innerHTML.match(/<circle\b/g)?.length, 3);
|
||||||
|
assert.match(container.innerHTML, /cx="123" cy="54"[^>]*><title>Weber County/);
|
||||||
|
assert.match(container.innerHTML, /cx="121" cy="83"[^>]*><title>Salt Lake County/);
|
||||||
|
assert.match(container.innerHTML, /cx="131" cy="108"[^>]*><title>Utah County/);
|
||||||
|
assert.match(container.innerHTML, />UTAH<\/text>/);
|
||||||
|
assert.match(attributes.get("aria-label"), /^Outline of Utah showing/);
|
||||||
|
assert.doesNotMatch(attributes.get("aria-label"), /_/);
|
||||||
|
});
|
||||||
|
|
||||||
test("model view declares the final model, benchmark role, and publication boundary", () => {
|
test("model view declares the final model, benchmark role, and publication boundary", () => {
|
||||||
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
|
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
|
||||||
const modelView = viewSource(html, "model");
|
const modelView = viewSource(html, "model");
|
||||||
@ -120,6 +149,16 @@ test("every result-facing view visibly labels the sample and non-population scop
|
|||||||
assert.match(html, /not a reliability ranking or population comparison/i);
|
assert.match(html, /not a reliability ranking or population comparison/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("cohort view discloses which rows are combined families", () => {
|
||||||
|
const html = readFileSync(path.join(DASHBOARD_ROOT, "index.html"), "utf8");
|
||||||
|
const cohortView = viewSource(html, "cohorts");
|
||||||
|
assert.match(cohortView, /Combined model families:/);
|
||||||
|
assert.match(cohortView, /Silverado 1500; Dodge Ram 1500; Hyundai Elantra; Nissan Altima; Subaru Outback;/);
|
||||||
|
assert.match(cohortView, /Toyota 4Runner, Corolla, and Tacoma/);
|
||||||
|
assert.match(cohortView, /Exact source-label cohorts:/);
|
||||||
|
assert.match(cohortView, /Ford F150; Honda Accord and Civic; and Toyota Camry/);
|
||||||
|
});
|
||||||
|
|
||||||
test("all required generated assets satisfy the browser contract", () => {
|
test("all required generated assets satisfy the browser contract", () => {
|
||||||
const validated = validateAssetSet(assetSet());
|
const validated = validateAssetSet(assetSet());
|
||||||
assert.equal(validated.manifest.schema_version, SCHEMA_VERSION);
|
assert.equal(validated.manifest.schema_version, SCHEMA_VERSION);
|
||||||
@ -139,6 +178,14 @@ test("all required generated assets satisfy the browser contract", () => {
|
|||||||
["train", "tune", "calibrate"],
|
["train", "tune", "calibrate"],
|
||||||
);
|
);
|
||||||
assert.ok(validated.overview.rows.length > 0);
|
assert.ok(validated.overview.rows.length > 0);
|
||||||
|
assert.equal(validated.scorecards.rows.length, 12);
|
||||||
|
assert.deepEqual(
|
||||||
|
validated.filters.prior_make_models,
|
||||||
|
validated.scorecards.rows.map(({ prior_make, prior_model }) => ({
|
||||||
|
prior_make,
|
||||||
|
prior_model,
|
||||||
|
})),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("browser loader verifies every raw asset digest before rendering", async () => {
|
test("browser loader verifies every raw asset digest before rendering", async () => {
|
||||||
|
|||||||
@ -31,6 +31,13 @@ headlined, or described as population county performance.
|
|||||||
### Sample cohorts
|
### Sample cohorts
|
||||||
|
|
||||||
- Search and compare only supported, suppression-cleared make/model cohorts
|
- Search and compare only supported, suppression-cleared make/model cohorts
|
||||||
|
- Combine documented source abbreviations and trim labels for newly published
|
||||||
|
model families before applying the unchanged publication thresholds
|
||||||
|
- Keep earlier public cohort definitions stable across releases so differences
|
||||||
|
cannot expose a formerly suppressed slice
|
||||||
|
- Identify Silverado 1500, Ram 1500, Elantra, Altima, Outback, 4Runner,
|
||||||
|
Corolla, and Tacoma as combined families; identify F150, Accord, Civic, and
|
||||||
|
Camry as exact source-label cohorts
|
||||||
- Show observed sample non-pass rates and rounded support
|
- Show observed sample non-pass rates and rounded support
|
||||||
- Default sorting by support rather than risk
|
- Default sorting by support rather than risk
|
||||||
- Disable filters or adjusted views that the aggregate bundle cannot support
|
- Disable filters or adjusted views that the aggregate bundle cannot support
|
||||||
|
|||||||
@ -53,7 +53,9 @@ the “observed sample non-pass” label.
|
|||||||
**Say:**
|
**Say:**
|
||||||
|
|
||||||
> This view contains only prior make-and-model cohorts that cleared the
|
> This view contains only prior make-and-model cohorts that cleared the
|
||||||
> publication thresholds. I can search supported cohorts, but these are observed
|
> publication thresholds. Selected source abbreviations and trim labels are
|
||||||
|
> combined for new model families, while earlier published cohorts keep stable
|
||||||
|
> definitions. I can search supported cohorts, but these are observed
|
||||||
> development-sample associations—not reliability grades, rankings, or
|
> development-sample associations—not reliability grades, rankings, or
|
||||||
> recommendations. Unsupported slices are not inferred in the browser, and
|
> recommendations. Unsupported slices are not inferred in the browser, and
|
||||||
> suppressed rows are absent rather than hidden.
|
> suppressed rows are absent rather than hidden.
|
||||||
|
|||||||
@ -140,7 +140,9 @@ views:
|
|||||||
|
|
||||||
1. **Overview** shows rounded support, sample outcome patterns, vehicle-age
|
1. **Overview** shows rounded support, sample outcome patterns, vehicle-age
|
||||||
patterns, and county feed coverage.
|
patterns, and county feed coverage.
|
||||||
2. **Sample cohorts** shows only supported make/model sample aggregates; it does
|
2. **Sample cohorts** shows only supported make/model sample aggregates;
|
||||||
|
selected source abbreviations and trim labels are combined for new model
|
||||||
|
families while earlier published cohorts keep stable definitions. It does
|
||||||
not issue reliability ratings or recommendations.
|
not issue reliability ratings or recommendations.
|
||||||
3. **Model & benchmark** identifies calibrated logistic regression as final and
|
3. **Model & benchmark** identifies calibrated logistic regression as final and
|
||||||
the boosted tree as benchmark-only. It shows pre-2025, 2024 calibration-fit
|
the boosted tree as benchmark-only. It shows pre-2025, 2024 calibration-fit
|
||||||
|
|||||||
@ -132,7 +132,8 @@ population or external-validation results.
|
|||||||
- Page sampling is not population-representative.
|
- Page sampling is not population-representative.
|
||||||
- Source, program, time, and geography are entangled.
|
- Source, program, time, and geography are entangled.
|
||||||
- Reject and abort are heterogeneous non-pass outcomes.
|
- Reject and abort are heterogeneous non-pass outcomes.
|
||||||
- Make/model aliases and incomplete feed coverage can distort cohorts.
|
- Residual make/model aliases outside the documented scorecard mappings and
|
||||||
|
incomplete feed coverage can distort cohorts.
|
||||||
- The one-time 2025 holdout is not an external validation dataset.
|
- The one-time 2025 holdout is not an external validation dataset.
|
||||||
- No protected attributes are modeled, but that does not establish fairness.
|
- No protected attributes are modeled, but that does not establish fairness.
|
||||||
- Sample calibration does not establish production calibration.
|
- Sample calibration does not establish production calibration.
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import duckdb
|
|||||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||||
PUBLIC_DATA_ROOT = (PROJECT_ROOT / "dashboard/public/data").resolve()
|
PUBLIC_DATA_ROOT = (PROJECT_ROOT / "dashboard/public/data").resolve()
|
||||||
SCHEMA_VERSION = "dashboard_data_v1"
|
SCHEMA_VERSION = "dashboard_data_v1"
|
||||||
|
PUBLICATION_POLICY_VERSION = "dashboard_publication_policy_v1"
|
||||||
ALLOWED_PARTITIONS = ("train", "tune", "calibrate")
|
ALLOWED_PARTITIONS = ("train", "tune", "calibrate")
|
||||||
PARTITION_ORDER = {name: index for index, name in enumerate(ALLOWED_PARTITIONS)}
|
PARTITION_ORDER = {name: index for index, name in enumerate(ALLOWED_PARTITIONS)}
|
||||||
LOCKED_PARTITION_ALIASES = ("test", "locked_test", "locked-test")
|
LOCKED_PARTITION_ALIASES = ("test", "locked_test", "locked-test")
|
||||||
@ -641,7 +642,183 @@ def _age_risk_rows(connection: duckdb.DuckDBPyConnection) -> List[Dict[str, obje
|
|||||||
def _scorecard_rows(connection: duckdb.DuckDBPyConnection) -> List[Dict[str, object]]:
|
def _scorecard_rows(connection: duckdb.DuckDBPyConnection) -> List[Dict[str, object]]:
|
||||||
raw = connection.execute(
|
raw = connection.execute(
|
||||||
"""
|
"""
|
||||||
SELECT last_observed_make, last_observed_model,
|
WITH legacy_cells(prior_make, prior_model) AS (
|
||||||
|
VALUES
|
||||||
|
('FORD', 'F150'),
|
||||||
|
('HONDA', 'ACCORD'),
|
||||||
|
('HONDA', 'CIVIC'),
|
||||||
|
('TOYOTA', 'CAMRY')
|
||||||
|
),
|
||||||
|
make_aliases(raw_make, canonical_make) AS (
|
||||||
|
VALUES
|
||||||
|
('CHEVR', 'CHEVROLET'),
|
||||||
|
('HYUND', 'HYUNDAI'),
|
||||||
|
('NISSA', 'NISSAN'),
|
||||||
|
('SUBAR', 'SUBARU'),
|
||||||
|
('TOYOT', 'TOYOTA')
|
||||||
|
),
|
||||||
|
normalized AS (
|
||||||
|
SELECT
|
||||||
|
safe_mart.last_observed_make AS raw_make,
|
||||||
|
safe_mart.last_observed_model AS raw_model,
|
||||||
|
coalesce(
|
||||||
|
make_aliases.canonical_make,
|
||||||
|
upper(trim(safe_mart.last_observed_make))
|
||||||
|
) AS normalized_make,
|
||||||
|
upper(trim(safe_mart.last_observed_model)) AS normalized_model,
|
||||||
|
exact_legacy.prior_make IS NOT NULL AS is_exact_legacy,
|
||||||
|
display_legacy.prior_make IS NOT NULL AS collides_with_legacy,
|
||||||
|
safe_mart.vehicle_token,
|
||||||
|
safe_mart.target_nonpass
|
||||||
|
FROM safe_mart
|
||||||
|
LEFT JOIN make_aliases
|
||||||
|
ON upper(trim(safe_mart.last_observed_make)) = make_aliases.raw_make
|
||||||
|
LEFT JOIN legacy_cells AS exact_legacy
|
||||||
|
ON safe_mart.last_observed_make = exact_legacy.prior_make
|
||||||
|
AND safe_mart.last_observed_model = exact_legacy.prior_model
|
||||||
|
LEFT JOIN legacy_cells AS display_legacy
|
||||||
|
ON trim(safe_mart.last_observed_make) = display_legacy.prior_make
|
||||||
|
AND trim(safe_mart.last_observed_model) = display_legacy.prior_model
|
||||||
|
WHERE safe_mart.eligible_returning_target
|
||||||
|
AND safe_mart.target_nonpass IN (0, 1)
|
||||||
|
AND safe_mart.last_observed_make IS NOT NULL
|
||||||
|
AND safe_mart.last_observed_model IS NOT NULL
|
||||||
|
),
|
||||||
|
classified AS (
|
||||||
|
SELECT
|
||||||
|
CASE
|
||||||
|
-- Only the eight families introduced in this release use
|
||||||
|
-- alias folding. The four legacy cells never enter this
|
||||||
|
-- branch, so their published counts retain raw-key meaning.
|
||||||
|
WHEN normalized_make = 'TOYOTA'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'COROLLA'
|
||||||
|
OR normalized_model LIKE 'COROLLA %'
|
||||||
|
)
|
||||||
|
AND normalized_model != 'COROLLA IM'
|
||||||
|
AND normalized_model NOT LIKE 'COROLLA IM %'
|
||||||
|
THEN 'toyota_corolla'
|
||||||
|
WHEN normalized_make = 'TOYOTA'
|
||||||
|
AND (
|
||||||
|
normalized_model = '4RUNNER'
|
||||||
|
OR normalized_model LIKE '4RUNNER %'
|
||||||
|
)
|
||||||
|
THEN 'toyota_4runner'
|
||||||
|
WHEN normalized_make = 'TOYOTA'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'TACOMA'
|
||||||
|
OR normalized_model LIKE 'TACOMA %'
|
||||||
|
)
|
||||||
|
THEN 'toyota_tacoma'
|
||||||
|
WHEN normalized_make = 'DODGE'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'RAM 1500'
|
||||||
|
OR normalized_model LIKE 'RAM 1500 %'
|
||||||
|
OR normalized_model = 'RAM PICKUP 1500'
|
||||||
|
OR normalized_model LIKE 'RAM PICKUP 1500 %'
|
||||||
|
OR normalized_model = 'RAM1500'
|
||||||
|
OR normalized_model LIKE 'RAM1500 %'
|
||||||
|
)
|
||||||
|
AND normalized_model NOT LIKE '% VAN'
|
||||||
|
AND normalized_model NOT LIKE '% VAN %'
|
||||||
|
THEN 'dodge_ram_1500'
|
||||||
|
WHEN normalized_make = 'CHEVROLET'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'SILVERADO 1500'
|
||||||
|
OR normalized_model LIKE 'SILVERADO 1500 %'
|
||||||
|
OR normalized_model = 'SILVERADO 1500HD'
|
||||||
|
OR normalized_model LIKE 'SILVERADO 1500HD %'
|
||||||
|
OR normalized_model = 'SILVERADO C1500'
|
||||||
|
OR normalized_model LIKE 'SILVERADO C1500 %'
|
||||||
|
OR normalized_model = 'SILVERADO K1500'
|
||||||
|
OR normalized_model LIKE 'SILVERADO K1500 %'
|
||||||
|
OR normalized_model = 'C1500 SILVERADO'
|
||||||
|
OR normalized_model LIKE 'C1500 SILVERADO %'
|
||||||
|
OR normalized_model = 'K15 SILVERADO'
|
||||||
|
OR normalized_model LIKE 'K15 SILVERADO %'
|
||||||
|
OR normalized_model = 'K1500 SILVERADO'
|
||||||
|
OR normalized_model LIKE 'K1500 SILVERADO %'
|
||||||
|
)
|
||||||
|
THEN 'chevrolet_silverado_1500'
|
||||||
|
WHEN normalized_make = 'NISSAN'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'ALTIMA'
|
||||||
|
OR normalized_model LIKE 'ALTIMA %'
|
||||||
|
)
|
||||||
|
THEN 'nissan_altima'
|
||||||
|
WHEN normalized_make = 'HYUNDAI'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'ELANTRA'
|
||||||
|
OR normalized_model LIKE 'ELANTRA %'
|
||||||
|
)
|
||||||
|
THEN 'hyundai_elantra'
|
||||||
|
WHEN normalized_make = 'SUBARU'
|
||||||
|
AND (
|
||||||
|
normalized_model = 'OUTBACK'
|
||||||
|
OR normalized_model LIKE 'OUTBACK %'
|
||||||
|
OR normalized_model = 'LEGACY OUTBACK'
|
||||||
|
OR normalized_model LIKE 'LEGACY OUTBACK %'
|
||||||
|
)
|
||||||
|
THEN 'subaru_outback'
|
||||||
|
ELSE NULL
|
||||||
|
END AS new_family,
|
||||||
|
*
|
||||||
|
FROM normalized
|
||||||
|
),
|
||||||
|
publication_input AS (
|
||||||
|
-- Exact legacy keys are deliberately copied from the raw columns.
|
||||||
|
SELECT raw_make AS prior_make,
|
||||||
|
raw_model AS prior_model,
|
||||||
|
vehicle_token,
|
||||||
|
target_nonpass
|
||||||
|
FROM classified
|
||||||
|
WHERE is_exact_legacy
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- Preserve the original raw-key behavior for unrelated cohorts.
|
||||||
|
-- A whitespace variant that would render as a legacy key is
|
||||||
|
-- withheld rather than creating a duplicate public identity.
|
||||||
|
SELECT raw_make AS prior_make,
|
||||||
|
raw_model AS prior_model,
|
||||||
|
vehicle_token,
|
||||||
|
target_nonpass
|
||||||
|
FROM classified
|
||||||
|
WHERE NOT is_exact_legacy
|
||||||
|
AND NOT collides_with_legacy
|
||||||
|
AND new_family IS NULL
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
CASE new_family
|
||||||
|
WHEN 'chevrolet_silverado_1500' THEN 'CHEVROLET'
|
||||||
|
WHEN 'dodge_ram_1500' THEN 'DODGE'
|
||||||
|
WHEN 'hyundai_elantra' THEN 'HYUNDAI'
|
||||||
|
WHEN 'nissan_altima' THEN 'NISSAN'
|
||||||
|
WHEN 'subaru_outback' THEN 'SUBARU'
|
||||||
|
WHEN 'toyota_4runner' THEN 'TOYOTA'
|
||||||
|
WHEN 'toyota_corolla' THEN 'TOYOTA'
|
||||||
|
WHEN 'toyota_tacoma' THEN 'TOYOTA'
|
||||||
|
ELSE NULL
|
||||||
|
END AS prior_make,
|
||||||
|
CASE new_family
|
||||||
|
WHEN 'chevrolet_silverado_1500' THEN 'SILVERADO 1500'
|
||||||
|
WHEN 'dodge_ram_1500' THEN 'RAM 1500'
|
||||||
|
WHEN 'hyundai_elantra' THEN 'ELANTRA'
|
||||||
|
WHEN 'nissan_altima' THEN 'ALTIMA'
|
||||||
|
WHEN 'subaru_outback' THEN 'OUTBACK'
|
||||||
|
WHEN 'toyota_4runner' THEN '4RUNNER'
|
||||||
|
WHEN 'toyota_corolla' THEN 'COROLLA'
|
||||||
|
WHEN 'toyota_tacoma' THEN 'TACOMA'
|
||||||
|
ELSE NULL
|
||||||
|
END AS prior_model,
|
||||||
|
vehicle_token,
|
||||||
|
target_nonpass
|
||||||
|
FROM classified
|
||||||
|
WHERE new_family IS NOT NULL
|
||||||
|
)
|
||||||
|
SELECT prior_make, prior_model,
|
||||||
count(*)::BIGINT AS n,
|
count(*)::BIGINT AS n,
|
||||||
sum(target_nonpass)::BIGINT AS nonpass,
|
sum(target_nonpass)::BIGINT AS nonpass,
|
||||||
count(DISTINCT vehicle_token)::BIGINT AS vehicles,
|
count(DISTINCT vehicle_token)::BIGINT AS vehicles,
|
||||||
@ -651,11 +828,7 @@ def _scorecard_rows(connection: duckdb.DuckDBPyConnection) -> List[Dict[str, obj
|
|||||||
count(DISTINCT vehicle_token) FILTER (
|
count(DISTINCT vehicle_token) FILTER (
|
||||||
WHERE target_nonpass = 1
|
WHERE target_nonpass = 1
|
||||||
)::BIGINT AS nonpass_vehicles
|
)::BIGINT AS nonpass_vehicles
|
||||||
FROM safe_mart
|
FROM publication_input
|
||||||
WHERE eligible_returning_target
|
|
||||||
AND target_nonpass IN (0, 1)
|
|
||||||
AND last_observed_make IS NOT NULL
|
|
||||||
AND last_observed_model IS NOT NULL
|
|
||||||
GROUP BY 1, 2
|
GROUP BY 1, 2
|
||||||
HAVING count(*) >= 100
|
HAVING count(*) >= 100
|
||||||
AND sum(target_nonpass) >= 10
|
AND sum(target_nonpass) >= 10
|
||||||
@ -667,7 +840,7 @@ def _scorecard_rows(connection: duckdb.DuckDBPyConnection) -> List[Dict[str, obj
|
|||||||
AND count(DISTINCT vehicle_token) FILTER (
|
AND count(DISTINCT vehicle_token) FILTER (
|
||||||
WHERE target_nonpass = 1
|
WHERE target_nonpass = 1
|
||||||
) >= 10
|
) >= 10
|
||||||
ORDER BY n DESC, last_observed_make, last_observed_model
|
ORDER BY n DESC, prior_make, prior_model
|
||||||
LIMIT 200
|
LIMIT 200
|
||||||
"""
|
"""
|
||||||
).fetchall()
|
).fetchall()
|
||||||
@ -784,6 +957,7 @@ def _release_id(
|
|||||||
) -> str:
|
) -> str:
|
||||||
material = {
|
material = {
|
||||||
"schema_version": SCHEMA_VERSION,
|
"schema_version": SCHEMA_VERSION,
|
||||||
|
"publication_policy_version": PUBLICATION_POLICY_VERSION,
|
||||||
"mart_sha256": mart_digest,
|
"mart_sha256": mart_digest,
|
||||||
"models": [
|
"models": [
|
||||||
{"model_version": version, "metrics_sha256": metrics_digest}
|
{"model_version": version, "metrics_sha256": metrics_digest}
|
||||||
|
|||||||
@ -117,6 +117,213 @@ class DashboardExportTests(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual(first_bytes, second_bytes)
|
self.assertEqual(first_bytes, second_bytes)
|
||||||
|
|
||||||
|
def test_release_id_includes_publication_policy_version(self) -> None:
|
||||||
|
mart_digest = "a" * 64
|
||||||
|
provenance = [("model_v1", "b" * 64)]
|
||||||
|
release_id = dashboard_export._release_id(mart_digest, provenance)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
dashboard_export,
|
||||||
|
"PUBLICATION_POLICY_VERSION",
|
||||||
|
dashboard_export.PUBLICATION_POLICY_VERSION + "_changed",
|
||||||
|
):
|
||||||
|
changed_release_id = dashboard_export._release_id(
|
||||||
|
mart_digest, provenance
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNotEqual(release_id, changed_release_id)
|
||||||
|
|
||||||
|
def test_scorecards_fold_only_new_family_aliases_before_suppression(self) -> None:
|
||||||
|
connection = duckdb.connect(":memory:")
|
||||||
|
try:
|
||||||
|
connection.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE safe_mart (
|
||||||
|
vehicle_token VARCHAR,
|
||||||
|
eligible_returning_target BOOLEAN,
|
||||||
|
target_nonpass INTEGER,
|
||||||
|
last_observed_make VARCHAR,
|
||||||
|
last_observed_model VARCHAR
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
def add_group(make: str, model: str, nonpass: int = 10) -> None:
|
||||||
|
group_number = len(rows)
|
||||||
|
for index in range(60):
|
||||||
|
rows.append(
|
||||||
|
(
|
||||||
|
"group-{}-vehicle-{}".format(group_number, index),
|
||||||
|
True,
|
||||||
|
1 if index < nonpass else 0,
|
||||||
|
make,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Every raw-string group is below the 100-vehicle threshold. Each
|
||||||
|
# approved pair reaches 120 only after its explicit aliases merge.
|
||||||
|
canonical_groups = {
|
||||||
|
("CHEVROLET", "SILVERADO 1500"): (
|
||||||
|
("CHEVROLET", "SILVERADO 1500 LT"),
|
||||||
|
("CHEVR", "K15 SILVERADO"),
|
||||||
|
),
|
||||||
|
("DODGE", "RAM 1500"): (
|
||||||
|
("DODGE", "RAM 1500 QUAD"),
|
||||||
|
("DODGE", "RAM PICKUP 1500"),
|
||||||
|
),
|
||||||
|
("HYUNDAI", "ELANTRA"): (
|
||||||
|
("HYUNDAI", "ELANTRA GLS"),
|
||||||
|
("HYUND", "ELANTRA"),
|
||||||
|
),
|
||||||
|
("NISSAN", "ALTIMA"): (
|
||||||
|
("NISSAN", "ALTIMA 2.5 S"),
|
||||||
|
("NISSA", "ALTIMA"),
|
||||||
|
),
|
||||||
|
("SUBARU", "OUTBACK"): (
|
||||||
|
("SUBARU", "LEGACY OUTBACK 2.5I AWD"),
|
||||||
|
("SUBAR", "OUTBACK"),
|
||||||
|
),
|
||||||
|
("TOYOTA", "4RUNNER"): (
|
||||||
|
("TOYOTA", "4RUNNER SR5"),
|
||||||
|
("TOYOT", "4RUNNER 4WD"),
|
||||||
|
),
|
||||||
|
("TOYOTA", "COROLLA"): (
|
||||||
|
("TOYOTA", "COROLLA CE LE S"),
|
||||||
|
("TOYOT", "COROLLA"),
|
||||||
|
),
|
||||||
|
("TOYOTA", "TACOMA"): (
|
||||||
|
("TOYOTA", "TACOMA V6"),
|
||||||
|
("TOYOT", "TACOMA 4WD"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for variants in canonical_groups.values():
|
||||||
|
for make, model in variants:
|
||||||
|
add_group(make, model)
|
||||||
|
|
||||||
|
# These separately marketed lines share a prefix with a canonical
|
||||||
|
# family but must remain distinct and suppressed at this support.
|
||||||
|
for make, model in (
|
||||||
|
("HONDA", "ACCORD CROSS TOUR EXL"),
|
||||||
|
("HONDA", "ACCORD CROSSTOUR"),
|
||||||
|
("HONDA", "CIVIC CRX SI"),
|
||||||
|
("HONDA", "CIVIC DEL SOL SI"),
|
||||||
|
("TOYOT", "CAMRY SOLARA"),
|
||||||
|
("TOYOTA", "COROLLA IM"),
|
||||||
|
("DODGE", "RAM 1500 VAN"),
|
||||||
|
("FORD", "F1500"),
|
||||||
|
("NISSAN", "ALTIMAX"),
|
||||||
|
("TOYOTA", "CAMRYX"),
|
||||||
|
):
|
||||||
|
add_group(make, model, nonpass=30)
|
||||||
|
|
||||||
|
connection.executemany(
|
||||||
|
"INSERT INTO safe_mart VALUES (?, ?, ?, ?, ?)", rows
|
||||||
|
)
|
||||||
|
scorecards = dashboard_export._scorecard_rows(connection)
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
[(row["prior_make"], row["prior_model"]) for row in scorecards],
|
||||||
|
sorted(canonical_groups),
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
all(row["support_rounded"] == 100 for row in scorecards)
|
||||||
|
)
|
||||||
|
self.assertTrue(all(row["nonpass_rate"] == 0.167 for row in scorecards))
|
||||||
|
|
||||||
|
def test_legacy_scorecard_cells_are_not_broadened_by_aliases(self) -> None:
|
||||||
|
connection = duckdb.connect(":memory:")
|
||||||
|
try:
|
||||||
|
connection.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE safe_mart (
|
||||||
|
vehicle_token VARCHAR,
|
||||||
|
eligible_returning_target BOOLEAN,
|
||||||
|
target_nonpass INTEGER,
|
||||||
|
last_observed_make VARCHAR,
|
||||||
|
last_observed_model VARCHAR
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
def add_group(
|
||||||
|
make: str,
|
||||||
|
model: str,
|
||||||
|
n: int,
|
||||||
|
nonpass: int,
|
||||||
|
) -> None:
|
||||||
|
group_number = len(rows)
|
||||||
|
for index in range(n):
|
||||||
|
rows.append(
|
||||||
|
(
|
||||||
|
"group-{}-vehicle-{}".format(group_number, index),
|
||||||
|
True,
|
||||||
|
1 if index < nonpass else 0,
|
||||||
|
make,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
legacy_cells = {
|
||||||
|
("FORD", "F150"): 12,
|
||||||
|
("HONDA", "ACCORD"): 18,
|
||||||
|
("HONDA", "CIVIC"): 24,
|
||||||
|
("TOYOTA", "CAMRY"): 30,
|
||||||
|
}
|
||||||
|
aliases = {
|
||||||
|
("FORD", "F150"): (
|
||||||
|
("FORD", "F150 4WD"),
|
||||||
|
("ford", "f150"),
|
||||||
|
),
|
||||||
|
("HONDA", "ACCORD"): (
|
||||||
|
("HONDA", "ACCORD LX"),
|
||||||
|
("honda", "accord"),
|
||||||
|
),
|
||||||
|
("HONDA", "CIVIC"): (
|
||||||
|
("HONDA", "CIVIC EX"),
|
||||||
|
("honda", "civic"),
|
||||||
|
),
|
||||||
|
("TOYOTA", "CAMRY"): (
|
||||||
|
("TOYOTA", "CAMRY LE"),
|
||||||
|
("TOYOT", "CAMRY"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for (make, model), nonpass in legacy_cells.items():
|
||||||
|
add_group(make, model, 120, nonpass)
|
||||||
|
|
||||||
|
# This raw key renders as the legacy key after safe-category
|
||||||
|
# trimming. It must not become a second public identity.
|
||||||
|
add_group(" " + make + " ", " " + model + " ", 120, 60)
|
||||||
|
|
||||||
|
# Each alias is suppressed alone but would pass support if the
|
||||||
|
# two were folded together, making accidental broadening clear.
|
||||||
|
for alias_make, alias_model in aliases[(make, model)]:
|
||||||
|
add_group(alias_make, alias_model, 60, 30)
|
||||||
|
|
||||||
|
connection.executemany(
|
||||||
|
"INSERT INTO safe_mart VALUES (?, ?, ?, ?, ?)", rows
|
||||||
|
)
|
||||||
|
scorecards = dashboard_export._scorecard_rows(connection)
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
self.assertEqual(len(scorecards), len(legacy_cells))
|
||||||
|
by_cell = {
|
||||||
|
(row["prior_make"], row["prior_model"]): row
|
||||||
|
for row in scorecards
|
||||||
|
}
|
||||||
|
self.assertEqual(set(by_cell), set(legacy_cells))
|
||||||
|
for cell, nonpass in legacy_cells.items():
|
||||||
|
with self.subTest(cell=cell):
|
||||||
|
self.assertEqual(by_cell[cell]["support_rounded"], 100)
|
||||||
|
self.assertEqual(
|
||||||
|
by_cell[cell]["nonpass_rate"], round(nonpass / 120, 3)
|
||||||
|
)
|
||||||
|
|
||||||
def test_unlocked_model_manifest_is_refused(self) -> None:
|
def test_unlocked_model_manifest_is_refused(self) -> None:
|
||||||
manifest = self._read_json(self.model_manifest)
|
manifest = self._read_json(self.model_manifest)
|
||||||
manifest["locked_test_evaluated"] = True
|
manifest["locked_test_evaluated"] = True
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user