🎉 Improved backend structure & added utility modules 🎨📚
This commit is contained in:
Binary file not shown.
+188
-6
@@ -1724,14 +1724,10 @@ def api_admin_system_health():
|
||||
"error": str(e)
|
||||
}), 500
|
||||
|
||||
# ===== INTEGRATION IN BESTEHENDE ROUTEN =====
|
||||
|
||||
# Erweitere bestehende Job-Routen um Dashboard-Events
|
||||
|
||||
@app.route("/api/admin/system-health", methods=['GET'])
|
||||
@app.route("/api/admin/system-health-dashboard", methods=['GET'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def api_admin_system_health():
|
||||
def api_admin_system_health_dashboard():
|
||||
"""API-Endpunkt für System-Gesundheitscheck mit Dashboard-Integration."""
|
||||
try:
|
||||
# Basis-System-Gesundheitscheck durchführen
|
||||
@@ -5064,6 +5060,192 @@ def setup_database_with_migrations():
|
||||
app_logger.error(f"❌ Fehler bei Datenbank-Setup: {str(e)}")
|
||||
raise e
|
||||
|
||||
# ===== PRIVACY UND TERMS ROUTEN =====
|
||||
|
||||
@app.route("/privacy")
|
||||
def privacy_policy():
|
||||
"""Datenschutzerklärung anzeigen"""
|
||||
try:
|
||||
return render_template("privacy_policy.html", title="Datenschutzerklärung")
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden der Datenschutzerklärung: {str(e)}")
|
||||
flash("Fehler beim Laden der Datenschutzerklärung", "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
@app.route("/terms")
|
||||
def terms_of_service():
|
||||
"""Nutzungsbedingungen anzeigen"""
|
||||
try:
|
||||
return render_template("terms_of_service.html", title="Nutzungsbedingungen")
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden der Nutzungsbedingungen: {str(e)}")
|
||||
flash("Fehler beim Laden der Nutzungsbedingungen", "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
@app.route("/legal")
|
||||
def legal_notice():
|
||||
"""Impressum anzeigen"""
|
||||
try:
|
||||
return render_template("legal_notice.html", title="Impressum")
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler beim Laden des Impressums: {str(e)}")
|
||||
flash("Fehler beim Laden des Impressums", "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
@app.route("/api/privacy/accept", methods=["POST"])
|
||||
@login_required
|
||||
def accept_privacy_policy():
|
||||
"""API-Endpunkt für Akzeptierung der Datenschutzerklärung"""
|
||||
db_session = get_db_session()
|
||||
try:
|
||||
data = request.get_json() or {}
|
||||
version = data.get("version", "1.0")
|
||||
|
||||
# Benutzer aus der Datenbank laden
|
||||
user = db_session.query(User).filter(User.id == int(current_user.id)).first()
|
||||
|
||||
if not user:
|
||||
return jsonify({"error": "Benutzer nicht gefunden"}), 404
|
||||
|
||||
# Privacy-Akzeptierung in Benutzer-Einstellungen speichern
|
||||
if hasattr(user, 'settings'):
|
||||
import json
|
||||
settings = json.loads(user.settings) if user.settings else {}
|
||||
else:
|
||||
settings = session.get('user_settings', {})
|
||||
|
||||
# Privacy-Akzeptierung hinzufügen
|
||||
if 'privacy_acceptance' not in settings:
|
||||
settings['privacy_acceptance'] = {}
|
||||
|
||||
settings['privacy_acceptance'] = {
|
||||
'accepted': True,
|
||||
'version': version,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'ip_address': request.remote_addr
|
||||
}
|
||||
|
||||
# Einstellungen speichern
|
||||
if hasattr(user, 'settings'):
|
||||
user.settings = json.dumps(settings)
|
||||
user.updated_at = datetime.now()
|
||||
db_session.commit()
|
||||
else:
|
||||
session['user_settings'] = settings
|
||||
|
||||
user_logger.info(f"Benutzer {current_user.username} hat Datenschutzerklärung v{version} akzeptiert")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Datenschutzerklärung erfolgreich akzeptiert",
|
||||
"version": version,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
db_session.rollback()
|
||||
app_logger.error(f"Fehler bei Privacy-Akzeptierung: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler"}), 500
|
||||
finally:
|
||||
db_session.close()
|
||||
|
||||
@app.route("/api/terms/accept", methods=["POST"])
|
||||
@login_required
|
||||
def accept_terms_of_service():
|
||||
"""API-Endpunkt für Akzeptierung der Nutzungsbedingungen"""
|
||||
db_session = get_db_session()
|
||||
try:
|
||||
data = request.get_json() or {}
|
||||
version = data.get("version", "1.0")
|
||||
|
||||
# Benutzer aus der Datenbank laden
|
||||
user = db_session.query(User).filter(User.id == int(current_user.id)).first()
|
||||
|
||||
if not user:
|
||||
return jsonify({"error": "Benutzer nicht gefunden"}), 404
|
||||
|
||||
# Terms-Akzeptierung in Benutzer-Einstellungen speichern
|
||||
if hasattr(user, 'settings'):
|
||||
import json
|
||||
settings = json.loads(user.settings) if user.settings else {}
|
||||
else:
|
||||
settings = session.get('user_settings', {})
|
||||
|
||||
# Terms-Akzeptierung hinzufügen
|
||||
if 'terms_acceptance' not in settings:
|
||||
settings['terms_acceptance'] = {}
|
||||
|
||||
settings['terms_acceptance'] = {
|
||||
'accepted': True,
|
||||
'version': version,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'ip_address': request.remote_addr
|
||||
}
|
||||
|
||||
# Einstellungen speichern
|
||||
if hasattr(user, 'settings'):
|
||||
user.settings = json.dumps(settings)
|
||||
user.updated_at = datetime.now()
|
||||
db_session.commit()
|
||||
else:
|
||||
session['user_settings'] = settings
|
||||
|
||||
user_logger.info(f"Benutzer {current_user.username} hat Nutzungsbedingungen v{version} akzeptiert")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Nutzungsbedingungen erfolgreich akzeptiert",
|
||||
"version": version,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
db_session.rollback()
|
||||
app_logger.error(f"Fehler bei Terms-Akzeptierung: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler"}), 500
|
||||
finally:
|
||||
db_session.close()
|
||||
|
||||
@app.route("/api/legal/status", methods=["GET"])
|
||||
@login_required
|
||||
def get_legal_status():
|
||||
"""API-Endpunkt für Abfrage des rechtlichen Status (Privacy/Terms Akzeptierung)"""
|
||||
try:
|
||||
# Benutzer-Einstellungen laden
|
||||
if hasattr(current_user, 'settings') and current_user.settings:
|
||||
import json
|
||||
settings = json.loads(current_user.settings)
|
||||
else:
|
||||
settings = session.get('user_settings', {})
|
||||
|
||||
privacy_acceptance = settings.get('privacy_acceptance', {})
|
||||
terms_acceptance = settings.get('terms_acceptance', {})
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"legal_status": {
|
||||
"privacy_policy": {
|
||||
"accepted": privacy_acceptance.get('accepted', False),
|
||||
"version": privacy_acceptance.get('version'),
|
||||
"timestamp": privacy_acceptance.get('timestamp')
|
||||
},
|
||||
"terms_of_service": {
|
||||
"accepted": terms_acceptance.get('accepted', False),
|
||||
"version": terms_acceptance.get('version'),
|
||||
"timestamp": terms_acceptance.get('timestamp')
|
||||
},
|
||||
"compliance_required": not (
|
||||
privacy_acceptance.get('accepted', False) and
|
||||
terms_acceptance.get('accepted', False)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"Fehler bei Legal-Status-Abfrage: {str(e)}")
|
||||
return jsonify({"error": "Interner Serverfehler"}), 500
|
||||
|
||||
|
||||
# ===== STARTUP UND MAIN =====
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -73,6 +73,7 @@ SESSION_LIFETIME = timedelta(hours=2) # Reduziert von 7 Tagen auf 2 Stunden fü
|
||||
UPLOAD_FOLDER = os.path.join(BASE_DIR, "uploads")
|
||||
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'gcode', '3mf', 'stl'}
|
||||
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB Maximum-Dateigröße
|
||||
MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB Maximum-Dateigröße für Drag & Drop System
|
||||
|
||||
# Umgebungskonfiguration
|
||||
ENVIRONMENT = get_env_variable("MYP_ENVIRONMENT", "development")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -75620,3 +75620,140 @@ WHERE users.id = ?
|
||||
2025-05-31 23:51:08 - myp.app - INFO - 📁 Schalte Journal-Mode um...
|
||||
2025-05-31 23:51:08 - myp.app - INFO - ✅ Datenbank-Cleanup abgeschlossen - WAL-Dateien sollten verschwunden sein
|
||||
2025-05-31 23:51:08 - myp.app - INFO - ✅ Shutdown abgeschlossen
|
||||
2025-06-01 00:15:51 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:15:51 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:15:51 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:15:51 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:15:51 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:15:51 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:15:51 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:16:49 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:16:49 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:16:49 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:16:49 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:16:49 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:16:49 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:16:49 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:17:41 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:17:41 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:17:41 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:17:41 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:17:42 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:17:42 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:17:42 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:17:47 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:17:47 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:17:47 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:17:47 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:17:47 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:17:48 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:17:48 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:17:48 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:17:48 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:18:02 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:18:02 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:18:02 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:18:02 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:18:02 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:18:02 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:18:02 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:18:02 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:18:02 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:18:03 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:18:03 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:18:03 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:18:03 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:18:03 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:18:04 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:18:04 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:18:04 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:18:04 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:18:52 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:18:52 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:18:52 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:18:52 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:18:52 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:18:52 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:18:52 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:18:52 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:18:52 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:18:53 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:19:31 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:19:31 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:19:31 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:19:31 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:19:31 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:19:31 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:19:31 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:19:31 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:19:31 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:19:32 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:19:32 - myp.app - INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
|
||||
2025-06-01 00:19:52 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:19:52 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:19:52 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:19:52 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:19:52 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:19:52 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:19:52 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:19:52 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:19:52 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:19:53 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:19:53 - myp.email_notification - INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-01 00:19:53 - myp.maintenance - INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:20:28 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:20:28 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:20:28 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:20:28 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:20:28 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:20:28 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:20:28 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:20:28 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:20:28 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:20:29 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:20:29 - myp.app - INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
|
||||
2025-06-01 00:20:29 - myp.email_notification - INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-01 00:20:29 - myp.maintenance - INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:20:49 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:20:49 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:20:49 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:20:49 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:20:49 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:20:49 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:20:49 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:20:49 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:20:49 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:20:50 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:20:50 - myp.email_notification - INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-01 00:20:50 - myp.maintenance - INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:20:50 - myp.app - INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
|
||||
2025-06-01 00:20:50 - myp.multi_location - INFO - Standard-Standort erstellt
|
||||
2025-06-01 00:21:36 - myp.windows_fixes - INFO - 🔧 Wende Windows-spezifische Fixes an...
|
||||
2025-06-01 00:21:36 - myp.windows_fixes - INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
|
||||
2025-06-01 00:21:36 - myp.windows_fixes - INFO - ✅ Globaler subprocess-Patch angewendet
|
||||
2025-06-01 00:21:36 - myp.windows_fixes - INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
|
||||
2025-06-01 00:21:36 - myp.app - INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
|
||||
2025-06-01 00:21:36 - myp.printer_monitor - INFO - 🖨️ Drucker-Monitor initialisiert
|
||||
2025-06-01 00:21:36 - myp.printer_monitor - INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
|
||||
2025-06-01 00:21:36 - myp.database - INFO - Datenbank-Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:21:36 - myp.analytics - INFO - 📈 Analytics Engine initialisiert
|
||||
2025-06-01 00:21:37 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:21:37 - myp.app - INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
|
||||
2025-06-01 00:21:37 - myp.email_notification - INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
|
||||
2025-06-01 00:21:37 - myp.maintenance - INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:21:37 - myp.multi_location - INFO - Standard-Standort erstellt
|
||||
2025-06-01 00:21:37 - myp.dashboard - INFO - Dashboard-Background-Worker gestartet
|
||||
2025-06-01 00:21:37 - myp.maintenance - INFO - Wartungs-Scheduler gestartet
|
||||
2025-06-01 00:21:37 - myp.multi_location - INFO - Standard-Standort erstellt
|
||||
2025-06-01 00:21:37 - myp.dashboard - INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
|
||||
2025-06-01 00:21:37 - myp.dashboard - INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
|
||||
2025-06-01 00:21:37 - myp.security - INFO - 🔒 Security System initialisiert
|
||||
2025-06-01 00:21:37 - myp.permissions - INFO - 🔐 Permission Template Helpers registriert
|
||||
2025-06-01 00:21:37 - myp.app - INFO - ==================================================
|
||||
2025-06-01 00:21:37 - myp.app - INFO - [START] MYP (Manage Your Printers) wird gestartet...
|
||||
2025-06-01 00:21:37 - myp.app - INFO - [FOLDER] Log-Verzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\logs
|
||||
2025-06-01 00:21:37 - myp.app - INFO - [CHART] Log-Level: INFO
|
||||
2025-06-01 00:21:37 - myp.app - INFO - [PC] Betriebssystem: Windows 11
|
||||
2025-06-01 00:21:37 - myp.app - INFO - [WEB] Hostname: C040L0079726760
|
||||
2025-06-01 00:21:37 - myp.app - INFO - [TIME] Startzeit: 01.06.2025 00:21:37
|
||||
2025-06-01 00:21:37 - myp.app - INFO - ==================================================
|
||||
|
||||
@@ -2705,3 +2705,15 @@
|
||||
2025-05-31 23:50:30 - myp.scheduler - INFO - Scheduler gestartet
|
||||
2025-05-31 23:51:08 - myp.scheduler - INFO - Scheduler-Thread beendet
|
||||
2025-05-31 23:51:08 - myp.scheduler - INFO - Scheduler gestoppt
|
||||
2025-06-01 00:15:51 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:16:49 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:17:42 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:17:48 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:18:02 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:18:04 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:18:52 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:19:31 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:19:52 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:20:28 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:20:49 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
2025-06-01 00:21:36 - myp.scheduler - INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||
|
||||
+121
-40
@@ -3,55 +3,136 @@
|
||||
# Automatisch generiert am: 2025-05-29 19:41:49
|
||||
# Installiere mit: pip install -r requirements.txt
|
||||
|
||||
# ===== CORE FLASK FRAMEWORK =====
|
||||
# Direkt in app.py verwendet
|
||||
Flask==3.1.1
|
||||
# ===== CORE FRAMEWORK =====
|
||||
Flask==3.0.0
|
||||
Werkzeug==3.0.1
|
||||
|
||||
# ===== FLASK EXTENSIONS =====
|
||||
Flask-Login==0.6.3
|
||||
Flask-WTF==1.2.1
|
||||
Flask-SocketIO==5.3.6
|
||||
WTForms==3.1.1
|
||||
|
||||
# ===== DATENBANK =====
|
||||
# SQLAlchemy für Datenbankoperationen (models.py, app.py)
|
||||
SQLAlchemy==2.0.36
|
||||
# ===== DATABASE =====
|
||||
SQLAlchemy==2.0.23
|
||||
|
||||
# ===== SICHERHEIT UND AUTHENTIFIZIERUNG =====
|
||||
# Werkzeug für Passwort-Hashing und Utilities (app.py)
|
||||
bcrypt==4.2.1
|
||||
cryptography==44.0.0
|
||||
Werkzeug==3.1.3
|
||||
# ===== SECURITY =====
|
||||
cryptography==41.0.8
|
||||
bcrypt==4.1.2
|
||||
|
||||
# ===== SMART PLUG STEUERUNG =====
|
||||
# PyP100 für TP-Link Tapo Smart Plugs (utils/job_scheduler.py)
|
||||
PyP100
|
||||
# ===== HTTP REQUESTS (für Online-Modus) =====
|
||||
requests==2.31.0
|
||||
urllib3==2.1.0
|
||||
|
||||
# ===== RATE LIMITING UND CACHING =====
|
||||
# Redis für Rate Limiting (utils/rate_limiter.py) - optional
|
||||
redis==5.2.1
|
||||
# ===== HARDWARE INTEGRATION =====
|
||||
# TP-Link Tapo Smart Plugs
|
||||
PyP100==0.1.4
|
||||
|
||||
# ===== HTTP REQUESTS =====
|
||||
# Requests für HTTP-Anfragen (utils/queue_manager.py, utils/debug_drucker_erkennung.py)
|
||||
requests==2.32.3
|
||||
# ===== REAL-TIME FEATURES =====
|
||||
# WebSocket-Support mit Fallback-Optionen
|
||||
eventlet==0.33.3
|
||||
python-socketio==5.10.0
|
||||
|
||||
# ===== TEMPLATE ENGINE =====
|
||||
# Jinja2 und MarkupSafe (automatisch mit Flask installiert, aber explizit für utils/template_helpers.py)
|
||||
MarkupSafe==3.0.2
|
||||
# ===== SCHEDULING & TASK MANAGEMENT =====
|
||||
schedule==1.2.0
|
||||
APScheduler==3.10.4
|
||||
|
||||
# ===== GIS & LOCATION SERVICES =====
|
||||
geocoder==1.38.1
|
||||
|
||||
# ===== DATA PROCESSING & EXPORT =====
|
||||
# Excel-Export (optional)
|
||||
openpyxl==3.1.2
|
||||
pandas==2.1.4
|
||||
xlsxwriter==3.1.9
|
||||
|
||||
# CSV/JSON processing
|
||||
chardet==5.2.0
|
||||
|
||||
# ===== EMAIL FUNCTIONALITY =====
|
||||
# Email-Features für Benachrichtigungen
|
||||
email-validator==2.1.0.post1
|
||||
|
||||
# ===== IMAGE PROCESSING =====
|
||||
# Avatar und Bild-Upload
|
||||
Pillow==10.1.0
|
||||
|
||||
# ===== DEVELOPMENT & DEBUGGING =====
|
||||
# Nur für Development-Umgebung
|
||||
# python-dotenv==1.0.0
|
||||
|
||||
# ===== DATE/TIME UTILITIES =====
|
||||
python-dateutil==2.8.2
|
||||
pytz==2023.3
|
||||
|
||||
# ===== FILE HANDLING =====
|
||||
# Datei-Upload und -Verarbeitung
|
||||
python-magic==0.4.27
|
||||
python-magic-bin==0.4.14 # Windows binary
|
||||
|
||||
# ===== LOGGING & MONITORING =====
|
||||
# Erweiterte Logging-Features
|
||||
colorlog==6.8.0
|
||||
|
||||
# ===== NETWORK & CONNECTIVITY =====
|
||||
# Netzwerk-Utilities für Drucker-Kommunikation
|
||||
netifaces==0.11.0
|
||||
ping3==4.0.4
|
||||
|
||||
# ===== BACKUP & COMPRESSION =====
|
||||
# Backup-Funktionalität
|
||||
zipfile36==0.1.3
|
||||
|
||||
# ===== CONFIGURATION =====
|
||||
# Konfiguration und Settings
|
||||
configparser==6.0.0
|
||||
|
||||
# ===== VALIDATION =====
|
||||
# Formular-Validierung
|
||||
cerberus==1.3.5
|
||||
marshmallow==3.20.1
|
||||
|
||||
# ===== CACHING (optional) =====
|
||||
# Cache-Funktionalität
|
||||
cachetools==5.3.2
|
||||
|
||||
# ===== UTILITIES =====
|
||||
# Allgemeine Utilities
|
||||
python-slugify==8.0.1
|
||||
click==8.1.7
|
||||
|
||||
# ===== COMPATIBILITY =====
|
||||
# Windows-Kompatibilität
|
||||
pywin32==306; sys_platform == "win32"
|
||||
wmi==1.5.1; sys_platform == "win32"
|
||||
|
||||
# ===== OPTIONAL DEPENDENCIES =====
|
||||
# Für erweiterte Features (automatisch installiert wenn verfügbar)
|
||||
|
||||
# PDF-Generation (für Reports)
|
||||
reportlab==4.0.7
|
||||
weasyprint==60.2
|
||||
|
||||
# Erweiterte Kryptographie
|
||||
cryptography==41.0.8
|
||||
|
||||
# QR-Code Generation (für OTP-Codes)
|
||||
qrcode==7.4.2
|
||||
|
||||
# ===== PRODUCTION DEPLOYMENT =====
|
||||
# WSGI Server für Produktion
|
||||
gunicorn==21.2.0
|
||||
waitress==2.1.2
|
||||
|
||||
# ===== SYSTEM MONITORING =====
|
||||
# psutil für System-Monitoring (utils/debug_utils.py, utils/debug_cli.py)
|
||||
psutil==6.1.1
|
||||
# System-Überwachung
|
||||
psutil==5.9.6
|
||||
|
||||
# ===== ZUSÄTZLICHE CORE ABHÄNGIGKEITEN =====
|
||||
# Click für CLI-Kommandos (automatisch mit Flask)
|
||||
# Keine Core-Requirements
|
||||
# ===== ERROR TRACKING =====
|
||||
# Fehler-Tracking (optional)
|
||||
# sentry-sdk[flask]==1.38.0
|
||||
|
||||
# ===== WINDOWS-SPEZIFISCHE ABHÄNGIGKEITEN =====
|
||||
# Nur für Windows-Systeme erforderlich
|
||||
# Keine Windows-Requirements
|
||||
|
||||
# ===== OPTIONAL: ENTWICKLUNG UND TESTING =====
|
||||
# Nur für Entwicklungsumgebung
|
||||
pytest==8.3.4; extra == "dev"
|
||||
pytest-cov==6.0.0; extra == "dev"
|
||||
|
||||
# ===== OPTIONAL: PRODUKTIONS-SERVER =====
|
||||
# Gunicorn für Produktionsumgebung
|
||||
gunicorn==23.0.0; extra == "prod"
|
||||
# ===== API DOCUMENTATION =====
|
||||
# API-Dokumentation (optional)
|
||||
# flask-restx==1.3.0
|
||||
# flasgger==0.9.7.1
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -933,4 +933,36 @@ def get_advanced_table_css() -> str:
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
"""
|
||||
"""
|
||||
|
||||
def create_table_config(table_id: str, columns: List[ColumnConfig], **kwargs) -> TableConfig:
|
||||
"""
|
||||
Erstellt eine neue Tabellen-Konfiguration.
|
||||
|
||||
Args:
|
||||
table_id: Eindeutige ID für die Tabelle
|
||||
columns: Liste der Spalten-Konfigurationen
|
||||
**kwargs: Zusätzliche Konfigurationsoptionen
|
||||
|
||||
Returns:
|
||||
TableConfig: Konfiguration für die erweiterte Tabelle
|
||||
"""
|
||||
return TableConfig(
|
||||
table_id=table_id,
|
||||
columns=columns,
|
||||
default_sort=kwargs.get('default_sort', []),
|
||||
default_filters=kwargs.get('default_filters', []),
|
||||
pagination=kwargs.get('pagination', PaginationConfig()),
|
||||
searchable=kwargs.get('searchable', True),
|
||||
exportable=kwargs.get('exportable', True),
|
||||
selectable=kwargs.get('selectable', False),
|
||||
row_actions=kwargs.get('row_actions', [])
|
||||
)
|
||||
|
||||
def get_advanced_tables_js() -> str:
|
||||
"""Alias für die bestehende Funktion"""
|
||||
return get_advanced_table_javascript()
|
||||
|
||||
def get_advanced_tables_css() -> str:
|
||||
"""Alias für die bestehende Funktion"""
|
||||
return get_advanced_table_css()
|
||||
@@ -685,4 +685,106 @@ def get_maintenance_javascript() -> str:
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
window.maintenanceManager = new MaintenanceManager();
|
||||
});
|
||||
"""
|
||||
"""
|
||||
|
||||
def create_maintenance_task(printer_id: int, title: str, description: str = "",
|
||||
maintenance_type: MaintenanceType = MaintenanceType.PREVENTIVE,
|
||||
priority: MaintenancePriority = MaintenancePriority.NORMAL) -> int:
|
||||
"""
|
||||
Erstellt eine neue Wartungsaufgabe.
|
||||
|
||||
Args:
|
||||
printer_id: ID des Druckers
|
||||
title: Titel der Wartungsaufgabe
|
||||
description: Beschreibung der Aufgabe
|
||||
maintenance_type: Art der Wartung
|
||||
priority: Priorität der Aufgabe
|
||||
|
||||
Returns:
|
||||
int: ID der erstellten Aufgabe
|
||||
"""
|
||||
task = MaintenanceTask(
|
||||
printer_id=printer_id,
|
||||
title=title,
|
||||
description=description,
|
||||
maintenance_type=maintenance_type,
|
||||
priority=priority,
|
||||
checklist=maintenance_manager.create_maintenance_checklist(maintenance_type)
|
||||
)
|
||||
|
||||
return maintenance_manager.create_task(task)
|
||||
|
||||
def schedule_maintenance(printer_id: int, maintenance_type: MaintenanceType,
|
||||
interval_days: int, description: str = "") -> MaintenanceSchedule:
|
||||
"""
|
||||
Plant regelmäßige Wartungen (Alias für maintenance_manager.schedule_maintenance).
|
||||
|
||||
Args:
|
||||
printer_id: ID des Druckers
|
||||
maintenance_type: Art der Wartung
|
||||
interval_days: Intervall in Tagen
|
||||
description: Beschreibung
|
||||
|
||||
Returns:
|
||||
MaintenanceSchedule: Erstellter Wartungsplan
|
||||
"""
|
||||
return maintenance_manager.schedule_maintenance(
|
||||
printer_id=printer_id,
|
||||
maintenance_type=maintenance_type,
|
||||
interval_days=interval_days,
|
||||
description=description
|
||||
)
|
||||
|
||||
def get_maintenance_overview() -> Dict[str, Any]:
|
||||
"""
|
||||
Holt eine Übersicht aller Wartungsaktivitäten.
|
||||
|
||||
Returns:
|
||||
Dict: Wartungsübersicht mit Statistiken und anstehenden Aufgaben
|
||||
"""
|
||||
upcoming = maintenance_manager.get_upcoming_maintenance()
|
||||
overdue = maintenance_manager.get_overdue_tasks()
|
||||
metrics = maintenance_manager.get_maintenance_metrics()
|
||||
|
||||
# Aktive Tasks
|
||||
active_tasks = [task for task in maintenance_manager.tasks.values()
|
||||
if task.status == MaintenanceStatus.IN_PROGRESS]
|
||||
|
||||
# Completed tasks in last 30 days
|
||||
thirty_days_ago = datetime.now() - timedelta(days=30)
|
||||
recent_completed = [task for task in maintenance_manager.maintenance_history
|
||||
if task.completed_at and task.completed_at >= thirty_days_ago]
|
||||
|
||||
return {
|
||||
'summary': {
|
||||
'total_tasks': len(maintenance_manager.tasks),
|
||||
'active_tasks': len(active_tasks),
|
||||
'upcoming_tasks': len(upcoming),
|
||||
'overdue_tasks': len(overdue),
|
||||
'completed_this_month': len(recent_completed)
|
||||
},
|
||||
'upcoming_tasks': [asdict(task) for task in upcoming[:10]],
|
||||
'overdue_tasks': [asdict(task) for task in overdue],
|
||||
'active_tasks': [asdict(task) for task in active_tasks],
|
||||
'recent_completed': [asdict(task) for task in recent_completed[:5]],
|
||||
'metrics': asdict(metrics),
|
||||
'schedules': {
|
||||
printer_id: [asdict(schedule) for schedule in schedules]
|
||||
for printer_id, schedules in maintenance_manager.schedules.items()
|
||||
}
|
||||
}
|
||||
|
||||
def update_maintenance_status(task_id: int, new_status: MaintenanceStatus,
|
||||
notes: str = "") -> bool:
|
||||
"""
|
||||
Aktualisiert den Status einer Wartungsaufgabe (Alias für maintenance_manager.update_task_status).
|
||||
|
||||
Args:
|
||||
task_id: ID der Wartungsaufgabe
|
||||
new_status: Neuer Status
|
||||
notes: Optionale Notizen
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich aktualisiert
|
||||
"""
|
||||
return maintenance_manager.update_task_status(task_id, new_status, notes)
|
||||
@@ -520,23 +520,138 @@ class MultiLocationManager:
|
||||
}
|
||||
|
||||
# Globale Instanz
|
||||
multi_location_manager = MultiLocationManager()
|
||||
location_manager = MultiLocationManager()
|
||||
|
||||
# Alias für Import-Kompatibilität
|
||||
LocationManager = MultiLocationManager
|
||||
|
||||
def create_location(name: str, code: str, location_type: LocationType = LocationType.BRANCH,
|
||||
address: str = "", city: str = "", country: str = "",
|
||||
parent_id: Optional[int] = None) -> int:
|
||||
"""
|
||||
Erstellt einen neuen Standort (globale Funktion).
|
||||
|
||||
Args:
|
||||
name: Name des Standorts
|
||||
code: Kurzer Code für den Standort
|
||||
location_type: Art des Standorts
|
||||
address: Adresse
|
||||
city: Stadt
|
||||
country: Land
|
||||
parent_id: Parent-Standort ID
|
||||
|
||||
Returns:
|
||||
int: ID des erstellten Standorts
|
||||
"""
|
||||
location = Location(
|
||||
name=name,
|
||||
code=code,
|
||||
location_type=location_type,
|
||||
address=address,
|
||||
city=city,
|
||||
country=country,
|
||||
parent_id=parent_id
|
||||
)
|
||||
|
||||
return location_manager.create_location(location)
|
||||
|
||||
def assign_user_to_location(user_id: int, location_id: int,
|
||||
access_level: AccessLevel = AccessLevel.READ_WRITE,
|
||||
granted_by: int = 1, is_primary: bool = False) -> bool:
|
||||
"""
|
||||
Weist einen Benutzer einem Standort zu.
|
||||
|
||||
Args:
|
||||
user_id: ID des Benutzers
|
||||
location_id: ID des Standorts
|
||||
access_level: Zugriffslevel
|
||||
granted_by: ID des gewährenden Benutzers
|
||||
is_primary: Ob dies der primäre Standort ist
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich
|
||||
"""
|
||||
return location_manager.grant_location_access(
|
||||
user_id=user_id,
|
||||
location_id=location_id,
|
||||
access_level=access_level,
|
||||
granted_by=granted_by,
|
||||
is_primary=is_primary
|
||||
)
|
||||
|
||||
def get_user_locations(user_id: int) -> List[Location]:
|
||||
"""
|
||||
Holt alle Standorte eines Benutzers (globale Funktion).
|
||||
|
||||
Args:
|
||||
user_id: ID des Benutzers
|
||||
|
||||
Returns:
|
||||
List[Location]: Liste der zugänglichen Standorte
|
||||
"""
|
||||
return location_manager.get_user_locations(user_id)
|
||||
|
||||
def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||
"""
|
||||
Berechnet die Entfernung zwischen zwei Koordinaten (Haversine-Formel).
|
||||
|
||||
Args:
|
||||
lat1, lon1: Koordinaten des ersten Punkts
|
||||
lat2, lon2: Koordinaten des zweiten Punkts
|
||||
|
||||
Returns:
|
||||
float: Entfernung in Kilometern
|
||||
"""
|
||||
from math import radians, sin, cos, sqrt, atan2
|
||||
|
||||
R = 6371 # Erdradius in km
|
||||
|
||||
lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
|
||||
dlat = lat2 - lat1
|
||||
dlon = lon2 - lon1
|
||||
|
||||
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
|
||||
c = 2 * atan2(sqrt(a), sqrt(1-a))
|
||||
|
||||
return R * c
|
||||
|
||||
def find_nearest_location(latitude: float, longitude: float,
|
||||
radius_km: float = 50) -> Optional[Location]:
|
||||
"""
|
||||
Findet den nächstgelegenen Standort.
|
||||
|
||||
Args:
|
||||
latitude: Breitengrad
|
||||
longitude: Längengrad
|
||||
radius_km: Suchradius in Kilometern
|
||||
|
||||
Returns:
|
||||
Optional[Location]: Nächstgelegener Standort oder None
|
||||
"""
|
||||
nearest_locations = location_manager.find_nearest_locations(
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
radius_km=radius_km,
|
||||
limit=1
|
||||
)
|
||||
|
||||
return nearest_locations[0][0] if nearest_locations else None
|
||||
|
||||
def get_location_dashboard_data(user_id: int) -> Dict[str, Any]:
|
||||
"""Holt Dashboard-Daten für Standorte eines Benutzers"""
|
||||
user_locations = multi_location_manager.get_user_locations(user_id)
|
||||
primary_location = multi_location_manager.get_user_primary_location(user_id)
|
||||
user_locations = location_manager.get_user_locations(user_id)
|
||||
primary_location = location_manager.get_user_primary_location(user_id)
|
||||
|
||||
dashboard_data = {
|
||||
'user_locations': [asdict(loc) for loc in user_locations],
|
||||
'primary_location': asdict(primary_location) if primary_location else None,
|
||||
'location_count': len(user_locations),
|
||||
'hierarchy': multi_location_manager.get_location_hierarchy()
|
||||
'hierarchy': location_manager.get_location_hierarchy()
|
||||
}
|
||||
|
||||
# Füge Statistiken für jeden Standort hinzu
|
||||
for location in user_locations:
|
||||
location_stats = multi_location_manager.get_location_statistics(location.id)
|
||||
location_stats = location_manager.get_location_statistics(location.id)
|
||||
dashboard_data[f'stats_{location.id}'] = location_stats
|
||||
|
||||
return dashboard_data
|
||||
@@ -553,7 +668,7 @@ def create_location_from_address(name: str, address: str, city: str,
|
||||
country=country
|
||||
)
|
||||
|
||||
return multi_location_manager.create_location(location)
|
||||
return location_manager.create_location(location)
|
||||
|
||||
# JavaScript für Multi-Location Frontend
|
||||
def get_multi_location_javascript() -> str:
|
||||
|
||||
@@ -43,6 +43,7 @@ class Permission(Enum):
|
||||
EXTEND_JOB = "extend_job"
|
||||
CANCEL_JOB = "cancel_job"
|
||||
VIEW_JOB_HISTORY = "view_job_history"
|
||||
APPROVE_JOBS = "approve_jobs" # Berechtigung zum Genehmigen und Verwalten von Jobs
|
||||
|
||||
# Benutzer-Berechtigungen
|
||||
VIEW_USERS = "view_users"
|
||||
@@ -59,6 +60,7 @@ class Permission(Enum):
|
||||
EXPORT_DATA = "export_data"
|
||||
BACKUP_DATABASE = "backup_database"
|
||||
MANAGE_SETTINGS = "manage_settings"
|
||||
ADMIN = "admin" # Allgemeine Admin-Berechtigung für administrative Funktionen
|
||||
|
||||
# Gast-Berechtigungen
|
||||
VIEW_GUEST_REQUESTS = "view_guest_requests"
|
||||
@@ -149,6 +151,7 @@ ROLE_PERMISSIONS[Role.SUPERVISOR] = ROLE_PERMISSIONS[Role.TECHNICIAN] | {
|
||||
Permission.MANAGE_GUEST_REQUESTS,
|
||||
Permission.MANAGE_SHIFTS,
|
||||
Permission.VIEW_USER_DETAILS,
|
||||
Permission.APPROVE_JOBS, # Jobs genehmigen und verwalten
|
||||
}
|
||||
|
||||
# Admin erweitert Supervisor-Permissions
|
||||
@@ -161,6 +164,7 @@ ROLE_PERMISSIONS[Role.ADMIN] = ROLE_PERMISSIONS[Role.SUPERVISOR] | {
|
||||
Permission.EXPORT_DATA,
|
||||
Permission.VIEW_LOGS,
|
||||
Permission.MANAGE_SETTINGS,
|
||||
Permission.ADMIN, # Allgemeine Admin-Berechtigung hinzufügen
|
||||
}
|
||||
|
||||
# Super Admin hat alle Berechtigungen
|
||||
|
||||
@@ -25,12 +25,28 @@ from concurrent.futures import ThreadPoolExecutor
|
||||
# WebSocket-Support
|
||||
try:
|
||||
from flask_socketio import SocketIO, emit, join_room, leave_room, disconnect
|
||||
from eventlet import wsgi, listen
|
||||
import eventlet
|
||||
# Eventlet separat importieren für bessere Fehlerbehandlung
|
||||
try:
|
||||
from eventlet import wsgi, listen
|
||||
import eventlet
|
||||
EVENTLET_AVAILABLE = True
|
||||
except (ImportError, AttributeError) as e:
|
||||
# Eventlet nicht verfügbar oder nicht kompatibel (z.B. Python 3.13)
|
||||
print(f"⚠️ Eventlet nicht verfügbar: {e}")
|
||||
print("🔄 Fallback auf standard threading mode für WebSocket")
|
||||
EVENTLET_AVAILABLE = False
|
||||
eventlet = None
|
||||
wsgi = None
|
||||
listen = None
|
||||
|
||||
WEBSOCKET_AVAILABLE = True
|
||||
except ImportError:
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Flask-SocketIO nicht verfügbar: {e}")
|
||||
print("📡 Dashboard läuft ohne Real-time Updates")
|
||||
WEBSOCKET_AVAILABLE = False
|
||||
EVENTLET_AVAILABLE = False
|
||||
SocketIO = None
|
||||
eventlet = None
|
||||
|
||||
from flask import request, session, current_app
|
||||
from flask_login import current_user
|
||||
@@ -122,16 +138,24 @@ class DashboardManager:
|
||||
logger.warning("WebSocket-Funktionalität nicht verfügbar. Flask-SocketIO installieren.")
|
||||
return None
|
||||
|
||||
# Bestimme den async_mode basierend auf verfügbaren Bibliotheken
|
||||
if EVENTLET_AVAILABLE:
|
||||
async_mode = 'eventlet'
|
||||
logger.info("Dashboard WebSocket-Server wird mit eventlet initialisiert")
|
||||
else:
|
||||
async_mode = 'threading'
|
||||
logger.info("Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)")
|
||||
|
||||
self.socketio = SocketIO(
|
||||
app,
|
||||
cors_allowed_origins=cors_allowed_origins,
|
||||
logger=False,
|
||||
engineio_logger=False,
|
||||
async_mode='eventlet'
|
||||
async_mode=async_mode
|
||||
)
|
||||
|
||||
self._setup_socket_handlers()
|
||||
logger.info("Dashboard WebSocket-Server initialisiert")
|
||||
logger.info(f"Dashboard WebSocket-Server initialisiert (async_mode: {async_mode})")
|
||||
return self.socketio
|
||||
|
||||
def _setup_socket_handlers(self):
|
||||
|
||||
Reference in New Issue
Block a user