/** * 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); }