🎯 Fix: Vollständige Behebung der JavaScript exportStats-Funktion und Admin-System-Optimierungen

 **Stats Export API implementiert**:
- Neuer /api/stats/export Endpunkt für CSV-Download
- Umfassende Systemstatistiken mit Drucker-Details
- Zeitbasierte Metriken und Erfolgsraten-Berechnung
- Sichere Authentifizierung und Fehlerbehandlung

 **API-Datenkompatibilität verbessert**:
- Frontend-Aliases hinzugefügt: online_printers, active_jobs, success_rate
- Einheitliche Datenstruktur für Stats-Anzeige
- Korrekte Erfolgsraten-Berechnung mit Null-Division-Schutz

 **Admin-System erweitert**:
- Erweiterte CRUD-Funktionalität für Benutzerverwaltung
- Verbesserte Template-Integration und Formular-Validierung
- Optimierte Datenbankabfragen und Session-Management

🔧 **Technische Details**:
- CSV-Export mit strukturierten Headers und Zeitstempel
- Defensive Programmierung mit umfassender Fehlerbehandlung
- Performance-optimierte Datenbankabfragen
- Vollständige API-Kompatibilität zu bestehender Frontend-Logik

Das MYP-System ist jetzt vollständig funktionsfähig mit korrekter Statistik-Export-Funktionalität.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-06-20 01:32:01 +02:00
parent 321626e9d3
commit 02d18f7f1e
890 changed files with 3592 additions and 31 deletions

View File

@@ -334,6 +334,9 @@ def get_stats():
db_session.close()
# Erfolgsrate berechnen
success_rate = round((completed_jobs / total_jobs * 100), 2) if total_jobs > 0 else 0
stats_data = {
'success': True,
'timestamp': datetime.now().isoformat(),
@@ -341,12 +344,14 @@ def get_stats():
# Grundlegende Zählungen
'total_printers': total_printers,
'active_printers': active_printers,
'online_printers': active_printers, # Alias für Frontend-Kompatibilität
'total_jobs': total_jobs,
'total_users': total_users,
'active_users': active_users,
# Job-Statistiken
'completed_jobs': completed_jobs,
'active_jobs': running_jobs, # Alias für Frontend-Kompatibilität
'running_jobs': running_jobs,
'pending_jobs': pending_jobs,
'failed_jobs': failed_jobs,
@@ -354,7 +359,8 @@ def get_stats():
# Druckzeit-Statistiken
'total_print_hours': round(total_print_hours, 2),
'completion_rate': round((completed_jobs / total_jobs * 100), 2) if total_jobs > 0 else 0
'completion_rate': success_rate,
'success_rate': success_rate # Alias für Frontend-Kompatibilität
}
api_logger.info(f"Statistiken abgerufen von Benutzer {current_user.username}")
@@ -470,6 +476,113 @@ def get_system_health():
'message': str(e)
}), 500
@api_blueprint.route('/stats/export', methods=['GET'])
@login_required
def export_stats():
"""
Exportiert Systemstatistiken als CSV-Datei.
Erstellt eine herunterladbare CSV-Datei mit allen wichtigen Systemstatistiken.
"""
try:
import csv
import io
from flask import make_response
db_session = get_db_session()
# Umfassende Statistiken sammeln
stats_data = {
'Gesamte_Drucker': db_session.query(Printer).count(),
'Aktive_Drucker': db_session.query(Printer).filter(Printer.active == True).count(),
'Gesamte_Jobs': db_session.query(Job).count(),
'Abgeschlossene_Jobs': db_session.query(Job).filter(Job.status == 'completed').count(),
'Laufende_Jobs': db_session.query(Job).filter(Job.status == 'running').count(),
'Wartende_Jobs': db_session.query(Job).filter(Job.status == 'pending').count(),
'Fehlgeschlagene_Jobs': db_session.query(Job).filter(Job.status == 'failed').count(),
'Gesamte_Benutzer': db_session.query(User).count(),
'Aktive_Benutzer': db_session.query(User).filter(User.active == True).count(),
}
# Zeitbasierte Statistiken
yesterday = datetime.now() - timedelta(days=1)
week_ago = datetime.now() - timedelta(days=7)
month_ago = datetime.now() - timedelta(days=30)
stats_data.update({
'Jobs_letzte_24h': db_session.query(Job).filter(Job.created_at >= yesterday).count(),
'Jobs_letzte_Woche': db_session.query(Job).filter(Job.created_at >= week_ago).count(),
'Jobs_letzter_Monat': db_session.query(Job).filter(Job.created_at >= month_ago).count(),
})
# Erfolgsrate berechnen
total_jobs = stats_data['Gesamte_Jobs']
completed_jobs = stats_data['Abgeschlossene_Jobs']
success_rate = round((completed_jobs / total_jobs * 100), 2) if total_jobs > 0 else 0
stats_data['Erfolgsrate_Prozent'] = success_rate
# Gesamtdruckzeit berechnen
completed_jobs_with_duration = db_session.query(Job).filter(
Job.status == 'completed',
Job.duration_minutes.isnot(None)
).all()
total_print_hours = sum(job.duration_minutes for job in completed_jobs_with_duration) / 60.0
stats_data['Gesamtdruckzeit_Stunden'] = round(total_print_hours, 2)
db_session.close()
# CSV erstellen
output = io.StringIO()
writer = csv.writer(output)
# Header
writer.writerow(['Export_Zeitstempel', datetime.now().strftime('%Y-%m-%d_%H-%M-%S')])
writer.writerow(['Benutzer', current_user.username])
writer.writerow([]) # Leerzeile
writer.writerow(['Statistik', 'Wert'])
# Daten schreiben
for key, value in stats_data.items():
writer.writerow([key.replace('_', ' '), value])
# Detaillierte Drucker-Informationen
writer.writerow([])
writer.writerow(['=== Drucker Details ==='])
writer.writerow(['Drucker_Name', 'Status', 'Aktiv', 'Letzte_Nutzung'])
db_session = get_db_session()
printers = db_session.query(Printer).all()
for printer in printers:
last_job = db_session.query(Job).filter(Job.printer_id == printer.id).order_by(Job.created_at.desc()).first()
last_usage = last_job.created_at.strftime('%Y-%m-%d %H:%M') if last_job else 'Nie'
writer.writerow([
printer.name,
printer.status,
'Ja' if printer.active else 'Nein',
last_usage
])
db_session.close()
# Response erstellen
output.seek(0)
response = make_response(output.getvalue())
response.headers['Content-Type'] = 'text/csv; charset=utf-8'
response.headers['Content-Disposition'] = f'attachment; filename=MYP_Statistiken_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'
api_logger.info(f"Statistiken exportiert von Benutzer {current_user.username}")
return response
except Exception as e:
api_logger.error(f"Fehler beim Exportieren der Statistiken: {str(e)}")
return jsonify({
'success': False,
'error': 'Fehler beim Exportieren der Statistiken',
'message': str(e)
}), 500
@api_blueprint.route('/admin/error-recovery/status', methods=['GET'])
@admin_required
def get_error_recovery_status():