diff --git a/experiment_results_visualization/js/ui.js b/experiment_results_visualization/js/ui.js new file mode 100644 index 0000000..7f3fe70 --- /dev/null +++ b/experiment_results_visualization/js/ui.js @@ -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 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 = ` + + Run ID + Type + Latency (ms) + Timestamp + + `; + 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); +}