This is an automated email from the ASF dual-hosted git repository. Gerrrr pushed a commit to branch multiple-algorithms-single-point in repository https://gitbox.apache.org/repos/asf/otava-playground.git
commit 828fc44b2c1888b26129d6847d8e523cb7345f72 Author: Alex Sorokoumov <[email protected]> AuthorDate: Sun Jul 5 10:47:32 2026 -0700 Prepare per-algorithm Otava UI plumbing Add canonical algorithm ordering, compact labels, reusable compare fetch plumbing, and table/header markup and styles needed by per-algorithm views. --- src/otava_test_data/web/static/css/style.css | 50 ++++++++++++++++++++++++++++ src/otava_test_data/web/static/js/app.js | 43 ++++++++++++++++++++++++ src/otava_test_data/web/templates/index.html | 7 ++-- 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/otava_test_data/web/static/css/style.css b/src/otava_test_data/web/static/css/style.css index 2d1bf63..59830aa 100644 --- a/src/otava_test_data/web/static/css/style.css +++ b/src/otava_test_data/web/static/css/style.css @@ -2079,3 +2079,53 @@ body[data-mode="dataset"] .hide-in-dataset { .otava-algo:has(.otava-algo-checkbox:not(:checked)) .algo-params { opacity: 0.45; } + +/* ── Per-algorithm Otava visualization ───────────────────────────── */ + +/* Status text colors used inside detected-table per-algo cells. */ +.pvalue-tp { color: #10b981; font-weight: 600; } +.pvalue-cm { color: #f59e0b; font-weight: 600; } +.pvalue-fp { color: #ef4444; font-weight: 600; } +.pvalue-empty { color: #cbd5e1; } + +/* Dots used in the ground-truth "Matched by" column and the stat card. */ +.algo-dot { + display: inline-block; + width: 9px; + height: 9px; + border-radius: 50%; + vertical-align: middle; + margin-right: 4px; +} +.algo-dot-split { background: #1f77b4; } +.algo-dot-orig { background: #d62728; } +.algo-dot-deterministic { background: #2ca02c; } + +/* Per-algorithm segments inside the "Otava Detected" stat card. */ +.otava-stat-segments { + display: inline-flex; + gap: 8px; + flex-wrap: wrap; + font-size: 0.85rem; +} +.otava-stat-segment { + display: inline-flex; + align-items: center; + gap: 4px; + line-height: 1; +} +.otava-stat-label { color: #64748b; font-size: 0.75rem; } + +/* Matched-by column inner layout — one dot+text per algo, dot-separated. */ +.matched-by-list { + display: inline-flex; + flex-wrap: wrap; + gap: 0 8px; +} +.matched-by-list .matched-entry::after { + content: "·"; + margin-left: 8px; + color: #cbd5e1; +} +.matched-by-list .matched-entry:last-child::after { content: ""; } +.matched-entry-miss { color: #cbd5e1; } diff --git a/src/otava_test_data/web/static/js/app.js b/src/otava_test_data/web/static/js/app.js index 32b032c..68d1ded 100644 --- a/src/otava_test_data/web/static/js/app.js +++ b/src/otava_test_data/web/static/js/app.js @@ -51,6 +51,17 @@ const ALGO_COLORS = { deterministic: '#2ca02c', }; +// Short human-readable labels for compact UI surfaces (stat card, dot lists). +const ALGO_LABELS = { + split: 'split', + orig: 'orig', + deterministic: 'det', +}; + +// Canonical order — drives stat-card layout, accuracy rows, detected columns, +// and matched-by dots. Iteration order of `ALGO_COLORS` is not guaranteed by spec. +const ALGO_ORDER = ['split', 'orig', 'deterministic']; + // In-flight AbortController for /api/compare so out-of-order responses can't // land after a newer one and desync the chart from the controls. let datasetInflight = null; @@ -167,6 +178,38 @@ function getOtavaParamsByAlgo() { return out; } +/** Call /api/compare with the currently-enabled algorithms and their params. + * Returns Promise<Map<algoName, {indices, count, change_points, error?}>> + * for the algorithms that were actually requested. Order in the returned + * Map follows ALGO_ORDER. */ +async function runOtavaForEnabled(series, { signal } = {}) { + const algos = getEnabledOtavaAlgorithms(); + if (algos.length === 0) return new Map(); + + const r = await fetch('/api/compare', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + data: series, + algorithms: algos, + algorithm_params: getOtavaParamsByAlgo(), + }), + signal, + }); + if (!r.ok) { + const err = await r.json().catch(() => ({})); + throw new Error(err.error || `HTTP ${r.status}`); + } + const body = await r.json(); + const out = new Map(); + for (const name of ALGO_ORDER) { + if (body.results && Object.prototype.hasOwnProperty.call(body.results, name)) { + out.set(name, body.results[name]); + } + } + return out; +} + // DOM Elements - Actions const generateBtn = document.getElementById('generate-btn'); const showAllBtn = document.getElementById('show-all-btn'); diff --git a/src/otava_test_data/web/templates/index.html b/src/otava_test_data/web/templates/index.html index 8de8a34..9d35fc1 100644 --- a/src/otava_test_data/web/templates/index.html +++ b/src/otava_test_data/web/templates/index.html @@ -470,7 +470,7 @@ <th>Index</th> <th>Type</th> <th>Description</th> - <th>Matched?</th> + <th class="matched-col-header">Matched by</th> </tr> </thead> <tbody id="truth-table-body"> @@ -481,12 +481,11 @@ <h4>Otava Detected</h4> <table id="detected-table"> <thead> - <tr> + <tr id="detected-table-head-row"> <th>Index</th> <th>Mean Before</th> <th>Mean After</th> - <th>P-Value</th> - <th>Status</th> + <!-- Per-algorithm columns appended by JS --> </tr> </thead> <tbody id="detected-table-body">
