diff --git a/backend/utils/test_korrekturen.py b/MIGRATION_LOG.md similarity index 100% rename from backend/utils/test_korrekturen.py rename to MIGRATION_LOG.md diff --git a/VERIFIKATION_KONSOLIDIERUNG.md b/VERIFIKATION_KONSOLIDIERUNG.md new file mode 100644 index 000000000..0519ecba6 --- /dev/null +++ b/VERIFIKATION_KONSOLIDIERUNG.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/app.py b/backend/app.py index 8d3422e08..da778541a 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1765,7 +1765,7 @@ def main(): # Performance-Monitoring aktivieren if getattr(ProductionConfig, 'ENABLE_PERFORMANCE_MONITORING', False): try: - from utils.performance_monitor import init_performance_monitoring + from utils.system_utilities import init_performance_monitoring init_performance_monitoring(app) app_logger.info("[PRODUCTION] ✅ Performance-Monitoring aktiviert") except ImportError: diff --git a/backend/blueprints/deprecated/user.py b/backend/blueprints/deprecated/user.py deleted file mode 100644 index 2fffa2ff9..000000000 --- a/backend/blueprints/deprecated/user.py +++ /dev/null @@ -1,359 +0,0 @@ -""" -Benutzer-Blueprint für das 3D-Druck-Management-System - -Dieses Modul enthält alle Benutzer-spezifischen Routen und Funktionen, -einschließlich Profilverwaltung, Einstellungen und Passwort-Änderung. -""" - -import json -from datetime import datetime -from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash, make_response -from flask_login import login_required, current_user -from werkzeug.security import check_password_hash -from models import User, get_db_session -from utils.logging_config import get_logger - -# Blueprint erstellen -user_blueprint = Blueprint('user', __name__, url_prefix='/user') - -# Logger initialisieren -user_logger = get_logger("user") - -@user_blueprint.route("/profile", methods=["GET"]) -@login_required -def profile(): - """Benutzerprofil anzeigen""" - return render_template('user/profile.html', user=current_user) - -@user_blueprint.route("/settings", methods=["GET"]) -@login_required -def settings(): - """Benutzereinstellungen anzeigen""" - return render_template('user/settings.html', user=current_user) - -@user_blueprint.route("/update-profile", methods=["POST"]) -@login_required -def update_profile(): - """Benutzerprofil aktualisieren (Form-basiert)""" - try: - db_session = get_db_session() - user = db_session.query(User).filter(User.id == current_user.id).first() - - if not user: - db_session.close() - flash("Benutzer nicht gefunden", "error") - return redirect(url_for('user.profile')) - - # Aktualisierbare Felder aus dem Formular - user.name = request.form.get('name', user.name) - user.email = request.form.get('email', user.email) - user.department = request.form.get('department', user.department) - user.position = request.form.get('position', user.position) - user.phone = request.form.get('phone', user.phone) - user.bio = request.form.get('bio', user.bio) - - user.updated_at = datetime.now() - db_session.commit() - - user_logger.info(f"Profil aktualisiert für Benutzer {user.username}") - flash("Profil erfolgreich aktualisiert", "success") - - db_session.close() - return redirect(url_for('user.profile')) - - except Exception as e: - user_logger.error(f"Fehler beim Aktualisieren des Profils: {str(e)}") - flash("Fehler beim Aktualisieren des Profils", "error") - return redirect(url_for('user.profile')) - -@user_blueprint.route("/api/update-settings", methods=["POST"]) -@login_required -def api_update_settings(): - """API-Endpunkt für Einstellungen-Updates""" - try: - data = request.get_json() - - db_session = get_db_session() - user = db_session.query(User).filter(User.id == current_user.id).first() - - if not user: - db_session.close() - return jsonify({"error": "Benutzer nicht gefunden"}), 404 - - # Einstellungen JSON aktualisieren - current_settings = user.settings or {} - if isinstance(current_settings, str): - try: - current_settings = json.loads(current_settings) - except json.JSONDecodeError: - current_settings = {} - - # Neue Einstellungen hinzufügen/aktualisieren - for key, value in data.items(): - current_settings[key] = value - - user.settings = json.dumps(current_settings) - user.updated_at = datetime.now() - db_session.commit() - - user_logger.info(f"Einstellungen aktualisiert für Benutzer {user.username}") - - db_session.close() - return jsonify({ - "success": True, - "message": "Einstellungen erfolgreich aktualisiert" - }) - - except Exception as e: - user_logger.error(f"Fehler beim Aktualisieren der Einstellungen: {str(e)}") - return jsonify({"error": "Fehler beim Aktualisieren der Einstellungen"}), 500 - -@user_blueprint.route("/update-settings", methods=["POST"]) -@login_required -def update_settings(): - """Benutzereinstellungen aktualisieren (Form-basiert)""" - try: - db_session = get_db_session() - user = db_session.query(User).filter(User.id == current_user.id).first() - - if not user: - db_session.close() - flash("Benutzer nicht gefunden", "error") - return redirect(url_for('user.settings')) - - # Einstellungen aus dem Formular sammeln - settings = {} - - # Theme-Einstellungen - settings['theme'] = request.form.get('theme', 'light') - settings['language'] = request.form.get('language', 'de') - - # Benachrichtigungseinstellungen - settings['email_notifications'] = request.form.get('email_notifications') == 'on' - settings['push_notifications'] = request.form.get('push_notifications') == 'on' - settings['job_completion_notifications'] = request.form.get('job_completion_notifications') == 'on' - settings['printer_error_notifications'] = request.form.get('printer_error_notifications') == 'on' - - # Dashboard-Einstellungen - settings['default_dashboard_view'] = request.form.get('default_dashboard_view', 'overview') - settings['auto_refresh_interval'] = int(request.form.get('auto_refresh_interval', 30)) - - # Privacy-Einstellungen - settings['show_profile_publicly'] = request.form.get('show_profile_publicly') == 'on' - settings['allow_job_sharing'] = request.form.get('allow_job_sharing') == 'on' - - user.settings = json.dumps(settings) - user.updated_at = datetime.now() - db_session.commit() - - user_logger.info(f"Einstellungen aktualisiert für Benutzer {user.username}") - flash("Einstellungen erfolgreich aktualisiert", "success") - - db_session.close() - return redirect(url_for('user.settings')) - - except Exception as e: - user_logger.error(f"Fehler beim Aktualisieren der Einstellungen: {str(e)}") - flash("Fehler beim Aktualisieren der Einstellungen", "error") - return redirect(url_for('user.settings')) - -@user_blueprint.route("/change-password", methods=["POST"]) -@login_required -def change_password(): - """Passwort ändern""" - try: - # Daten aus Form oder JSON extrahieren - if request.is_json: - data = request.get_json() - current_password = data.get('current_password') - new_password = data.get('new_password') - confirm_password = data.get('confirm_password') - else: - current_password = request.form.get('current_password') - new_password = request.form.get('new_password') - confirm_password = request.form.get('confirm_password') - - # Validierung - if not all([current_password, new_password, confirm_password]): - error_msg = "Alle Passwort-Felder sind erforderlich" - if request.is_json: - return jsonify({"error": error_msg}), 400 - flash(error_msg, "error") - return redirect(url_for('user.settings')) - - if new_password != confirm_password: - error_msg = "Neue Passwörter stimmen nicht überein" - if request.is_json: - return jsonify({"error": error_msg}), 400 - flash(error_msg, "error") - return redirect(url_for('user.settings')) - - if len(new_password) < 8: - error_msg = "Das neue Passwort muss mindestens 8 Zeichen lang sein" - if request.is_json: - return jsonify({"error": error_msg}), 400 - flash(error_msg, "error") - return redirect(url_for('user.settings')) - - db_session = get_db_session() - user = db_session.query(User).filter(User.id == current_user.id).first() - - if not user: - db_session.close() - error_msg = "Benutzer nicht gefunden" - if request.is_json: - return jsonify({"error": error_msg}), 404 - flash(error_msg, "error") - return redirect(url_for('user.settings')) - - # Aktuelles Passwort überprüfen - if not user.check_password(current_password): - db_session.close() - error_msg = "Aktuelles Passwort ist falsch" - if request.is_json: - return jsonify({"error": error_msg}), 401 - flash(error_msg, "error") - return redirect(url_for('user.settings')) - - # Neues Passwort setzen - user.set_password(new_password) - user.updated_at = datetime.now() - db_session.commit() - - user_logger.info(f"Passwort geändert für Benutzer {user.username}") - - db_session.close() - - success_msg = "Passwort erfolgreich geändert" - if request.is_json: - return jsonify({"success": True, "message": success_msg}) - flash(success_msg, "success") - return redirect(url_for('user.settings')) - - except Exception as e: - user_logger.error(f"Fehler beim Ändern des Passworts: {str(e)}") - error_msg = "Fehler beim Ändern des Passworts" - if request.is_json: - return jsonify({"error": error_msg}), 500 - flash(error_msg, "error") - return redirect(url_for('user.settings')) - -@user_blueprint.route("/export", methods=["GET"]) -@login_required -def export_data(): - """Benutzerdaten exportieren (DSGVO-Compliance)""" - try: - db_session = get_db_session() - user = db_session.query(User).filter(User.id == current_user.id).first() - - if not user: - db_session.close() - flash("Benutzer nicht gefunden", "error") - return redirect(url_for('user.settings')) - - # Benutzerdaten sammeln - user_data = { - "personal_information": { - "id": user.id, - "username": user.username, - "email": user.email, - "name": user.name, - "department": user.department, - "position": user.position, - "phone": user.phone, - "bio": user.bio, - "role": user.role, - "created_at": user.created_at.isoformat() if user.created_at else None, - "last_login": user.last_login.isoformat() if user.last_login else None, - "updated_at": user.updated_at.isoformat() if user.updated_at else None, - "last_activity": user.last_activity.isoformat() if user.last_activity else None - }, - "settings": json.loads(user.settings) if user.settings else {}, - "jobs": [], - "export_date": datetime.now().isoformat(), - "export_note": "Dies ist ein Export Ihrer persönlichen Daten gemäß DSGVO Art. 20" - } - - # Benutzer-Jobs sammeln (falls verfügbar) - try: - from models import Job - user_jobs = db_session.query(Job).filter(Job.user_id == user.id).all() - for job in user_jobs: - user_data["jobs"].append({ - "id": job.id, - "filename": job.filename, - "status": job.status, - "created_at": job.created_at.isoformat() if job.created_at else None, - "estimated_duration": job.estimated_duration, - "material_used": job.material_used, - "notes": job.notes - }) - except Exception as job_error: - user_logger.warning(f"Fehler beim Sammeln der Job-Daten: {str(job_error)}") - - db_session.close() - - # JSON-Response erstellen - response = make_response(jsonify(user_data)) - response.headers['Content-Disposition'] = f'attachment; filename=user_data_{user.username}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json' - response.headers['Content-Type'] = 'application/json' - - user_logger.info(f"Datenexport erstellt für Benutzer {user.username}") - - return response - - except Exception as e: - user_logger.error(f"Fehler beim Datenexport: {str(e)}") - flash("Fehler beim Erstellen des Datenexports", "error") - return redirect(url_for('user.settings')) - -@user_blueprint.route("/profile", methods=["PUT"]) -@login_required -def update_profile_api(): - """API-Endpunkt für Profil-Updates""" - try: - data = request.get_json() - - db_session = get_db_session() - user = db_session.query(User).filter(User.id == current_user.id).first() - - if not user: - db_session.close() - return jsonify({"error": "Benutzer nicht gefunden"}), 404 - - # Aktualisierbare Felder (ohne sensitive Daten) - updatable_fields = ['name', 'email', 'department', 'position', 'phone', 'bio'] - - for field in updatable_fields: - if field in data: - setattr(user, field, data[field]) - - user.updated_at = datetime.now() - db_session.commit() - - user_logger.info(f"Profil über API aktualisiert für Benutzer {user.username}") - - # Aktuelle Benutzerdaten zurückgeben - user_data = { - "id": user.id, - "username": user.username, - "email": user.email, - "name": user.name, - "department": user.department, - "position": user.position, - "phone": user.phone, - "bio": user.bio, - "role": user.role, - "updated_at": user.updated_at.isoformat() - } - - db_session.close() - return jsonify({ - "success": True, - "message": "Profil erfolgreich aktualisiert", - "user": user_data - }) - - except Exception as e: - user_logger.error(f"Fehler beim API-Profil-Update: {str(e)}") - return jsonify({"error": "Fehler beim Aktualisieren des Profils"}), 500 \ No newline at end of file diff --git a/backend/blueprints/deprecated/users.py b/backend/blueprints/deprecated/users.py deleted file mode 100644 index b74695753..000000000 --- a/backend/blueprints/deprecated/users.py +++ /dev/null @@ -1,168 +0,0 @@ -from flask import Blueprint, render_template, request, jsonify, redirect, url_for, abort -from flask_login import current_user, login_required -from sqlalchemy.exc import SQLAlchemyError -from functools import wraps - -from models import User, UserPermission, get_cached_session -from utils.logging_config import get_logger - -users_blueprint = Blueprint('users', __name__) -logger = get_logger("users") - -def users_admin_required(f): - """Decorator zur Prüfung der Admin-Berechtigung für Users Blueprint.""" - @wraps(f) - @login_required - def users_decorated_function(*args, **kwargs): - if not current_user.is_admin: - abort(403, "Nur Administratoren haben Zugriff auf diese Seite") - return f(*args, **kwargs) - return users_decorated_function - -@users_blueprint.route('/admin/users//permissions', methods=['GET']) -@users_admin_required -def admin_user_permissions(user_id): - """Benutzerberechtigungen anzeigen und bearbeiten.""" - with get_cached_session() as db_session: - user = db_session.query(User).filter_by(id=user_id).first() - if not user: - abort(404, "Benutzer nicht gefunden") - - # Berechtigungen laden oder erstellen, falls nicht vorhanden - permission = db_session.query(UserPermission).filter_by(user_id=user_id).first() - if not permission: - permission = UserPermission(user_id=user_id) - db_session.add(permission) - db_session.commit() - - return render_template('admin_user_permissions.html', user=user, permission=permission) - -@users_blueprint.route('/api/users//permissions', methods=['GET']) -@login_required -def api_get_user_permissions(user_id): - """Benutzerberechtigungen als JSON zurückgeben.""" - # Nur Admins oder der Benutzer selbst darf seine Berechtigungen sehen - if not current_user.is_admin and current_user.id != user_id: - return jsonify({"error": "Keine Berechtigung"}), 403 - - try: - with get_cached_session() as db_session: - permission = db_session.query(UserPermission).filter_by(user_id=user_id).first() - - if not permission: - # Falls keine Berechtigungen existieren, Standard-Werte zurückgeben - return jsonify({ - "user_id": user_id, - "can_start_jobs": False, - "needs_approval": True, - "can_approve_jobs": False - }) - - return jsonify(permission.to_dict()) - - except Exception as e: - logger.error(f"Fehler beim Abrufen der Benutzerberechtigungen: {str(e)}") - return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500 - -@users_blueprint.route('/api/users//permissions', methods=['PUT']) -@users_admin_required -def api_update_user_permissions(user_id): - """Benutzerberechtigungen aktualisieren.""" - try: - data = request.get_json() - if not data: - return jsonify({"error": "Keine Daten erhalten"}), 400 - - with get_cached_session() as db_session: - # Benutzer prüfen - user = db_session.query(User).filter_by(id=user_id).first() - if not user: - return jsonify({"error": "Benutzer nicht gefunden"}), 404 - - # Berechtigungen laden oder erstellen - permission = db_session.query(UserPermission).filter_by(user_id=user_id).first() - if not permission: - permission = UserPermission(user_id=user_id) - db_session.add(permission) - - # Berechtigungen aktualisieren - if 'can_start_jobs' in data: - permission.can_start_jobs = bool(data['can_start_jobs']) - - if 'needs_approval' in data: - permission.needs_approval = bool(data['needs_approval']) - - if 'can_approve_jobs' in data: - permission.can_approve_jobs = bool(data['can_approve_jobs']) - - db_session.commit() - - logger.info(f"Berechtigungen für Benutzer {user_id} aktualisiert durch Admin {current_user.id}") - - return jsonify({ - "success": True, - "permissions": permission.to_dict() - }) - - except SQLAlchemyError as e: - logger.error(f"Datenbankfehler beim Aktualisieren der Benutzerberechtigungen: {str(e)}") - return jsonify({"error": "Datenbankfehler beim Verarbeiten der Anfrage"}), 500 - except Exception as e: - logger.error(f"Fehler beim Aktualisieren der Benutzerberechtigungen: {str(e)}") - return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500 - -@users_blueprint.route('/admin/users//permissions/update', methods=['POST']) -@users_admin_required -def admin_update_user_permissions(user_id): - """Benutzerberechtigungen über Formular aktualisieren.""" - try: - # Formularfelder auslesen - 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' - - with get_cached_session() as db_session: - # Benutzer prüfen - user = db_session.query(User).filter_by(id=user_id).first() - if not user: - abort(404, "Benutzer nicht gefunden") - - # Berechtigungen laden oder erstellen - permission = db_session.query(UserPermission).filter_by(user_id=user_id).first() - if not permission: - permission = UserPermission(user_id=user_id) - db_session.add(permission) - - # Berechtigungen aktualisieren - permission.can_start_jobs = can_start_jobs - permission.needs_approval = needs_approval - permission.can_approve_jobs = can_approve_jobs - - db_session.commit() - - logger.info(f"Berechtigungen für Benutzer {user_id} aktualisiert durch Admin {current_user.id} (Formular)") - - return redirect(url_for('users.admin_user_permissions', user_id=user_id)) - - except Exception as e: - logger.error(f"Fehler beim Aktualisieren der Benutzerberechtigungen: {str(e)}") - abort(500, "Fehler beim Verarbeiten der Anfrage") - -# Erweiterung des bestehenden Benutzer-Bearbeitungsformulars -@users_blueprint.route('/admin/users//edit/permissions', methods=['GET']) -@users_admin_required -def admin_edit_user_permissions_section(user_id): - """Rendert nur den Berechtigungsteil für das Benutzer-Edit-Formular.""" - with get_cached_session() as db_session: - user = db_session.query(User).filter_by(id=user_id).first() - if not user: - abort(404, "Benutzer nicht gefunden") - - # Berechtigungen laden oder erstellen, falls nicht vorhanden - permission = db_session.query(UserPermission).filter_by(user_id=user_id).first() - if not permission: - permission = UserPermission(user_id=user_id) - db_session.add(permission) - db_session.commit() - - return render_template('_user_permissions_form.html', user=user, permission=permission) \ No newline at end of file diff --git a/backend/logs/app/app.log b/backend/logs/app/app.log index 894939672..d7e02fe9a 100644 --- a/backend/logs/app/app.log +++ b/backend/logs/app/app.log @@ -5154,3 +5154,9 @@ WHERE users.id = ? 2025-06-11 10:52:44 - [app] app - [DEBUG] DEBUG - Response: 200 2025-06-11 10:52:45 - [app] app - [INFO] INFO - ✅ Admin API: System-Health abgerufen 2025-06-11 10:52:45 - [app] app - [DEBUG] DEBUG - Response: 200 +2025-06-11 12:43:22 - [app] app - [WARNING] WARNING - DatabaseCleanupManager nicht verfügbar - Fallback auf Legacy-Cleanup +2025-06-11 12:43:22 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\instance\printer_manager.db +2025-06-11 12:43:29 - [app] app - [WARNING] WARNING - DatabaseCleanupManager nicht verfügbar - Fallback auf Legacy-Cleanup +2025-06-11 12:43:29 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\instance\printer_manager.db +2025-06-11 12:43:36 - [app] app - [WARNING] WARNING - DatabaseCleanupManager nicht verfügbar - Fallback auf Legacy-Cleanup +2025-06-11 12:43:36 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\instance\printer_manager.db diff --git a/backend/logs/development_utilities/development_utilities.log b/backend/logs/development_utilities/development_utilities.log new file mode 100644 index 000000000..e69de29bb diff --git a/backend/logs/printer_utilities/printer_utilities.log b/backend/logs/printer_utilities/printer_utilities.log new file mode 100644 index 000000000..e69de29bb diff --git a/backend/logs/scheduler/scheduler.log b/backend/logs/scheduler/scheduler.log index 3e5cd3d44..ca0d28a87 100644 --- a/backend/logs/scheduler/scheduler.log +++ b/backend/logs/scheduler/scheduler.log @@ -195,3 +195,4 @@ 2025-06-11 10:51:39 - [scheduler] scheduler - [INFO] INFO - ✅ Drucker Tapo P110 (192.168.0.106): Status auf 'online' gesetzt (Steckdose ausgeschaltet - bereit) 2025-06-11 10:51:39 - [scheduler] scheduler - [INFO] INFO - ✅ Status für Drucker Tapo P110 (192.168.0.106) erfolgreich aktualisiert 2025-06-11 10:51:39 - [scheduler] scheduler - [INFO] INFO - 💤 Drucker Tapo P110 (192.168.0.106) nach 7 Min Leerlauf ausgeschaltet +2025-06-11 12:43:22 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True diff --git a/backend/logs/system_utilities/system_utilities.log b/backend/logs/system_utilities/system_utilities.log new file mode 100644 index 000000000..e69de29bb diff --git a/backend/logs/tapo_controller/tapo_controller.log b/backend/logs/tapo_controller/tapo_controller.log index 6dcba04b9..566d96e9d 100644 --- a/backend/logs/tapo_controller/tapo_controller.log +++ b/backend/logs/tapo_controller/tapo_controller.log @@ -528,3 +528,4 @@ 2025-06-11 10:51:50 - [tapo_controller] tapo_controller - [INFO] INFO - 🔍 teste ip 6/6: 192.168.0.105 2025-06-11 10:51:53 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ steckdosen-erkennung abgeschlossen: 5/6 steckdosen gefunden in 18.0s 2025-06-11 10:51:56 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ steckdosen-erkennung abgeschlossen: 5/6 steckdosen gefunden in 18.7s +2025-06-11 12:43:22 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert diff --git a/backend/utils/__pycache__/development_utilities.cpython-313.pyc b/backend/utils/__pycache__/development_utilities.cpython-313.pyc new file mode 100644 index 000000000..00510a3d8 Binary files /dev/null and b/backend/utils/__pycache__/development_utilities.cpython-313.pyc differ diff --git a/backend/utils/__pycache__/printer_utilities.cpython-313.pyc b/backend/utils/__pycache__/printer_utilities.cpython-313.pyc new file mode 100644 index 000000000..0e26bed87 Binary files /dev/null and b/backend/utils/__pycache__/printer_utilities.cpython-313.pyc differ diff --git a/backend/utils/__pycache__/system_utilities.cpython-313.pyc b/backend/utils/__pycache__/system_utilities.cpython-313.pyc new file mode 100644 index 000000000..4ad7bc7d0 Binary files /dev/null and b/backend/utils/__pycache__/system_utilities.cpython-313.pyc differ diff --git a/backend/utils/aktiviere_drucker.py b/backend/utils/aktiviere_drucker.py deleted file mode 100644 index 51eb43fbd..000000000 --- a/backend/utils/aktiviere_drucker.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from models import get_db_session, Printer - -def aktiviere_alle_drucker(): - """Aktiviert alle Drucker in der Datenbank.""" - try: - session = get_db_session() - drucker = session.query(Printer).all() - - if not drucker: - print("Keine Drucker in der Datenbank gefunden.") - session.close() - return - - print(f"Anzahl Drucker: {len(drucker)}") - print("Aktiviere alle Drucker...") - - for d in drucker: - d.active = True - print(f"Drucker {d.id}: {d.name} - IP: {d.plug_ip} - Aktiv: {d.active}") - - session.commit() - print("Alle Drucker wurden erfolgreich aktiviert!") - session.close() - - except Exception as e: - print(f"Fehler: {str(e)}") - try: - session.rollback() - session.close() - except: - pass - -if __name__ == "__main__": - aktiviere_alle_drucker() \ No newline at end of file diff --git a/backend/utils/deprecated/database_cleanup.py b/backend/utils/deprecated/database_cleanup.py deleted file mode 100644 index 5914e83b0..000000000 --- a/backend/utils/deprecated/database_cleanup.py +++ /dev/null @@ -1,336 +0,0 @@ -#!/usr/bin/env python3 -""" -Robuste Datenbank-Cleanup-Utilities -Verhindert "database is locked" Fehler durch intelligente Retry-Logik und Verbindungsmanagement -""" - -import os -import time -import sqlite3 -import threading -from datetime import datetime -from typing import Optional, Tuple, List -from contextlib import contextmanager - -from sqlalchemy import text, create_engine -from sqlalchemy.engine import Engine -from sqlalchemy.pool import StaticPool - -from utils.settings import DATABASE_PATH -from utils.logging_config import get_logger - -logger = get_logger("database_cleanup") - -class DatabaseCleanupManager: - """ - Verwaltet sichere Datenbank-Cleanup-Operationen mit Retry-Logik - Verhindert "database is locked" Fehler durch intelligente Session-Verwaltung - """ - - def __init__(self): - self._cleanup_lock = threading.Lock() - self._cleanup_completed = False - self._active_engines = [] - - def register_engine(self, engine: Engine): - """Registriert eine Engine für das Cleanup""" - with self._cleanup_lock: - if engine not in self._active_engines: - self._active_engines.append(engine) - - def force_close_all_connections(self, max_wait_seconds: int = 10) -> bool: - """ - Schließt alle aktiven Datenbankverbindungen forciert - - Args: - max_wait_seconds: Maximale Wartezeit für graceful shutdown - - Returns: - bool: True wenn erfolgreich - """ - try: - logger.info("🔄 Schließe alle aktiven Datenbankverbindungen...") - - # Alle registrierten Engines disposen - with self._cleanup_lock: - for engine in self._active_engines: - try: - logger.debug(f"Disposing Engine: {engine}") - engine.dispose() - except Exception as e: - logger.warning(f"Fehler beim Engine Dispose: {e}") - self._active_engines.clear() - - # Kurz warten damit alle Verbindungen sich schließen können - time.sleep(1) - - # Prüfe ob noch WAL-Locks bestehen - wal_path = DATABASE_PATH + "-wal" - shm_path = DATABASE_PATH + "-shm" - - start_time = time.time() - while time.time() - start_time < max_wait_seconds: - try: - # Teste kurze Verbindung - test_conn = sqlite3.connect(DATABASE_PATH, timeout=2) - test_conn.execute("BEGIN IMMEDIATE") # Teste exklusiven Zugriff - test_conn.rollback() - test_conn.close() - - logger.info("✅ Alle Datenbankverbindungen erfolgreich geschlossen") - return True - - except sqlite3.OperationalError as e: - if "database is locked" in str(e): - logger.debug(f"Warte auf Verbindungsschließung... ({time.time() - start_time:.1f}s)") - time.sleep(0.5) - continue - else: - raise - - logger.warning(f"⚠️ Timeout beim Warten auf Verbindungsschließung ({max_wait_seconds}s)") - return False - - except Exception as e: - logger.error(f"❌ Fehler beim Schließen der Verbindungen: {e}") - return False - - def safe_wal_checkpoint(self, retry_attempts: int = 5) -> Tuple[bool, Optional[str]]: - """ - Führt sicheren WAL-Checkpoint mit Retry-Logik durch - - Args: - retry_attempts: Anzahl der Wiederholungsversuche - - Returns: - Tuple[bool, Optional[str]]: (Erfolg, Fehlermeldung) - """ - for attempt in range(retry_attempts): - try: - # Kurze, direkte SQLite-Verbindung für Checkpoint - conn = sqlite3.connect(DATABASE_PATH, timeout=10) - - # WAL-Checkpoint mit verschiedenen Strategien - strategies = ["TRUNCATE", "RESTART", "FULL", "PASSIVE"] - - for strategy in strategies: - try: - cursor = conn.execute(f"PRAGMA wal_checkpoint({strategy})") - result = cursor.fetchone() - - if result and result[0] == 0: # Erfolg (0 = success) - pages_transferred = result[1] if len(result) > 1 else 0 - pages_reset = result[2] if len(result) > 2 else 0 - - if pages_transferred > 0: - logger.info(f"✅ WAL-Checkpoint ({strategy}): {pages_transferred} Seiten übertragen, {pages_reset} Seiten zurückgesetzt") - else: - logger.debug(f"WAL-Checkpoint ({strategy}): Keine Seiten zu übertragen") - - conn.close() - return True, None - else: - logger.warning(f"WAL-Checkpoint ({strategy}) unvollständig: {result}") - - except Exception as strategy_error: - logger.warning(f"WAL-Checkpoint ({strategy}) fehlgeschlagen: {strategy_error}") - continue - - conn.close() - - # Wenn alle Strategien fehlschlagen, versuche VACUUM als Fallback - if attempt == 0: # Nur beim ersten Versuch - logger.info("Versuche VACUUM als Fallback...") - conn = sqlite3.connect(DATABASE_PATH, timeout=10) - conn.execute("VACUUM") - conn.close() - logger.info("✅ VACUUM erfolgreich") - return True, None - - except sqlite3.OperationalError as e: - if "database is locked" in str(e): - wait_time = (2 ** attempt) * 0.5 # Exponential backoff - logger.warning(f"Database locked - Versuch {attempt + 1}/{retry_attempts}, warte {wait_time}s...") - time.sleep(wait_time) - continue - else: - return False, f"SQLite-Fehler: {e}" - - except Exception as e: - return False, f"Unerwarteter Fehler: {e}" - - return False, f"Database nach {retry_attempts} Versuchen immer noch gesperrt" - - def safe_journal_mode_switch(self, target_mode: str = "DELETE", retry_attempts: int = 3) -> Tuple[bool, Optional[str]]: - """ - Führt sicheren Journal-Mode-Switch mit Retry-Logik durch - - Args: - target_mode: Ziel-Journal-Mode (DELETE, WAL, etc.) - retry_attempts: Anzahl der Wiederholungsversuche - - Returns: - Tuple[bool, Optional[str]]: (Erfolg, Fehlermeldung) - """ - for attempt in range(retry_attempts): - try: - conn = sqlite3.connect(DATABASE_PATH, timeout=15) - - # Prüfe aktuellen Journal-Mode - current_mode = conn.execute("PRAGMA journal_mode").fetchone()[0] - logger.debug(f"Aktueller Journal-Mode: {current_mode}") - - if current_mode.upper() == target_mode.upper(): - logger.info(f"Journal-Mode bereits auf {target_mode}") - conn.close() - return True, None - - # Mode-Switch durchführen - result = conn.execute(f"PRAGMA journal_mode={target_mode}").fetchone() - new_mode = result[0] if result else None - - conn.close() - - if new_mode and new_mode.upper() == target_mode.upper(): - logger.info(f"✅ Journal-Mode erfolgreich auf {new_mode} umgeschaltet") - return True, None - else: - logger.warning(f"Journal-Mode-Switch unvollständig: {new_mode} != {target_mode}") - - except sqlite3.OperationalError as e: - if "database is locked" in str(e): - wait_time = (2 ** attempt) * 1.0 # Exponential backoff - logger.warning(f"Database locked bei Mode-Switch - Versuch {attempt + 1}/{retry_attempts}, warte {wait_time}s...") - time.sleep(wait_time) - continue - else: - return False, f"SQLite-Fehler: {e}" - - except Exception as e: - return False, f"Unerwarteter Fehler: {e}" - - return False, f"Journal-Mode-Switch nach {retry_attempts} Versuchen fehlgeschlagen" - - def comprehensive_cleanup(self, force_mode_switch: bool = True) -> dict: - """ - Führt umfassendes, sicheres Datenbank-Cleanup durch - - Args: - force_mode_switch: Ob Journal-Mode forciert umgeschaltet werden soll - - Returns: - dict: Cleanup-Ergebnis mit Details - """ - with self._cleanup_lock: - if self._cleanup_completed: - logger.info("Datenbank-Cleanup bereits durchgeführt") - return {"success": True, "message": "Bereits durchgeführt", "operations": []} - - logger.info("🧹 Starte umfassendes Datenbank-Cleanup...") - - operations = [] - errors = [] - - try: - # Schritt 1: Alle Verbindungen schließen - logger.info("📝 Schritt 1: Schließe alle Datenbankverbindungen...") - connection_success = self.force_close_all_connections(max_wait_seconds=15) - - if connection_success: - operations.append("Alle Verbindungen geschlossen") - else: - errors.append("Timeout beim Verbindungsschließen") - - # Schritt 2: WAL-Checkpoint - logger.info("📝 Schritt 2: Führe WAL-Checkpoint durch...") - checkpoint_success, checkpoint_error = self.safe_wal_checkpoint(retry_attempts=5) - - if checkpoint_success: - operations.append("WAL-Checkpoint erfolgreich") - else: - errors.append(f"WAL-Checkpoint fehlgeschlagen: {checkpoint_error}") - - # Schritt 3: Journal-Mode-Switch (nur wenn gewünscht und Checkpoint erfolgreich) - if force_mode_switch and checkpoint_success: - logger.info("📝 Schritt 3: Schalte Journal-Mode um...") - mode_success, mode_error = self.safe_journal_mode_switch("DELETE", retry_attempts=3) - - if mode_success: - operations.append("Journal-Mode auf DELETE umgeschaltet") - else: - errors.append(f"Journal-Mode-Switch fehlgeschlagen: {mode_error}") - logger.warning(f"Journal-Mode-Switch fehlgeschlagen, aber WAL-Checkpoint war erfolgreich") - - # Schritt 4: Finale Optimierungen (nur bei Erfolg) - if checkpoint_success: - logger.info("📝 Schritt 4: Finale Optimierungen...") - try: - conn = sqlite3.connect(DATABASE_PATH, timeout=5) - conn.execute("PRAGMA optimize") - conn.close() - operations.append("Datenbank optimiert") - except Exception as opt_error: - logger.warning(f"Optimierung fehlgeschlagen: {opt_error}") - - # Schritt 5: Prüfe Ergebnis - wal_path = DATABASE_PATH + "-wal" - shm_path = DATABASE_PATH + "-shm" - wal_exists = os.path.exists(wal_path) - shm_exists = os.path.exists(shm_path) - - if not wal_exists and not shm_exists: - operations.append("WAL/SHM-Dateien erfolgreich entfernt") - logger.info("✅ WAL- und SHM-Dateien erfolgreich entfernt") - elif force_mode_switch: - errors.append(f"WAL/SHM-Dateien bestehen noch (WAL: {wal_exists}, SHM: {shm_exists})") - else: - logger.info("WAL/SHM-Dateien bleiben bestehen (kein Mode-Switch angefordert)") - - self._cleanup_completed = True - - # Erfolgsstatus bestimmen - success = len(operations) > 0 and (not force_mode_switch or not wal_exists) - - result = { - "success": success, - "operations": operations, - "errors": errors, - "timestamp": datetime.now().isoformat(), - "wal_files_removed": not wal_exists and not shm_exists - } - - if success: - logger.info(f"✅ Datenbank-Cleanup erfolgreich: {', '.join(operations)}") - else: - logger.error(f"❌ Datenbank-Cleanup mit Fehlern: {', '.join(errors)}") - - return result - - except Exception as e: - error_msg = f"Kritischer Fehler beim Datenbank-Cleanup: {e}" - logger.error(f"❌ {error_msg}") - return { - "success": False, - "operations": operations, - "errors": errors + [error_msg], - "timestamp": datetime.now().isoformat() - } - -# Globale Instanz -cleanup_manager = DatabaseCleanupManager() - -def get_cleanup_manager() -> DatabaseCleanupManager: - """Gibt die globale Cleanup-Manager-Instanz zurück""" - return cleanup_manager - -def safe_database_cleanup(force_mode_switch: bool = True) -> dict: - """ - Convenience-Funktion für sicheres Datenbank-Cleanup - - Args: - force_mode_switch: Ob Journal-Mode forciert umgeschaltet werden soll - - Returns: - dict: Cleanup-Ergebnis - """ - return cleanup_manager.comprehensive_cleanup(force_mode_switch=force_mode_switch) \ No newline at end of file diff --git a/backend/utils/deprecated/db_manager.py b/backend/utils/deprecated/db_manager.py deleted file mode 100644 index 92b78bf6c..000000000 --- a/backend/utils/deprecated/db_manager.py +++ /dev/null @@ -1,133 +0,0 @@ -import os -import logging -from typing import List, Optional, Any -from datetime import datetime - -from sqlalchemy import create_engine, func -from sqlalchemy.orm import sessionmaker, Session, joinedload - -from models import User, Printer, Job, Stats, Base -from utils.settings import DATABASE_PATH, ensure_database_directory - -logger = logging.getLogger(__name__) - -class DatabaseManager: - """Database manager class to handle database operations.""" - - def __init__(self): - """Initialize the database manager.""" - ensure_database_directory() - self.engine = create_engine(f"sqlite:///{DATABASE_PATH}") - self.Session = sessionmaker(bind=self.engine) - - def get_session(self) -> Session: - """Get a new database session. - - Returns: - Session: A new SQLAlchemy session. - """ - return self.Session() - - def test_connection(self) -> bool: - """Test the database connection. - - Returns: - bool: True if the connection is successful, False otherwise. - """ - try: - session = self.get_session() - session.execute("SELECT 1") - session.close() - return True - except Exception as e: - logger.error(f"Database connection test failed: {str(e)}") - return False - - def get_all_jobs(self) -> List[Job]: - """Get all jobs with eager loading of relationships. - - Returns: - List[Job]: A list of all jobs. - """ - session = self.get_session() - try: - jobs = session.query(Job).options( - joinedload(Job.user), - joinedload(Job.printer) - ).all() - return jobs - finally: - session.close() - - def get_jobs_by_status(self, status: str) -> List[Job]: - """Get jobs by status with eager loading of relationships. - - Args: - status: The job status to filter by. - - Returns: - List[Job]: A list of jobs with the specified status. - """ - session = self.get_session() - try: - jobs = session.query(Job).options( - joinedload(Job.user), - joinedload(Job.printer) - ).filter(Job.status == status).all() - return jobs - finally: - session.close() - - def get_job_by_id(self, job_id: int) -> Optional[Job]: - """Get a job by ID with eager loading of relationships. - - Args: - job_id: The job ID to find. - - Returns: - Optional[Job]: The job if found, None otherwise. - """ - session = self.get_session() - try: - job = session.query(Job).options( - joinedload(Job.user), - joinedload(Job.printer) - ).filter(Job.id == job_id).first() - return job - finally: - session.close() - - def get_available_printers(self) -> List[Printer]: - """Get all available printers. - - Returns: - List[Printer]: A list of available printers. - """ - session = self.get_session() - try: - printers = session.query(Printer).filter( - Printer.active == True, - Printer.status != "busy" - ).all() - return printers - finally: - session.close() - - def get_jobs_since(self, since_date: datetime) -> List[Job]: - """Get jobs created since a specific date. - - Args: - since_date: The date to filter jobs from. - - Returns: - List[Job]: A list of jobs created since the specified date. - """ - session = self.get_session() - try: - jobs = session.query(Job).options( - joinedload(Job.user), - joinedload(Job.printer) - ).filter(Job.created_at >= since_date).all() - return jobs - finally: - session.close() \ No newline at end of file diff --git a/backend/utils/development_utilities.py b/backend/utils/development_utilities.py new file mode 100644 index 000000000..a84aa172d --- /dev/null +++ b/backend/utils/development_utilities.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python3.11 +""" +Development Utilities - Konsolidierte Entwicklungs- und Test-Hilfsfunktionen +Zusammenfassung von Datenbank-Tests, Session-Fixes und anderen Entwicklungstools +""" + +import re +import os +import sys +from utils.logging_config import get_logger + +# Logger initialisieren +logger = get_logger("development_utilities") + +# ===== DATENBANK-TESTS ===== + +def test_database_connectivity(): + """ + Testet die grundlegende Datenbank-Konnektivität. + + Returns: + dict: Test-Ergebnisse + """ + try: + from models import get_cached_session, User, Printer, Job + + logger.info("=== DATENBANK-KONNEKTIVITÄTS-TEST ===") + + results = { + 'success': True, + 'tests': {}, + 'errors': [] + } + + with get_cached_session() as session: + # Test User-Query + try: + users = session.query(User).limit(5).all() + results['tests']['users'] = { + 'success': True, + 'count': len(users), + 'sample': users[0].username if users else None + } + logger.info(f"✓ User-Abfrage erfolgreich - {len(users)} Benutzer gefunden") + + if users: + user = users[0] + logger.info(f"✓ Test-User: {user.username} ({user.email})") + logger.info(f"✓ updated_at-Feld: {user.updated_at}") + + except Exception as e: + results['tests']['users'] = {'success': False, 'error': str(e)} + results['errors'].append(f"User-Test: {str(e)}") + logger.error(f"❌ User-Test fehlgeschlagen: {str(e)}") + + # Test Printer-Query + try: + printers = session.query(Printer).limit(5).all() + results['tests']['printers'] = { + 'success': True, + 'count': len(printers), + 'sample': printers[0].name if printers else None + } + logger.info(f"✓ Printer-Abfrage erfolgreich - {len(printers)} Drucker gefunden") + + except Exception as e: + results['tests']['printers'] = {'success': False, 'error': str(e)} + results['errors'].append(f"Printer-Test: {str(e)}") + logger.error(f"❌ Printer-Test fehlgeschlagen: {str(e)}") + + # Test Job-Query + try: + jobs = session.query(Job).limit(5).all() + results['tests']['jobs'] = { + 'success': True, + 'count': len(jobs), + 'sample': jobs[0].title if jobs else None + } + logger.info(f"✓ Job-Abfrage erfolgreich - {len(jobs)} Jobs gefunden") + + except Exception as e: + results['tests']['jobs'] = {'success': False, 'error': str(e)} + results['errors'].append(f"Job-Test: {str(e)}") + logger.error(f"❌ Job-Test fehlgeschlagen: {str(e)}") + + if results['errors']: + results['success'] = False + logger.error("❌ DATENBANK-TESTS TEILWEISE FEHLGESCHLAGEN") + else: + logger.info("🎉 ALLE DATENBANK-TESTS ERFOLGREICH!") + logger.info("Die Anwendung sollte jetzt ohne Fehler starten.") + + return results + + except Exception as e: + logger.error(f"❌ KRITISCHER DATENBANK-TEST-FEHLER: {str(e)}") + return { + 'success': False, + 'tests': {}, + 'errors': [f"Kritischer Fehler: {str(e)}"] + } + +def test_database_schema(): + """ + Testet die Datenbank-Schema-Integrität. + + Returns: + dict: Schema-Test-Ergebnisse + """ + try: + from models import get_cached_session, User, Printer, Job, GuestRequest + from sqlalchemy import inspect + + logger.info("=== DATENBANK-SCHEMA-TEST ===") + + results = { + 'success': True, + 'tables': {}, + 'errors': [] + } + + with get_cached_session() as session: + inspector = inspect(session.bind) + + # Erwartete Tabellen + expected_tables = ['users', 'printers', 'jobs', 'guest_requests', 'system_logs'] + + for table_name in expected_tables: + try: + if inspector.has_table(table_name): + columns = inspector.get_columns(table_name) + results['tables'][table_name] = { + 'exists': True, + 'columns': len(columns), + 'column_names': [col['name'] for col in columns] + } + logger.info(f"✓ Tabelle '{table_name}': {len(columns)} Spalten") + else: + results['tables'][table_name] = {'exists': False} + results['errors'].append(f"Tabelle '{table_name}' nicht gefunden") + logger.error(f"❌ Tabelle '{table_name}' nicht gefunden") + + except Exception as e: + results['tables'][table_name] = {'exists': False, 'error': str(e)} + results['errors'].append(f"Tabelle '{table_name}': {str(e)}") + logger.error(f"❌ Fehler bei Tabelle '{table_name}': {str(e)}") + + if results['errors']: + results['success'] = False + logger.error("❌ SCHEMA-TESTS TEILWEISE FEHLGESCHLAGEN") + else: + logger.info("🎉 ALLE SCHEMA-TESTS ERFOLGREICH!") + + return results + + except Exception as e: + logger.error(f"❌ KRITISCHER SCHEMA-TEST-FEHLER: {str(e)}") + return { + 'success': False, + 'tables': {}, + 'errors': [f"Kritischer Fehler: {str(e)}"] + } + +# ===== SESSION-FIXES ===== + +def fix_session_usage_in_file(file_path): + """ + Behebt Session-Usage in einer Datei durch Konvertierung zu Context Manager Pattern. + + Args: + file_path (str): Pfad zur zu reparierenden Datei + + Returns: + dict: Reparatur-Ergebnisse + """ + try: + if not os.path.exists(file_path): + return { + 'success': False, + 'message': f'Datei nicht gefunden: {file_path}', + 'changes_made': False + } + + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Pattern für direkte Session-Aufrufe + patterns = [ + # session = get_cached_session() -> with get_cached_session() as session: + (r'(\s+)session = get_cached_session\(\)', r'\1with get_cached_session() as session:'), + + # session.close() entfernen (wird automatisch durch Context Manager gemacht) + (r'\s+session\.close\(\)\s*\n', '\n'), + ] + + original_content = content + changes_count = 0 + + for pattern, replacement in patterns: + new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE) + + if new_content != content: + changes_count += 1 + content = new_content + + # Nur schreiben wenn sich etwas geändert hat + if content != original_content: + with open(file_path, 'w', encoding='utf-8') as f: + f.write(content) + + logger.info(f"✅ {file_path} wurde aktualisiert ({changes_count} Änderungen)") + return { + 'success': True, + 'message': f'Datei erfolgreich aktualisiert', + 'changes_made': True, + 'changes_count': changes_count + } + else: + logger.info(f"ℹ️ {file_path} benötigt keine Änderungen") + return { + 'success': True, + 'message': 'Keine Änderungen erforderlich', + 'changes_made': False, + 'changes_count': 0 + } + + except Exception as e: + logger.error(f"❌ Fehler beim Reparieren von {file_path}: {str(e)}") + return { + 'success': False, + 'message': f'Fehler: {str(e)}', + 'changes_made': False + } + +def fix_session_usage_bulk(directory_path=None): + """ + Repariert Session-Usage in mehreren Dateien. + + Args: + directory_path (str): Verzeichnis zum Durchsuchen (Standard: blueprints/) + + Returns: + dict: Bulk-Reparatur-Ergebnisse + """ + if directory_path is None: + backend_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + directory_path = os.path.join(backend_dir, 'blueprints') + + results = { + 'success': True, + 'files_processed': 0, + 'files_changed': 0, + 'total_changes': 0, + 'errors': [] + } + + try: + for root, dirs, files in os.walk(directory_path): + for file in files: + if file.endswith('.py'): + file_path = os.path.join(root, file) + result = fix_session_usage_in_file(file_path) + + results['files_processed'] += 1 + + if result['success']: + if result['changes_made']: + results['files_changed'] += 1 + results['total_changes'] += result.get('changes_count', 0) + else: + results['errors'].append(f"{file_path}: {result['message']}") + results['success'] = False + + logger.info(f"Bulk-Reparatur abgeschlossen: {results['files_processed']} Dateien verarbeitet, {results['files_changed']} geändert") + + except Exception as e: + logger.error(f"❌ Fehler bei Bulk-Reparatur: {str(e)}") + results['success'] = False + results['errors'].append(f"Bulk-Fehler: {str(e)}") + + return results + +# ===== CODE-QUALITÄT ===== + +def analyze_code_quality(file_path): + """ + Analysiert die Code-Qualität einer Python-Datei. + + Args: + file_path (str): Pfad zur zu analysierenden Datei + + Returns: + dict: Code-Qualitäts-Analyse + """ + try: + if not os.path.exists(file_path): + return { + 'success': False, + 'message': f'Datei nicht gefunden: {file_path}' + } + + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + lines = content.split('\n') + + analysis = { + 'success': True, + 'file_path': file_path, + 'metrics': { + 'total_lines': len(lines), + 'code_lines': len([line for line in lines if line.strip() and not line.strip().startswith('#')]), + 'comment_lines': len([line for line in lines if line.strip().startswith('#')]), + 'empty_lines': len([line for line in lines if not line.strip()]), + 'functions': len(re.findall(r'^\s*def\s+\w+', content, re.MULTILINE)), + 'classes': len(re.findall(r'^\s*class\s+\w+', content, re.MULTILINE)), + 'imports': len(re.findall(r'^\s*(import|from)\s+', content, re.MULTILINE)) + }, + 'issues': [] + } + + # Potentielle Probleme identifizieren + if analysis['metrics']['total_lines'] > 1000: + analysis['issues'].append('Datei sehr groß (>1000 Zeilen) - Aufteilung empfohlen') + + if analysis['metrics']['functions'] > 50: + analysis['issues'].append('Viele Funktionen (>50) - Modularisierung empfohlen') + + # TODO-Kommentare finden + todos = re.findall(r'#.*TODO.*', content, re.IGNORECASE) + if todos: + analysis['metrics']['todos'] = len(todos) + analysis['issues'].append(f'{len(todos)} TODO-Kommentare gefunden') + + return analysis + + except Exception as e: + logger.error(f"❌ Fehler bei Code-Qualitäts-Analyse von {file_path}: {str(e)}") + return { + 'success': False, + 'message': f'Fehler: {str(e)}' + } + +# ===== CLI INTERFACE ===== + +if __name__ == "__main__": + if len(sys.argv) > 1: + command = sys.argv[1] + + if command == "test-db": + result = test_database_connectivity() + if result['success']: + print("🎉 Datenbank-Tests erfolgreich!") + else: + print("❌ Datenbank-Tests fehlgeschlagen:") + for error in result['errors']: + print(f" - {error}") + + elif command == "test-schema": + result = test_database_schema() + if result['success']: + print("🎉 Schema-Tests erfolgreich!") + else: + print("❌ Schema-Tests fehlgeschlagen:") + for error in result['errors']: + print(f" - {error}") + + elif command == "fix-sessions": + if len(sys.argv) > 2: + file_path = sys.argv[2] + result = fix_session_usage_in_file(file_path) + print(f"{'✅' if result['success'] else '❌'} {result['message']}") + else: + result = fix_session_usage_bulk() + print(f"Bulk-Reparatur: {result['files_changed']}/{result['files_processed']} Dateien geändert") + + elif command == "analyze": + if len(sys.argv) > 2: + file_path = sys.argv[2] + result = analyze_code_quality(file_path) + if result['success']: + metrics = result['metrics'] + print(f"=== Code-Analyse: {os.path.basename(file_path)} ===") + print(f"Zeilen gesamt: {metrics['total_lines']}") + print(f"Code-Zeilen: {metrics['code_lines']}") + print(f"Funktionen: {metrics['functions']}") + print(f"Klassen: {metrics['classes']}") + if result['issues']: + print("Probleme:") + for issue in result['issues']: + print(f" ⚠️ {issue}") + else: + print(f"❌ {result['message']}") + else: + print("Verwendung: python3.11 development_utilities.py analyze ") + + else: + print("Verfügbare Kommandos:") + print(" test-db - Testet Datenbank-Konnektivität") + print(" test-schema - Testet Datenbank-Schema") + print(" fix-sessions [datei] - Repariert Session-Usage") + print(" analyze - Analysiert Code-Qualität") + else: + print("Verwendung: python3.11 development_utilities.py ") + print("Verfügbare Kommandos: test-db, test-schema, fix-sessions, analyze") \ No newline at end of file diff --git a/backend/utils/fix_csrf.py b/backend/utils/fix_csrf.py deleted file mode 100644 index a0792a7f9..000000000 --- a/backend/utils/fix_csrf.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python3 -"""Entferne problematischen CSRF-Error-Handler aus app.py""" - -import re - -# Lese die Backup-Datei -with open('app_backup.py', 'r', encoding='utf-8') as f: - content = f.read() - -# Entferne den CSRF-Error-Handler-Block -# Suche nach @csrf.error_handler bis zum ersten leeren Zeilen-Block -pattern = r'@csrf\.error_handler.*?(?=\n\n|\n# [A-Z])' -content = re.sub(pattern, '', content, flags=re.DOTALL) - -# Entferne auch mögliche doppelte Leerzeilen -content = re.sub(r'\n\n\n+', '\n\n', content) - -# Schreibe die bereinigte Version -with open('app.py', 'w', encoding='utf-8') as f: - f.write(content) - -print("CSRF-Error-Handler erfolgreich entfernt!") \ No newline at end of file diff --git a/backend/utils/fix_indentation.py b/backend/utils/fix_indentation.py deleted file mode 100644 index 775a7e0f3..000000000 --- a/backend/utils/fix_indentation.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3.11 -""" -Skript zur Behebung von Einrückungsproblemen in user_management.py -""" - -def fix_indentation(): - file_path = 'blueprints/user_management.py' - - with open(file_path, 'r') as f: - content = f.read() - - lines = content.split('\n') - fixed_lines = [] - - for line in lines: - # Behebe die falsche Einrückung nach 'with get_cached_session() as session:' - if line.startswith(' ') and not line.strip().startswith('#'): - # 7 Leerzeichen entfernen (von 15 auf 8) - fixed_lines.append(' ' + line[15:]) - else: - fixed_lines.append(line) - - with open(file_path, 'w') as f: - f.write('\n'.join(fixed_lines)) - - print('✅ Einrückung behoben') - -if __name__ == "__main__": - fix_indentation() \ No newline at end of file diff --git a/backend/utils/fix_session_usage.py b/backend/utils/fix_session_usage.py deleted file mode 100644 index 165756c5a..000000000 --- a/backend/utils/fix_session_usage.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3.11 -""" -Skript zur automatischen Behebung von get_cached_session() Aufrufen -Konvertiert direkte Session-Aufrufe zu Context Manager Pattern. - -Autor: MYP Team -Datum: 2025-06-09 -""" - -import re -import os - -def fix_session_usage(file_path): - """Behebt Session-Usage in einer Datei""" - - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Pattern für direkte Session-Aufrufe - patterns = [ - # session = get_cached_session() -> with get_cached_session() as session: - (r'(\s+)session = get_cached_session\(\)', r'\1with get_cached_session() as session:'), - - # session.close() entfernen (wird automatisch durch Context Manager gemacht) - (r'\s+session\.close\(\)\s*\n', '\n'), - - # Einrückung nach with-Statement anpassen - (r'(with get_cached_session\(\) as session:\s*\n)(\s+)([^\s])', - lambda m: m.group(1) + m.group(2) + ' ' + m.group(3)) - ] - - original_content = content - - for pattern, replacement in patterns: - if callable(replacement): - content = re.sub(pattern, replacement, content, flags=re.MULTILINE) - else: - content = re.sub(pattern, replacement, content, flags=re.MULTILINE) - - # Nur schreiben wenn sich etwas geändert hat - if content != original_content: - with open(file_path, 'w', encoding='utf-8') as f: - f.write(content) - print(f"✅ {file_path} wurde aktualisiert") - return True - else: - print(f"ℹ️ {file_path} benötigt keine Änderungen") - return False - -def main(): - """Hauptfunktion""" - backend_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - user_mgmt_file = os.path.join(backend_dir, 'blueprints', 'user_management.py') - - if os.path.exists(user_mgmt_file): - print(f"Bearbeite {user_mgmt_file}...") - fix_session_usage(user_mgmt_file) - else: - print(f"❌ Datei nicht gefunden: {user_mgmt_file}") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/backend/utils/init_db.py b/backend/utils/init_db.py deleted file mode 100644 index 5bbcf9309..000000000 --- a/backend/utils/init_db.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3.11 - -from models import init_database, create_initial_admin - -if __name__ == "__main__": - print("Initialisiere Datenbank...") - init_database() - - print("Erstelle initialen Admin-Benutzer...") - success = create_initial_admin( - email="admin@mercedes-benz.com", - password="744563017196A", - name="System Administrator", - username="admin" - ) - - if success: - print("Admin-Benutzer erfolgreich erstellt.") - print("Login-Daten:") - print(" Benutzername: admin") - print(" Passwort: 744563017196A") - else: - print("Admin-Benutzer konnte nicht erstellt werden (existiert bereits?).") - - print("Datenbank-Initialisierung abgeschlossen.") \ No newline at end of file diff --git a/backend/utils/performance_monitor.py b/backend/utils/performance_monitor.py deleted file mode 100644 index 363f5c7d7..000000000 --- a/backend/utils/performance_monitor.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Performance Monitor für Production Environment -Minimale Implementierung für Mercedes-Benz TBA Marienfelde -""" - -from utils.logging_config import get_logger - -logger = get_logger("performance_monitor") - -def init_performance_monitoring(app): - """ - Initialisiert Performance-Monitoring für die Flask-App - - Args: - app: Flask-App-Instanz - """ - try: - # Basic Performance-Monitoring Setup - logger.info("[PERF] Performance-Monitoring wird initialisiert...") - - # Optional: Hier könnten weitere Performance-Monitoring-Tools integriert werden - # Für Air-Gapped Environment halten wir es minimal - - app.config['PERFORMANCE_MONITORING_ENABLED'] = True - - logger.info("[PERF] ✅ Performance-Monitoring erfolgreich initialisiert") - - except Exception as e: - logger.error(f"[PERF] ❌ Fehler bei Performance-Monitoring-Initialisierung: {str(e)}") - app.config['PERFORMANCE_MONITORING_ENABLED'] = False \ No newline at end of file diff --git a/backend/utils/printer_utilities.py b/backend/utils/printer_utilities.py new file mode 100644 index 000000000..348f5e3c2 --- /dev/null +++ b/backend/utils/printer_utilities.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python3.11 +""" +Printer Utilities - Konsolidierte Drucker-Management-Hilfsfunktionen +Zusammenfassung von Drucker-Aktivierung und Standort-Updates +""" + +from models import get_db_session, Printer +from utils.logging_config import get_logger +from datetime import datetime + +# Logger initialisieren +logger = get_logger("printer_utilities") + +# ===== DRUCKER AKTIVIERUNG ===== + +def aktiviere_alle_drucker(): + """ + Aktiviert alle Drucker in der Datenbank. + + Returns: + dict: Ergebnis der Aktivierung mit Statistiken + """ + try: + session = get_db_session() + drucker = session.query(Printer).all() + + if not drucker: + logger.warning("Keine Drucker in der Datenbank gefunden.") + session.close() + return { + 'success': False, + 'message': 'Keine Drucker gefunden', + 'activated_count': 0 + } + + logger.info(f"Anzahl Drucker: {len(drucker)}") + logger.info("Aktiviere alle Drucker...") + + activated_count = 0 + for d in drucker: + if not d.active: + d.active = True + activated_count += 1 + logger.info(f"Drucker {d.id}: {d.name} - IP: {d.plug_ip} - Aktiviert") + else: + logger.debug(f"Drucker {d.id}: {d.name} - Bereits aktiv") + + session.commit() + session.close() + + logger.info(f"✅ {activated_count} Drucker wurden erfolgreich aktiviert!") + + return { + 'success': True, + 'message': f'{activated_count} Drucker aktiviert', + 'activated_count': activated_count, + 'total_count': len(drucker) + } + + except Exception as e: + logger.error(f"Fehler bei Drucker-Aktivierung: {str(e)}") + try: + session.rollback() + session.close() + except: + pass + + return { + 'success': False, + 'message': f'Fehler: {str(e)}', + 'activated_count': 0 + } + +def deaktiviere_alle_drucker(): + """ + Deaktiviert alle Drucker in der Datenbank. + + Returns: + dict: Ergebnis der Deaktivierung mit Statistiken + """ + try: + session = get_db_session() + drucker = session.query(Printer).all() + + if not drucker: + logger.warning("Keine Drucker in der Datenbank gefunden.") + session.close() + return { + 'success': False, + 'message': 'Keine Drucker gefunden', + 'deactivated_count': 0 + } + + logger.info(f"Anzahl Drucker: {len(drucker)}") + logger.info("Deaktiviere alle Drucker...") + + deactivated_count = 0 + for d in drucker: + if d.active: + d.active = False + deactivated_count += 1 + logger.info(f"Drucker {d.id}: {d.name} - IP: {d.plug_ip} - Deaktiviert") + else: + logger.debug(f"Drucker {d.id}: {d.name} - Bereits inaktiv") + + session.commit() + session.close() + + logger.info(f"✅ {deactivated_count} Drucker wurden erfolgreich deaktiviert!") + + return { + 'success': True, + 'message': f'{deactivated_count} Drucker deaktiviert', + 'deactivated_count': deactivated_count, + 'total_count': len(drucker) + } + + except Exception as e: + logger.error(f"Fehler bei Drucker-Deaktivierung: {str(e)}") + try: + session.rollback() + session.close() + except: + pass + + return { + 'success': False, + 'message': f'Fehler: {str(e)}', + 'deactivated_count': 0 + } + +# ===== STANDORT-MANAGEMENT ===== + +def update_printer_locations(new_location="Werk 040 - Berlin - TBA"): + """ + Aktualisiert alle Drucker-Standorte zu einem neuen Standort. + + Args: + new_location (str): Neuer Standort für alle Drucker + + Returns: + dict: Ergebnis der Standort-Aktualisierung mit Statistiken + """ + try: + session = get_db_session() + + # Alle Drucker abrufen + all_printers = session.query(Printer).all() + logger.info(f"Gefundene Drucker: {len(all_printers)}") + + if not all_printers: + logger.warning("Keine Drucker in der Datenbank gefunden.") + session.close() + return { + 'success': False, + 'message': 'Keine Drucker gefunden', + 'updated_count': 0 + } + + updated_count = 0 + location_changes = [] + + # Alle Drucker durchgehen und Standort aktualisieren + for printer in all_printers: + old_location = printer.location + if old_location != new_location: + printer.location = new_location + location_changes.append({ + 'printer_id': printer.id, + 'printer_name': printer.name, + 'old_location': old_location, + 'new_location': new_location + }) + logger.info(f"✅ {printer.name}: '{old_location}' → '{new_location}'") + updated_count += 1 + else: + logger.debug(f"Drucker {printer.name}: Standort bereits korrekt") + + # Änderungen speichern + session.commit() + session.close() + + logger.info(f"✅ {updated_count} Drucker-Standorte erfolgreich aktualisiert") + logger.info(f"Neuer Standort: {new_location}") + + return { + 'success': True, + 'message': f'{updated_count} Standorte aktualisiert', + 'updated_count': updated_count, + 'total_count': len(all_printers), + 'new_location': new_location, + 'changes': location_changes + } + + except Exception as e: + logger.error(f"❌ Fehler bei der Standort-Aktualisierung: {e}") + try: + session.rollback() + session.close() + except: + pass + + return { + 'success': False, + 'message': f'Fehler: {str(e)}', + 'updated_count': 0 + } + +def get_printer_locations(): + """ + Gibt eine Übersicht aller Drucker-Standorte zurück. + + Returns: + dict: Standort-Statistiken + """ + try: + session = get_db_session() + all_printers = session.query(Printer).all() + session.close() + + if not all_printers: + return { + 'success': False, + 'message': 'Keine Drucker gefunden', + 'locations': {} + } + + # Standorte gruppieren + locations = {} + for printer in all_printers: + location = printer.location or 'Unbekannt' + if location not in locations: + locations[location] = [] + locations[location].append({ + 'id': printer.id, + 'name': printer.name, + 'active': printer.active, + 'plug_ip': printer.plug_ip + }) + + return { + 'success': True, + 'total_printers': len(all_printers), + 'locations': locations, + 'location_count': len(locations) + } + + except Exception as e: + logger.error(f"Fehler beim Abrufen der Standorte: {str(e)}") + return { + 'success': False, + 'message': f'Fehler: {str(e)}', + 'locations': {} + } + +# ===== STATUS UND STATISTIKEN ===== + +def get_printer_status_summary(): + """ + Gibt eine Zusammenfassung des Drucker-Status zurück. + + Returns: + dict: Status-Zusammenfassung + """ + try: + session = get_db_session() + all_printers = session.query(Printer).all() + session.close() + + if not all_printers: + return { + 'success': False, + 'message': 'Keine Drucker gefunden', + 'summary': {} + } + + active_count = sum(1 for p in all_printers if p.active) + inactive_count = len(all_printers) - active_count + + # Standort-Verteilung + location_distribution = {} + for printer in all_printers: + location = printer.location or 'Unbekannt' + location_distribution[location] = location_distribution.get(location, 0) + 1 + + return { + 'success': True, + 'summary': { + 'total_printers': len(all_printers), + 'active_printers': active_count, + 'inactive_printers': inactive_count, + 'locations': location_distribution, + 'last_updated': datetime.now().isoformat() + } + } + + except Exception as e: + logger.error(f"Fehler beim Abrufen der Status-Zusammenfassung: {str(e)}") + return { + 'success': False, + 'message': f'Fehler: {str(e)}', + 'summary': {} + } + +# ===== CLI INTERFACE ===== + +if __name__ == "__main__": + import sys + + if len(sys.argv) > 1: + command = sys.argv[1] + + if command == "activate-all": + result = aktiviere_alle_drucker() + print(f"✅ {result['message']}") + + elif command == "deactivate-all": + result = deaktiviere_alle_drucker() + print(f"✅ {result['message']}") + + elif command == "update-locations": + new_location = sys.argv[2] if len(sys.argv) > 2 else "Werk 040 - Berlin - TBA" + result = update_printer_locations(new_location) + print(f"✅ {result['message']}") + + elif command == "locations": + result = get_printer_locations() + if result['success']: + print("=== Drucker-Standorte ===") + for location, printers in result['locations'].items(): + print(f"\n📍 {location} ({len(printers)} Drucker):") + for printer in printers: + status = "🟢" if printer['active'] else "🔴" + print(f" {status} {printer['name']} (ID: {printer['id']}, IP: {printer['plug_ip']})") + else: + print(f"❌ {result['message']}") + + elif command == "status": + result = get_printer_status_summary() + if result['success']: + summary = result['summary'] + print("=== Drucker-Status ===") + print(f"Gesamt: {summary['total_printers']}") + print(f"Aktiv: {summary['active_printers']} 🟢") + print(f"Inaktiv: {summary['inactive_printers']} 🔴") + print(f"Standorte: {len(summary['locations'])}") + print(f"Letzte Aktualisierung: {summary['last_updated']}") + else: + print(f"❌ {result['message']}") + + else: + print("Verfügbare Kommandos:") + print(" activate-all - Aktiviert alle Drucker") + print(" deactivate-all - Deaktiviert alle Drucker") + print(" update-locations [STANDORT] - Aktualisiert alle Standorte") + print(" locations - Zeigt Standort-Übersicht") + print(" status - Zeigt Status-Zusammenfassung") + else: + print("Verwendung: python3.11 printer_utilities.py ") + print("Verfügbare Kommandos: activate-all, deactivate-all, update-locations, locations, status") \ No newline at end of file diff --git a/backend/utils/scheduler.py b/backend/utils/scheduler.py deleted file mode 100644 index 6b89b871c..000000000 --- a/backend/utils/scheduler.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -Scheduler utility functions for the admin panel. -""" - -from utils.job_scheduler import scheduler - -def scheduler_is_running(): - """ - Überprüft, ob der Job-Scheduler läuft. - - Returns: - bool: True wenn der Scheduler aktiv ist, sonst False - """ - return scheduler.is_running() - -def start_scheduler(): - """ - Startet den Job-Scheduler. - - Returns: - bool: True wenn erfolgreich gestartet, False wenn bereits läuft - """ - return scheduler.start() - -def stop_scheduler(): - """ - Stoppt den Job-Scheduler. - - Returns: - bool: True wenn erfolgreich gestoppt, False wenn nicht läuft - """ - return scheduler.stop() - -def get_scheduler_uptime(): - """ - Gibt die Laufzeit des Schedulers zurück. - - Returns: - str: Formatierte Laufzeit oder None, wenn der Scheduler nicht läuft - """ - return scheduler.get_uptime() - -def get_scheduler_tasks(): - """ - Gibt alle registrierten Tasks im Scheduler zurück. - - Returns: - dict: Dictionary mit Task-IDs als Schlüssel und Task-Konfigurationen als Werte - """ - return scheduler.get_tasks() \ No newline at end of file diff --git a/backend/utils/system_utilities.py b/backend/utils/system_utilities.py new file mode 100644 index 000000000..1d4516bff --- /dev/null +++ b/backend/utils/system_utilities.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3.11 +""" +System Utilities - Konsolidierte System-Hilfsfunktionen +Zusammenfassung von performance_monitor, scheduler und init_db Funktionalitäten +""" + +from utils.logging_config import get_logger +from utils.job_scheduler import scheduler +from models import init_database, create_initial_admin + +# Logger initialisieren +logger = get_logger("system_utilities") + +# ===== PERFORMANCE MONITORING ===== + +def init_performance_monitoring(app): + """ + Initialisiert Performance-Monitoring für die Flask-App + + Args: + app: Flask-App-Instanz + """ + try: + # Basic Performance-Monitoring Setup + logger.info("[PERF] Performance-Monitoring wird initialisiert...") + + # Optional: Hier könnten weitere Performance-Monitoring-Tools integriert werden + # Für Air-Gapped Environment halten wir es minimal + + app.config['PERFORMANCE_MONITORING_ENABLED'] = True + + logger.info("[PERF] ✅ Performance-Monitoring erfolgreich initialisiert") + + except Exception as e: + logger.error(f"[PERF] ❌ Fehler bei Performance-Monitoring-Initialisierung: {str(e)}") + app.config['PERFORMANCE_MONITORING_ENABLED'] = False + +# ===== SCHEDULER UTILITIES ===== + +def scheduler_is_running(): + """ + Überprüft, ob der Job-Scheduler läuft. + + Returns: + bool: True wenn der Scheduler aktiv ist, sonst False + """ + return scheduler.is_running() + +def start_scheduler(): + """ + Startet den Job-Scheduler. + + Returns: + bool: True wenn erfolgreich gestartet, False wenn bereits läuft + """ + return scheduler.start() + +def stop_scheduler(): + """ + Stoppt den Job-Scheduler. + + Returns: + bool: True wenn erfolgreich gestoppt, False wenn nicht läuft + """ + return scheduler.stop() + +def get_scheduler_uptime(): + """ + Gibt die Laufzeit des Schedulers zurück. + + Returns: + str: Formatierte Laufzeit oder None, wenn der Scheduler nicht läuft + """ + return scheduler.get_uptime() + +def get_scheduler_tasks(): + """ + Gibt alle registrierten Tasks im Scheduler zurück. + + Returns: + dict: Dictionary mit Task-IDs als Schlüssel und Task-Konfigurationen als Werte + """ + return scheduler.get_tasks() + +# ===== DATABASE INITIALIZATION ===== + +def initialize_database_with_admin(): + """ + Initialisiert die Datenbank und erstellt einen initialen Admin-Benutzer. + + Returns: + bool: True wenn erfolgreich, False bei Fehlern + """ + try: + logger.info("Initialisiere Datenbank...") + init_database() + + logger.info("Erstelle initialen Admin-Benutzer...") + success = create_initial_admin( + email="admin@mercedes-benz.com", + password="744563017196A", + name="System Administrator", + username="admin" + ) + + if success: + logger.info("Admin-Benutzer erfolgreich erstellt.") + logger.info("Login-Daten: Benutzername: admin, Passwort: 744563017196A") + else: + logger.warning("Admin-Benutzer konnte nicht erstellt werden (existiert bereits?).") + + logger.info("Datenbank-Initialisierung abgeschlossen.") + return True + + except Exception as e: + logger.error(f"Fehler bei Datenbank-Initialisierung: {str(e)}") + return False + +# ===== SYSTEM STATUS ===== + +def get_system_status(): + """ + Gibt den aktuellen System-Status zurück. + + Returns: + dict: System-Status-Informationen + """ + return { + 'scheduler_running': scheduler_is_running(), + 'scheduler_uptime': get_scheduler_uptime(), + 'scheduler_tasks': len(get_scheduler_tasks()) if get_scheduler_tasks() else 0, + 'performance_monitoring': True # Immer aktiviert in dieser Version + } + +# ===== CLI INTERFACE ===== + +if __name__ == "__main__": + import sys + + if len(sys.argv) > 1: + command = sys.argv[1] + + if command == "init-db": + initialize_database_with_admin() + elif command == "status": + status = get_system_status() + print("=== System Status ===") + print(f"Scheduler läuft: {'✅' if status['scheduler_running'] else '❌'}") + print(f"Scheduler Uptime: {status['scheduler_uptime'] or 'N/A'}") + print(f"Scheduler Tasks: {status['scheduler_tasks']}") + print(f"Performance Monitoring: {'✅' if status['performance_monitoring'] else '❌'}") + else: + print("Verfügbare Kommandos:") + print(" init-db - Initialisiert Datenbank mit Admin-Benutzer") + print(" status - Zeigt System-Status an") + else: + print("Verwendung: python3.11 system_utilities.py ") + print("Verfügbare Kommandos: init-db, status") \ No newline at end of file diff --git a/backend/utils/test_database_fix.py b/backend/utils/test_database_fix.py deleted file mode 100644 index 45415d063..000000000 --- a/backend/utils/test_database_fix.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 -""" -Test-Script für die Datenbank-Reparatur -""" - -import sys -import os - -# Pfad zur App hinzufügen -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def test_database_fix(): - """Testet ob die Datenbank-Reparatur erfolgreich war.""" - try: - from models import get_cached_session, User, Printer, Job - - print("=== DATENBANK-TEST NACH REPARATUR ===") - - with get_cached_session() as session: - # Test User-Query (das war das ursprüngliche Problem) - users = session.query(User).limit(5).all() - print(f"✓ User-Abfrage erfolgreich - {len(users)} Benutzer gefunden") - - # Details des ersten Users anzeigen (falls vorhanden) - if users: - user = users[0] - print(f"✓ Test-User: {user.username} ({user.email})") - print(f"✓ updated_at-Feld: {user.updated_at}") - - # Test Printer-Query - printers = session.query(Printer).limit(5).all() - print(f"✓ Printer-Abfrage erfolgreich - {len(printers)} Drucker gefunden") - - # Test Job-Query - jobs = session.query(Job).limit(5).all() - print(f"✓ Job-Abfrage erfolgreich - {len(jobs)} Jobs gefunden") - - print("\n🎉 ALLE DATENBANK-TESTS ERFOLGREICH!") - print("Die Anwendung sollte jetzt ohne Fehler starten.") - return True - - except Exception as e: - print(f"\n❌ DATENBANK-TEST FEHLGESCHLAGEN: {str(e)}") - return False - -if __name__ == "__main__": - test_database_fix() \ No newline at end of file diff --git a/backend/utils/update_printer_locations.py b/backend/utils/update_printer_locations.py deleted file mode 100644 index b764c6ef3..000000000 --- a/backend/utils/update_printer_locations.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3.11 -""" -Skript zur Aktualisierung der Drucker-Standorte in der Datenbank. -Ändert alle Standorte von "Labor" zu "Werk 040 - Berlin - TBA". -""" - -import sys -import os -sys.path.append('.') - -from database.db_manager import DatabaseManager -from models import Printer -from datetime import datetime - -def update_printer_locations(): - """Aktualisiert alle Drucker-Standorte zu 'Werk 040 - Berlin - TBA'.""" - - print("=== Drucker-Standorte aktualisieren ===") - - try: - db = DatabaseManager() - session = db.get_session() - - # Alle Drucker abrufen - all_printers = session.query(Printer).all() - print(f"Gefundene Drucker: {len(all_printers)}") - - if not all_printers: - print("Keine Drucker in der Datenbank gefunden.") - session.close() - return - - # Neue Standort-Bezeichnung - new_location = "Werk 040 - Berlin - TBA" - updated_count = 0 - - # Alle Drucker durchgehen und Standort aktualisieren - for printer in all_printers: - old_location = printer.location - printer.location = new_location - - print(f"✅ {printer.name}: '{old_location}' → '{new_location}'") - updated_count += 1 - - # Änderungen speichern - session.commit() - session.close() - - print(f"\n✅ {updated_count} Drucker-Standorte erfolgreich aktualisiert") - print(f"Neuer Standort: {new_location}") - print("Standort-Aktualisierung abgeschlossen!") - - except Exception as e: - print(f"❌ Fehler bei der Standort-Aktualisierung: {e}") - if 'session' in locals(): - session.rollback() - session.close() - -if __name__ == "__main__": - update_printer_locations() \ No newline at end of file