121 lines
3.7 KiB
JavaScript
121 lines
3.7 KiB
JavaScript
"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>
|
|
`;
|
|
}
|