{% extends "base.html" %} {% block title %}Statistiken - MYP API Tester{% endblock %} {% block content %} <div class="row"> <div class="col-md-12 mb-4"> <div class="card"> <div class="card-header"> <h4 class="mb-0">Systemstatistiken</h4> </div> <div class="card-body"> <form class="api-form mb-3" data-url="/api/stats" data-method="GET" data-response="statsResponse"> <button type="submit" class="btn btn-primary">Statistiken aktualisieren</button> </form> <div class="row" id="statsContainer"> <!-- Wird dynamisch gefüllt --> </div> <!-- Problem-Drucker-Bereich --> <div class="row mt-4"> <div class="col-md-12"> <div class="card"> <div class="card-header bg-warning text-dark"> <h5 class="mb-0">Drucker mit Verbindungsproblemen</h5> </div> <div class="card-body" id="problemPrintersContainer"> <div class="alert alert-info">Keine Verbindungsprobleme festgestellt.</div> </div> </div> </div> </div> <!-- Uptime-Grafik --> <div class="row mt-4"> <div class="col-md-12"> <div class="card"> <div class="card-header bg-dark text-white"> <h5 class="mb-0">Steckdosen-Verfügbarkeit</h5> </div> <div class="card-body"> <form class="api-form mb-3" data-url="/api/uptime" data-method="GET" data-response="uptimeResponse"> <button type="submit" class="btn btn-primary">Uptime-Daten laden</button> </form> <canvas id="uptimeChart" width="100%" height="300"></canvas> </div> </div> </div> </div> <!-- API-Antworten --> <div class="row mt-4"> <div class="col-md-6"> <h6>Stats API-Antwort:</h6> <pre class="api-response" id="statsResponse"></pre> </div> <div class="col-md-6"> <h6>Uptime API-Antwort:</h6> <pre class="api-response" id="uptimeResponse"></pre> </div> </div> </div> </div> </div> </div> {% endblock %} {% block scripts %} <!-- Chart.js für Diagramme --> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> <script> let uptimeChart; document.addEventListener('DOMContentLoaded', function() { // Statistiken laden document.querySelector('form[data-url="/api/stats"]').dispatchEvent(new Event('submit')); document.querySelector('form[data-url="/api/uptime"]').dispatchEvent(new Event('submit')); // Statistiken aktualisieren, wenn API-Antwort geladen wird const statsResponse = document.getElementById('statsResponse'); const statsObserver = new MutationObserver(function(mutations) { try { const stats = JSON.parse(statsResponse.textContent); updateStatsDisplay(stats); updateProblemPrinters(stats); } catch (e) { console.error('Fehler beim Parsen der Statistik-Daten:', e); } }); statsObserver.observe(statsResponse, { childList: true, characterData: true, subtree: true }); // Uptime-Daten aktualisieren, wenn API-Antwort geladen wird const uptimeResponse = document.getElementById('uptimeResponse'); const uptimeObserver = new MutationObserver(function(mutations) { try { const uptime = JSON.parse(uptimeResponse.textContent); updateUptimeChart(uptime); } catch (e) { console.error('Fehler beim Parsen der Uptime-Daten:', e); } }); uptimeObserver.observe(uptimeResponse, { childList: true, characterData: true, subtree: true }); // Periodische Aktualisierung setInterval(function() { document.querySelector('form[data-url="/api/stats"]').dispatchEvent(new Event('submit')); document.querySelector('form[data-url="/api/uptime"]').dispatchEvent(new Event('submit')); }, 60000); // Alle 60 Sekunden aktualisieren }); function updateStatsDisplay(stats) { const container = document.getElementById('statsContainer'); container.innerHTML = ''; // Drucker-Statistiken const printerStats = document.createElement('div'); printerStats.className = 'col-md-4 mb-3'; printerStats.innerHTML = ` <div class="card h-100"> <div class="card-header bg-primary text-white"> <h5 class="mb-0">Drucker</h5> </div> <div class="card-body"> <div class="d-flex justify-content-between mb-2"> <span>Gesamt:</span> <span>${stats.printers.total}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Verfügbar:</span> <span>${stats.printers.available}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Auslastung:</span> <span>${Math.round(stats.printers.utilization_rate * 100)}%</span> </div> <div class="progress mt-3 mb-3"> <div class="progress-bar" role="progressbar" style="width: ${Math.round(stats.printers.utilization_rate * 100)}%"> ${Math.round(stats.printers.utilization_rate * 100)}% </div> </div> <hr /> <div class="d-flex justify-content-between mb-2"> <span>Online:</span> <span>${stats.printers.online}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Offline:</span> <span>${stats.printers.offline}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Verbindungsrate:</span> <span>${Math.round(stats.printers.connectivity_rate * 100)}%</span> </div> <div class="progress mt-3"> <div class="progress-bar bg-success" role="progressbar" style="width: ${Math.round(stats.printers.connectivity_rate * 100)}%"> ${Math.round(stats.printers.connectivity_rate * 100)}% </div> </div> </div> </div> `; // Job-Statistiken const jobStats = document.createElement('div'); jobStats.className = 'col-md-4 mb-3'; jobStats.innerHTML = ` <div class="card h-100"> <div class="card-header bg-success text-white"> <h5 class="mb-0">Druckaufträge</h5> </div> <div class="card-body"> <div class="d-flex justify-content-between mb-2"> <span>Gesamt:</span> <span>${stats.jobs.total}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Aktiv:</span> <span>${stats.jobs.active}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Abgeschlossen:</span> <span>${stats.jobs.completed}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Durchschnittliche Dauer:</span> <span>${stats.jobs.avg_duration} Minuten</span> </div> </div> </div> `; // Benutzer- und Uptime-Statistiken const userStats = document.createElement('div'); userStats.className = 'col-md-4 mb-3'; userStats.innerHTML = ` <div class="card h-100"> <div class="card-header bg-info text-white"> <h5 class="mb-0">System</h5> </div> <div class="card-body"> <div class="d-flex justify-content-between mb-2"> <span>Benutzer:</span> <span>${stats.users.total}</span> </div> <hr /> <div class="d-flex justify-content-between mb-2"> <span>Verbindungsausfälle (7 Tage):</span> <span>${stats.uptime.outages_last_7_days}</span> </div> <div class="d-flex justify-content-between mb-2"> <span>Aktuelle Probleme:</span> <span>${stats.uptime.problem_printers.length}</span> </div> </div> </div> `; container.appendChild(printerStats); container.appendChild(jobStats); container.appendChild(userStats); } function updateProblemPrinters(stats) { const container = document.getElementById('problemPrintersContainer'); const problemPrinters = stats.uptime.problem_printers; if (problemPrinters.length === 0) { container.innerHTML = '<div class="alert alert-info">Keine Verbindungsprobleme festgestellt.</div>'; return; } let html = '<div class="table-responsive"><table class="table table-striped">'; html += '<thead><tr><th>Drucker</th><th>Status</th><th>Offline seit</th><th>Dauer</th></tr></thead>'; html += '<tbody>'; problemPrinters.forEach(printer => { let offlineSince = 'Unbekannt'; let duration = 'Unbekannt'; if (printer.last_seen) { try { const lastSeen = new Date(printer.last_seen); const now = new Date(); const diffSeconds = Math.floor((now - lastSeen) / 1000); const hours = Math.floor(diffSeconds / 3600); const minutes = Math.floor((diffSeconds % 3600) / 60); offlineSince = lastSeen.toLocaleString(); duration = `${hours}h ${minutes}m`; } catch (e) { console.error('Fehler beim Berechnen der Offline-Zeit:', e); } } html += `<tr> <td>${printer.name}</td> <td><span class="badge bg-danger">Offline</span></td> <td>${offlineSince}</td> <td>${duration}</td> </tr>`; }); html += '</tbody></table></div>'; container.innerHTML = html; } function updateUptimeChart(uptimeData) { // Wenn keine Daten vorhanden sind, nichts tun if (!uptimeData || !uptimeData.sockets || uptimeData.sockets.length === 0) { return; } // Daten für das Diagramm vorbereiten const socketNames = []; const datasets = []; const colors = { online: 'rgba(40, 167, 69, 0.7)', offline: 'rgba(220, 53, 69, 0.7)', unknown: 'rgba(108, 117, 125, 0.7)' }; // Zeitraum für das Diagramm (letzten 7 Tage) const endDate = new Date(); const startDate = new Date(); startDate.setDate(startDate.getDate() - 7); // Für jede Steckdose uptimeData.sockets.forEach(socket => { socketNames.push(socket.name); // Sortiere Ereignisse nach Zeitstempel if (socket.events) { socket.events.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp)); // Erstelle einen Datensatz für diese Steckdose const data = []; // Füge Ereignisse zum Datensatz hinzu socket.events.forEach(event => { data.push({ x: new Date(event.timestamp), y: event.status === 'online' ? 1 : 0, status: event.status, duration: event.duration_seconds ? formatDuration(event.duration_seconds) : null }); }); // Füge aktuellen Status hinzu if (socket.current_status) { data.push({ x: new Date(), y: socket.current_status.connection_status === 'online' ? 1 : 0, status: socket.current_status.connection_status, duration: null }); } datasets.push({ label: socket.name, data: data, stepped: true, borderColor: colors[socket.current_status?.connection_status || 'unknown'], backgroundColor: colors[socket.current_status?.connection_status || 'unknown'], fill: false }); } }); // Chart.js Konfiguration const ctx = document.getElementById('uptimeChart').getContext('2d'); // Wenn Chart bereits existiert, zerstöre ihn if (uptimeChart) { uptimeChart.destroy(); } // Erstelle neuen Chart uptimeChart = new Chart(ctx, { type: 'line', data: { datasets: datasets }, options: { responsive: true, plugins: { tooltip: { callbacks: { label: function(context) { const point = context.raw; let label = context.dataset.label || ''; label += ': ' + (point.status === 'online' ? 'Online' : 'Offline'); if (point.duration) { label += ' (Dauer: ' + point.duration + ')'; } return label; } } } }, scales: { x: { type: 'time', time: { unit: 'day' }, min: startDate, max: endDate }, y: { min: -0.1, max: 1.1, ticks: { callback: function(value) { return value === 0 ? 'Offline' : value === 1 ? 'Online' : ''; } } } } } }); } function formatDuration(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); return `${hours}h ${minutes}m`; } </script> {% endblock %}