Add experiment_results_visualization/js/ui.js

This commit is contained in:
Mika 2026-01-23 12:53:26 +00:00
parent 0e33154c45
commit 0a3b8c027a

View file

@ -0,0 +1,161 @@
/**
* UI Update Module for Experiment Results Visualization
* Handles summary cards, charts, and table rendering.
* @module ui
*/
/**
* Renders statistical summary cards.
* @param {Object} experiment_data - Contains pinned and unpinned experiment results.
*/
export function renderSummary(experiment_data) {
const container = document.querySelector('.summary-section');
if (!container) return;
container.innerHTML = '';
const createCard = (title, value) => {
const card = document.createElement('div');
card.className = 'summary-card';
const heading = document.createElement('h3');
heading.textContent = title;
const valElem = document.createElement('p');
valElem.textContent = value;
card.appendChild(heading);
card.appendChild(valElem);
return card;
};
const pinned = experiment_data?.pinned ?? [];
const unpinned = experiment_data?.unpinned ?? [];
const pinnedLatencies = pinned.map(r => r.latency_ms ?? 0);
const unpinnedLatencies = unpinned.map(r => r.latency_ms ?? 0);
const percentiles = (arr, p) => {
if (!arr.length) return 0;
const sorted = [...arr].sort((a, b) => a - b);
const idx = Math.floor((p / 100) * sorted.length);
return sorted[idx] ?? 0;
};
const pinnedP95 = percentiles(pinnedLatencies, 95).toFixed(2);
const pinnedP99 = percentiles(pinnedLatencies, 99).toFixed(2);
const unpinnedP95 = percentiles(unpinnedLatencies, 95).toFixed(2);
const unpinnedP99 = percentiles(unpinnedLatencies, 99).toFixed(2);
const correlation = (() => {
if (pinnedLatencies.length !== unpinnedLatencies.length || !pinnedLatencies.length) return 0;
const avgPinned = pinnedLatencies.reduce((a,b)=>a+b,0)/pinnedLatencies.length;
const avgUnpinned = unpinnedLatencies.reduce((a,b)=>a+b,0)/unpinnedLatencies.length;
let num=0,den1=0,den2=0;
for(let i=0;i<pinnedLatencies.length;i++){
const x = pinnedLatencies[i] - avgPinned;
const y = unpinnedLatencies[i] - avgUnpinned;
num += x*y;
den1 += x*x;
den2 += y*y;
}
return (num / Math.sqrt(den1 * den2)).toFixed(3);
})();
container.appendChild(createCard('Pinned p95', `${pinnedP95} ms`));
container.appendChild(createCard('Pinned p99', `${pinnedP99} ms`));
container.appendChild(createCard('Unpinned p95', `${unpinnedP95} ms`));
container.appendChild(createCard('Unpinned p99', `${unpinnedP99} ms`));
container.appendChild(createCard('Correlation (latency)', correlation));
}
/**
* Renders comparative charts for pinned vs unpinned runs.
* Expects charting library available in global scope (e.g., Chart.js or similar).
* @param {Object} experiment_data - Experiment dataset
*/
export function renderCharts(experiment_data) {
const pinned = experiment_data?.pinned ?? [];
const unpinned = experiment_data?.unpinned ?? [];
const pinnedCanvas = document.getElementById('chart-pinned');
const unpinnedCanvas = document.getElementById('chart-unpinned');
if (!pinnedCanvas || !unpinnedCanvas) return;
const pinnedData = pinned.map(r => r.latency_ms ?? 0);
const unpinnedData = unpinned.map(r => r.latency_ms ?? 0);
const labels = pinned.map((_, i) => `Run ${i+1}`);
const createChart = (ctx, label, data, color) => {
if (!window.Chart) return;
if (ctx.__chartInstance) ctx.__chartInstance.destroy();
ctx.__chartInstance = new Chart(ctx, {
type: 'line',
data: {
labels,
datasets: [{
label,
data,
borderColor: color,
backgroundColor: 'transparent',
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: false }
}
}
});
};
createChart(pinnedCanvas.getContext('2d'), 'Pinned Latency (ms)', pinnedData, '#4caf50');
createChart(unpinnedCanvas.getContext('2d'), 'Unpinned Latency (ms)', unpinnedData, '#f44336');
}
/**
* Renders a table view of runs data.
* @param {Object} experiment_data - Dataset from API including pinned and unpinned.
*/
export function renderTable(experiment_data) {
const container = document.querySelector('.runs-table');
if (!container) return;
const pinned = experiment_data?.pinned ?? [];
const unpinned = experiment_data?.unpinned ?? [];
const table = document.createElement('table');
table.className = 'runs-table__inner';
const thead = document.createElement('thead');
thead.innerHTML = `
<tr>
<th>Run ID</th>
<th>Type</th>
<th>Latency (ms)</th>
<th>Timestamp</th>
</tr>
`;
table.appendChild(thead);
const tbody = document.createElement('tbody');
const makeRow = (run, type) => {
const tr = document.createElement('tr');
const tdId = document.createElement('td');
const tdType = document.createElement('td');
const tdLatency = document.createElement('td');
const tdTime = document.createElement('td');
tdId.textContent = run.id ?? '-';
tdType.textContent = type;
tdLatency.textContent = run.latency_ms?.toFixed(2) ?? '-';
tdTime.textContent = new Date(run.timestamp ?? Date.now()).toLocaleString();
tr.append(tdId, tdType, tdLatency, tdTime);
return tr;
};
pinned.forEach(run => tbody.appendChild(makeRow(run, 'pinned')));
unpinned.forEach(run => tbody.appendChild(makeRow(run, 'unpinned')));
table.appendChild(tbody);
container.innerHTML = '';
container.appendChild(table);
}