161 lines
5.3 KiB
JavaScript
161 lines
5.3 KiB
JavaScript
/**
|
|
* 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);
|
|
}
|