Add data_visualization/js/visualization.js
This commit is contained in:
parent
3419c4ddd3
commit
b4d509a745
1 changed files with 145 additions and 0 deletions
145
data_visualization/js/visualization.js
Normal file
145
data_visualization/js/visualization.js
Normal 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);
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue