🎯 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

@@ -27,7 +27,7 @@ from datetime import datetime, timedelta
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash, current_app
from flask_login import login_required, current_user
from functools import wraps
from models import User, Printer, Job, get_cached_session, Stats, SystemLog, PlugStatusLog, GuestRequest
from models import User, Printer, Job, get_cached_session, Stats, SystemLog, PlugStatusLog, GuestRequest, UserPermission
from utils.logging_config import get_logger
# ===== BLUEPRINT-KONFIGURATION =====
@@ -216,24 +216,46 @@ def edit_user_page(user_id):
@admin_blueprint.route("/users/create", methods=["POST"])
@admin_required
def create_user():
"""Erstellt einen neuen Benutzer"""
"""Erstellt einen neuen Benutzer mit erweiterten Feldern und Berechtigungen"""
try:
# Form-Daten aus POST-Request
# === GRUNDDATEN ===
username = request.form.get('username', '').strip()
email = request.form.get('email', '').strip()
name = request.form.get('name', '').strip()
password = request.form.get('password', '')
role = request.form.get('role', 'user')
# === ERWEITERTE PROFIL-INFORMATIONEN ===
department = request.form.get('department', '').strip()
position = request.form.get('position', '').strip()
phone = request.form.get('phone', '').strip()
active = request.form.get('active') == 'on'
bio = request.form.get('bio', '').strip()
# Validierung
# === BENUTZEREINSTELLUNGEN ===
theme_preference = request.form.get('theme_preference', 'auto')
language_preference = request.form.get('language_preference', 'de')
email_notifications = request.form.get('email_notifications') == 'on'
browser_notifications = request.form.get('browser_notifications') == 'on'
# === GRANULARE BERECHTIGUNGEN ===
can_start_jobs = request.form.get('can_start_jobs') == 'on'
needs_approval = request.form.get('needs_approval') == 'on'
can_approve_jobs = request.form.get('can_approve_jobs') == 'on'
can_manage_printers = request.form.get('can_manage_printers') == 'on'
can_view_all_jobs = request.form.get('can_view_all_jobs') == 'on'
can_access_admin_panel = request.form.get('can_access_admin_panel') == 'on'
can_manage_users = request.form.get('can_manage_users') == 'on'
can_access_energy_monitoring = request.form.get('can_access_energy_monitoring') == 'on'
# === VALIDIERUNG ===
if not username or not email or not password:
flash("Benutzername, E-Mail und Passwort sind erforderlich", "error")
return redirect(url_for('admin.add_user_page'))
if len(password) < 6:
flash("Das Passwort muss mindestens 6 Zeichen lang sein", "error")
return redirect(url_for('admin.add_user_page'))
with get_cached_session() as db_session:
# Prüfen ob Benutzer bereits existiert
existing_user = db_session.query(User).filter(
@@ -244,33 +266,77 @@ def create_user():
flash("Benutzer mit diesem Namen oder E-Mail existiert bereits", "error")
return redirect(url_for('admin.add_user_page'))
# Neuen Benutzer erstellen
# === NEUEN BENUTZER ERSTELLEN ===
new_user = User(
username=username,
email=email,
name=name if name else None,
name=name if name else username,
role=role,
active=True,
created_at=datetime.now(),
updated_at=datetime.now(),
# Erweiterte Profil-Felder
department=department if department else None,
position=position if position else None,
phone=phone if phone else None,
active=active,
created_at=datetime.now(),
updated_at=datetime.now()
bio=bio if bio else None,
# Benutzereinstellungen
theme_preference=theme_preference,
language_preference=language_preference,
email_notifications=email_notifications,
browser_notifications=browser_notifications,
dashboard_layout='default',
compact_mode=False,
show_completed_jobs=True,
auto_refresh_interval=30,
auto_logout_timeout=0
)
# Passwort hashen
new_user.set_password(password)
# User zur Session hinzufügen und committen
db_session.add(new_user)
db_session.flush() # Flush um user.id zu erhalten
# === BERECHTIGUNGEN ERSTELLEN ===
# Bei Admin-Rolle automatisch alle Berechtigungen setzen
if role == 'admin':
can_approve_jobs = True
can_manage_printers = True
can_view_all_jobs = True
can_access_admin_panel = True
can_manage_users = True
can_access_energy_monitoring = True
needs_approval = False # Admins brauchen keine Genehmigung
user_permissions = UserPermission(
user_id=new_user.id,
can_start_jobs=can_start_jobs,
needs_approval=needs_approval,
can_approve_jobs=can_approve_jobs,
can_manage_printers=can_manage_printers,
can_view_all_jobs=can_view_all_jobs,
can_access_admin_panel=can_access_admin_panel,
can_manage_users=can_manage_users,
can_access_energy_monitoring=can_access_energy_monitoring,
created_at=datetime.now(),
updated_at=datetime.now()
)
db_session.add(user_permissions)
db_session.commit()
admin_logger.info(f"Neuer Benutzer '{username}' erstellt von {current_user.username}")
flash(f"Benutzer '{username}' erfolgreich erstellt", "success")
admin_logger.info(f"Neuer Benutzer '{username}' (Rolle: {role}) mit erweiterten Berechtigungen erstellt von {current_user.username}")
flash(f"Benutzer '{username}' erfolgreich erstellt mit allen Einstellungen und Berechtigungen", "success")
return redirect(url_for('admin.users_overview'))
except Exception as e:
admin_logger.error(f"Fehler beim Erstellen des Benutzers: {str(e)}")
admin_logger.error(f"Traceback: {traceback.format_exc()}")
flash("Fehler beim Erstellen des Benutzers", "error")
return redirect(url_for('admin.add_user_page'))
@@ -4053,6 +4119,142 @@ def get_users_api():
admin_api_logger.error(f"Fehler beim Abrufen der Benutzer: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen der Benutzer"}), 500
# ===== BENUTZER-MANAGEMENT API ENDPOINTS =====
@admin_api_blueprint.route('/users/<int:user_id>/role', methods=['POST'])
@admin_required
def update_user_role_api(user_id):
"""API-Endpunkt zum Aktualisieren der Benutzerrolle"""
try:
data = request.get_json()
new_role = data.get('role')
if new_role not in ['user', 'admin']:
return jsonify({
'success': False,
'error': 'Ungültige Rolle'
}), 400
with get_cached_session() as db_session:
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
return jsonify({
'success': False,
'error': 'Benutzer nicht gefunden'
}), 404
# Verhindern dass der letzte Admin degradiert wird
if user.is_admin and new_role != 'admin':
admin_count = db_session.query(User).filter(User.role == 'admin').count()
if admin_count <= 1:
return jsonify({
'success': False,
'error': 'Kann den letzten Administrator nicht degradieren'
}), 400
user.role = new_role
user.updated_at = datetime.now()
db_session.commit()
admin_api_logger.info(f"Benutzerrolle für '{user.username}' zu '{new_role}' geändert von {current_user.username}")
return jsonify({
'success': True,
'message': f'Rolle erfolgreich zu {new_role} geändert'
})
except Exception as e:
admin_api_logger.error(f"Fehler beim Aktualisieren der Benutzerrolle: {str(e)}")
return jsonify({
'success': False,
'error': 'Systemfehler beim Aktualisieren der Rolle'
}), 500
@admin_api_blueprint.route('/users/<int:user_id>/status', methods=['POST'])
@admin_required
def update_user_status_api(user_id):
"""API-Endpunkt zum Aktualisieren des Benutzerstatus"""
try:
data = request.get_json()
is_active = data.get('active', False)
with get_cached_session() as db_session:
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
return jsonify({
'success': False,
'error': 'Benutzer nicht gefunden'
}), 404
# Verhindern dass sich der Admin selbst deaktiviert
if user.id == current_user.id and not is_active:
return jsonify({
'success': False,
'error': 'Sie können sich nicht selbst deaktivieren'
}), 400
user.active = is_active
user.updated_at = datetime.now()
db_session.commit()
admin_api_logger.info(f"Benutzerstatus für '{user.username}' zu {'aktiv' if is_active else 'inaktiv'} geändert von {current_user.username}")
return jsonify({
'success': True,
'message': f'Benutzer erfolgreich {"aktiviert" if is_active else "deaktiviert"}'
})
except Exception as e:
admin_api_logger.error(f"Fehler beim Aktualisieren des Benutzerstatus: {str(e)}")
return jsonify({
'success': False,
'error': 'Systemfehler beim Aktualisieren des Status'
}), 500
@admin_api_blueprint.route('/users/<int:user_id>/reset-password', methods=['POST'])
@admin_required
def reset_user_password_api(user_id):
"""API-Endpunkt zum Zurücksetzen des Benutzerpassworts"""
try:
data = request.get_json()
new_password = data.get('new_password')
if not new_password or len(new_password) < 6:
return jsonify({
'success': False,
'error': 'Passwort muss mindestens 6 Zeichen lang sein'
}), 400
with get_cached_session() as db_session:
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
return jsonify({
'success': False,
'error': 'Benutzer nicht gefunden'
}), 404
# Passwort aktualisieren
user.set_password(new_password)
user.updated_at = datetime.now()
db_session.commit()
admin_api_logger.info(f"Passwort für Benutzer '{user.username}' zurückgesetzt von {current_user.username}")
return jsonify({
'success': True,
'message': 'Passwort erfolgreich zurückgesetzt'
})
except Exception as e:
admin_api_logger.error(f"Fehler beim Zurücksetzen des Passworts: {str(e)}")
return jsonify({
'success': False,
'error': 'Systemfehler beim Zurücksetzen des Passworts'
}), 500
# ===== SYSTEM ERROR RECOVERY ENDPOINTS =====
@admin_api_blueprint.route('/error-recovery/status', methods=['GET'])