Add results_visualization/js/visualization.js

This commit is contained in:
Mika 2026-01-22 11:58:36 +00:00
parent 4a562fc18c
commit f0db07d35a

View file

@ -0,0 +1,121 @@
"use strict";
/**
* @module visualization
* @description Verarbeitung und Darstellung aggregierter Zeitmessungsdaten.
* Verantwortlich für Rendering und Update von Charts und Summary Cards.
*/
/**
* Rendert interaktive Diagramme auf Basis der geladenen Daten.
* @param {Array<Object>} data - Aggregierte Ergebnisse von der /results API.
* @param {HTMLElement} container - Container-Element zur Anzeige des Diagramms.
* @returns {void}
*/
export function renderCharts(data, container) {
if (!Array.isArray(data) || !container) return;
container.innerHTML = "";
const canvas = document.createElement("canvas");
canvas.id = "chart-results";
canvas.setAttribute("aria-label", "Zeitmessungsdiagramm");
canvas.setAttribute("role", "img");
container.appendChild(canvas);
const ctx = canvas.getContext("2d");
const runTypes = [...new Set(data.map(d => d.run_type))];
const labels = data.map(d => d.run_id);
const datasets = runTypes.map((type) => {
const subset = data.filter(d => d.run_type === type);
return {
label: type,
data: subset.map(r => r.metrics?.latency_mean ?? 0),
borderColor: type === "pinned" ? "#2a9d8f" : "#e76f51",
backgroundColor: type === "pinned" ? "rgba(42,157,143,0.4)" : "rgba(231,111,81,0.4)",
borderWidth: 2,
tension: 0.3,
};
});
if (window.currentChart) {
window.currentChart.destroy();
}
window.currentChart = new Chart(ctx, {
type: "line",
data: { labels, datasets },
options: {
responsive: true,
interaction: { mode: "index", intersect: false },
scales: {
x: { title: { display: true, text: "Run ID" } },
y: { title: { display: true, text: "Latenz (ms)" }, beginAtZero: true }
},
plugins: {
legend: { position: "top" },
tooltip: {
callbacks: {
label: ctx => `${ctx.dataset.label}: ${ctx.parsed.y.toFixed(2)} ms`
}
}
}
}
});
renderSummary(data);
}
/**
* Aktualisiert Diagramme und Statistiken basierend auf Filtern.
* @param {Object} filters - Nutzerdefinierte Filterparameter.
* @param {Array<Object>} data - Aktuelle Ergebnisdaten.
* @returns {void}
*/
export function updateVisualization(filters, data) {
if (typeof filters !== "object" || !Array.isArray(data)) return;
const filteredData = data.filter(item => {
const matchRunType = !filters.runType || item.run_type === filters.runType;
const matchMetric = !filters.metric || Object.keys(item.metrics || {}).includes(filters.metric);
return matchRunType && matchMetric;
});
const container = document.querySelector("#chart-container");
renderCharts(filteredData, container);
}
/**
* Rendert aggregierte Kennzahlen als Summary Cards unterhalb des Diagramms.
* @param {Array<Object>} data
*/
function renderSummary(data) {
const summaryContainer = document.querySelector("#summary-container");
if (!summaryContainer) return;
const total = data.length;
const avgLatency = (data.reduce((acc, val) => acc + (val.metrics?.latency_mean ?? 0), 0) / Math.max(total, 1)).toFixed(2);
const avgVariance = (data.reduce((acc, val) => acc + (val.metrics?.variance ?? 0), 0) / Math.max(total, 1)).toFixed(3);
const avgStability = (data.reduce((acc, val) => acc + (val.metrics?.stability_index ?? 0), 0) / Math.max(total, 1)).toFixed(3);
summaryContainer.innerHTML = `
<div class="summary-card">
<h3>Runs insgesamt</h3>
<p>${total}</p>
</div>
<div class="summary-card">
<h3>Ø Latenz</h3>
<p>${avgLatency} ms</p>
</div>
<div class="summary-card">
<h3>Ø Varianz</h3>
<p>${avgVariance}</p>
</div>
<div class="summary-card">
<h3>Ø Stabilität</h3>
<p>${avgStability}</p>
</div>
`;
}