Add data_visualization/js/visualization.js

This commit is contained in:
Mika 2026-03-24 11:10:13 +00:00
parent 3419c4ddd3
commit b4d509a745

View file

@ -0,0 +1,145 @@
"use strict";
/**
* Visualization module for replication analysis dashboard.
* Responsible for rendering charts and updating tables dynamically.
*/
/**
* @typedef {Object} RunComparison
* @property {string} run_id
* @property {Object} metrics
* @property {number[]} metrics.bandwidth
* @property {number[]} metrics.retry_tailp99
* @property {string[]} segments
*/
/**
* Render bandwidth chart based on provided run data.
* @param {RunComparison} data
*/
export function renderBandwidthChart(data) {
const container = document.getElementById('bandwidth-chart');
if (!container) return;
container.innerHTML = '';
const canvas = document.createElement('canvas');
canvas.setAttribute('aria-label', 'Bandwidth Chart');
canvas.setAttribute('role', 'img');
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
const bandwidth = data.metrics?.bandwidth ?? [];
if (!bandwidth.length) {
ctx.font = '16px sans-serif';
ctx.fillText('No bandwidth data available', 10, 30);
return;
}
const spacing = canvas.width / bandwidth.length || 5;
const maxValue = Math.max(...bandwidth);
const scale = (canvas.height - 20) / maxValue;
ctx.strokeStyle = '#3366cc';
ctx.beginPath();
bandwidth.forEach((val, i) => {
const x = i * spacing + 10;
const y = canvas.height - val * scale - 10;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
}
/**
* Render retry_tailp99 chart as a trend visualization.
* @param {RunComparison} data
*/
export function renderRetryTailChart(data) {
const container = document.getElementById('retry-tailp99-chart');
if (!container) return;
container.innerHTML = '';
const canvas = document.createElement('canvas');
canvas.setAttribute('aria-label', 'Retry Tailp99 Trend');
canvas.setAttribute('role', 'img');
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
const retries = data.metrics?.retry_tailp99 ?? [];
if (!retries.length) {
ctx.font = '16px sans-serif';
ctx.fillText('No retry_tailp99 data available', 10, 30);
return;
}
const spacing = canvas.width / retries.length || 5;
const maxValue = Math.max(...retries);
const scale = (canvas.height - 20) / maxValue;
ctx.strokeStyle = '#cc3333';
ctx.beginPath();
retries.forEach((val, i) => {
const x = i * spacing + 10;
const y = canvas.height - val * scale - 10;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
}
/**
* Update results table with latest run metrics.
* @param {RunComparison} data
*/
export function updateResultsTable(data) {
const table = document.getElementById('results-table');
if (!table) return;
table.innerHTML = '';
const headerRow = document.createElement('tr');
['Segment', 'Bandwidth', 'Retry Tail p99'].forEach(h => {
const th = document.createElement('th');
th.textContent = h;
headerRow.appendChild(th);
});
table.appendChild(headerRow);
const {segments = [], metrics: {bandwidth = [], retry_tailp99 = []} = {}} = data;
const len = Math.max(segments.length, bandwidth.length, retry_tailp99.length);
for (let i = 0; i < len; i++) {
const row = document.createElement('tr');
const seg = document.createElement('td');
seg.textContent = segments[i] ?? '-';
const bw = document.createElement('td');
bw.textContent = bandwidth[i]?.toFixed(2) ?? '-';
const rt = document.createElement('td');
rt.textContent = retry_tailp99[i]?.toFixed(2) ?? '-';
row.appendChild(seg);
row.appendChild(bw);
row.appendChild(rt);
table.appendChild(row);
}
}
/**
* Ensure responsiveness on resize.
*/
window.addEventListener('resize', () => {
const dataAttr = document.body.dataset.runComparison;
if (!dataAttr) return;
try {
const data = JSON.parse(dataAttr);
renderBandwidthChart(data);
renderRetryTailChart(data);
} catch (err) {
console.error('Failed to re-render charts on resize:', err);
}
});