🐛 Backend Documentation Fix for Button Reservation Creation Issue & Undefined Jobs Problem 🖥️📚

This commit is contained in:
Till Tomczak
2025-06-20 12:16:21 +02:00
parent 533132002a
commit b29b38c8a4
22 changed files with 580 additions and 25 deletions

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -949,8 +949,182 @@ def index():
@app.route("/dashboard")
@login_required
def dashboard():
"""Haupt-Dashboard"""
return render_template("dashboard.html")
"""Haupt-Dashboard mit vollständigen Daten für die Anzeige"""
try:
from models import get_db_session, Job, Printer, User
from datetime import datetime, timedelta
db_session = get_db_session()
# ===== AKTIVE JOBS LADEN =====
active_jobs_query = db_session.query(Job).filter(
Job.status.in_(['scheduled', 'running', 'printing', 'pending'])
)
# Normale Benutzer sehen nur ihre eigenen Jobs
if not current_user.is_admin:
active_jobs_query = active_jobs_query.filter(Job.user_id == current_user.id)
active_jobs = active_jobs_query.order_by(Job.created_at.desc()).limit(10).all()
# ===== DRUCKER-DATEN LADEN =====
printers = db_session.query(Printer).filter(Printer.active == True).all()
# ===== STATISTIKEN BERECHNEN =====
# Aktive Jobs zählen
active_jobs_count = len(active_jobs)
# Verfügbare Drucker zählen
available_printers_count = len([p for p in printers if p.status in ['idle', 'ready']])
# Gesamtanzahl Jobs (benutzerabhängig)
if current_user.is_admin:
total_jobs_count = db_session.query(Job).count()
completed_jobs_count = db_session.query(Job).filter(Job.status == 'completed').count()
else:
total_jobs_count = db_session.query(Job).filter(Job.user_id == current_user.id).count()
completed_jobs_count = db_session.query(Job).filter(
Job.user_id == current_user.id,
Job.status == 'completed'
).count()
# Erfolgsrate berechnen
success_rate = round((completed_jobs_count / total_jobs_count * 100), 1) if total_jobs_count > 0 else 0
# ===== AKTIVE JOBS FÜR DASHBOARD FORMATIEREN =====
dashboard_active_jobs = []
for job in active_jobs:
job_dict = job.to_dict() # Nutzt die erweiterte to_dict Methode
# Zusätzliche Dashboard-spezifische Felder
job_dict.update({
'id': job.id,
'name': job.name or f"Job #{job.id}",
'printer': job.printer.name if job.printer else "Unbekannter Drucker",
'start_time': job.start_at.strftime('%d.%m.%Y %H:%M') if job.start_at else "Nicht festgelegt",
'status_text': job._get_status_text(job.status),
'progress': job._calculate_progress(),
'file_name': job.name # Für Template-Kompatibilität
})
dashboard_active_jobs.append(job_dict)
# ===== DRUCKER FÜR DASHBOARD FORMATIEREN =====
dashboard_printers = []
for printer in printers:
printer_dict = printer.to_dict()
# Status-Icon und -Farbe bestimmen
status_mapping = {
'online': {'icon': 'fas fa-check-circle', 'color': 'text-green-500'},
'idle': {'icon': 'fas fa-check-circle', 'color': 'text-green-500'},
'busy': {'icon': 'fas fa-clock', 'color': 'text-yellow-500'},
'offline': {'icon': 'fas fa-times-circle', 'color': 'text-red-500'},
'error': {'icon': 'fas fa-exclamation-triangle', 'color': 'text-red-500'}
}
status_info = status_mapping.get(printer.status, status_mapping['offline'])
printer_dict.update({
'status_icon': status_info['icon'],
'status_color': status_info['color'],
'is_available': printer.status in ['idle', 'online', 'ready']
})
dashboard_printers.append(printer_dict)
# ===== LETZTE AKTIVITÄTEN =====
# Letzte Jobs als Aktivitäten
recent_jobs = db_session.query(Job).filter(
Job.user_id == current_user.id if not current_user.is_admin else True
).order_by(Job.updated_at.desc()).limit(5).all()
activities = []
for job in recent_jobs:
time_diff = datetime.now() - (job.updated_at or job.created_at)
if time_diff.days == 0:
if time_diff.seconds < 3600:
time_str = f"vor {time_diff.seconds // 60} Minuten"
else:
time_str = f"vor {time_diff.seconds // 3600} Stunden"
else:
time_str = f"vor {time_diff.days} Tagen"
activities.append({
'description': f"Job '{job.name}' - Status: {job._get_status_text(job.status)}",
'time': time_str
})
# ===== TRENDS BERECHNEN (optional) =====
yesterday = datetime.now() - timedelta(days=1)
week_ago = datetime.now() - timedelta(days=7)
# Jobs von heute vs. gestern
jobs_today = db_session.query(Job).filter(
Job.created_at >= datetime.now().replace(hour=0, minute=0, second=0)
).count()
jobs_yesterday = db_session.query(Job).filter(
Job.created_at >= yesterday.replace(hour=0, minute=0, second=0),
Job.created_at < datetime.now().replace(hour=0, minute=0, second=0)
).count()
active_jobs_trend = None
if jobs_yesterday > 0:
trend_percent = round(((jobs_today - jobs_yesterday) / jobs_yesterday) * 100, 1)
active_jobs_trend = {
'value': trend_percent,
'direction': 'up' if trend_percent > 0 else 'down' if trend_percent < 0 else 'stable'
}
db_session.close()
# ===== TEMPLATE-DATEN ZUSAMMENSTELLEN =====
template_data = {
# Statistiken
'active_jobs_count': active_jobs_count,
'available_printers_count': available_printers_count,
'total_jobs_count': total_jobs_count,
'success_rate': success_rate,
# Trends
'active_jobs_trend': active_jobs_trend,
'printer_availability_trend': None, # Kann später implementiert werden
'total_jobs_trend': None,
'success_rate_trend': None,
# Daten für Listen
'active_jobs': dashboard_active_jobs,
'printers': dashboard_printers,
'activities': activities,
# Metadaten
'last_updated': datetime.now().strftime('%d.%m.%Y %H:%M'),
'user_name': current_user.name,
'is_admin': current_user.is_admin
}
app_logger.info(f"✅ Dashboard geladen für {current_user.name}: {active_jobs_count} aktive Jobs, {available_printers_count} verfügbare Drucker")
return render_template("dashboard.html", **template_data)
except Exception as e:
app_logger.error(f"❌ Fehler beim Laden des Dashboards: {str(e)}", exc_info=True)
# Fallback-Dashboard mit minimalen Daten
fallback_data = {
'active_jobs_count': 0,
'available_printers_count': 0,
'total_jobs_count': 0,
'success_rate': 0,
'active_jobs': [],
'printers': [],
'activities': [],
'error': f"Fehler beim Laden der Dashboard-Daten: {str(e)}",
'last_updated': datetime.now().strftime('%d.%m.%Y %H:%M'),
'user_name': current_user.name if current_user.is_authenticated else "Unbekannt",
'is_admin': current_user.is_admin if current_user.is_authenticated else False
}
return render_template("dashboard.html", **fallback_data)
@app.route("/csrf-test")
def csrf_test_page():

View File

@ -64856,3 +64856,63 @@ WHERE users.role = ?]
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\macros\\ui_components.html')
2025-06-20 12:06:41 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:06:44 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:06:44 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:06:44 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:06:44 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:07 - [app] app - [ERROR] ERROR - Fehler beim Laden des Benutzers :
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:07 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:14 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:14 - [app] app - [DEBUG] DEBUG - Request:
2025-06-20 12:07:14 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:14 - [app] app - [DEBUG] DEBUG - Response:
2025-06-20 12:07:22 - [app] app - [INFO] INFO - [SHUTDOWN] 🧹 Cleanup wird ausgeführt...
2025-06-20 12:07:22 - [app] app - [INFO] INFO - [SHUTDOWN] ✅ Queue Manager gestoppt
2025-06-20 12:07:22 - [app] app - [ERROR] ERROR - [SHUTDOWN] ❌ Cleanup-Fehler:
2025-06-20 12:07:24 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: database/myp.db
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [CONFIG] Erkannte Umgebung:
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [CONFIG] Production-Modus:
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [CONFIG] Verwende Development-Konfiguration
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [DEVELOPMENT] Aktiviere Development-Konfiguration
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Konfiguration aktiviert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Environment:
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Debug Mode:
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ SQL Echo:
2025-06-20 12:07:25 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Admin-Berechtigungen beim Start korrigiert: erstellt, aktualisiert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] 🚀 Starte MYP -Umgebung
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] 🏢
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] 🔒 Air-Gapped:
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] Initialisiere Datenbank...
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] ✅ Datenbank initialisiert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] Prüfe Initial-Admin...
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] ✅ Admin-Benutzer geprüft
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] Initialisiere statische Drucker...
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Drucker aktualisiert: Drucker 1 (192.168.0.100)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Drucker aktualisiert: Drucker 2 (192.168.0.101)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Drucker aktualisiert: Drucker 3 (192.168.0.102)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Drucker aktualisiert: Drucker 4 (192.168.0.103)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Drucker aktualisiert: Drucker 5 (192.168.0.104)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - Drucker aktualisiert: Drucker 6 (192.168.0.106)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - ✅ Statische Drucker-Initialisierung abgeschlossen: 0 erstellt, 6 aktualisiert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - 📍 Alle Drucker sind für Standort 'TBA Marienfelde' konfiguriert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - 🌐 IP-Bereich: 192.168.0.100-106 (außer .105)
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] ✅ Statische Drucker konfiguriert
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] Starte Queue Manager...
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] ✅ Queue Manager gestartet
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] Starte Job Scheduler...
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] ✅ Job Scheduler gestartet
2025-06-20 12:07:25 - [app] app - [INFO] INFO - [STARTUP] Initialisiere Steckdosen (alle auf 'aus' = frei)...
2025-06-20 12:07:43 - [app] app - [WARNING] WARNING - [STARTUP] ⚠️ Keine der 6 Steckdosen konnte initialisiert werden
2025-06-20 12:07:43 - [app] app - [INFO] INFO - [STARTUP] 🌐 Server startet auf http://:

View File

@ -508,3 +508,5 @@
2025-06-20 12:05:13 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)
2025-06-20 12:06:21 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert
2025-06-20 12:06:21 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)
2025-06-20 12:07:23 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert
2025-06-20 12:07:23 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)

View File

@ -1047,3 +1047,5 @@
2025-06-20 12:05:13 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-20 12:06:21 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
2025-06-20 12:06:21 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-20 12:07:24 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
2025-06-20 12:07:24 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)

View File

@ -111,3 +111,4 @@
2025-06-20 12:02:32 - [drucker_steuerung] drucker_steuerung - [INFO] INFO - 🖨️ Drucker-Steuerungs-Blueprint (Backend-Kontrolle) geladen
2025-06-20 12:05:14 - [drucker_steuerung] drucker_steuerung - [INFO] INFO - 🖨️ Drucker-Steuerungs-Blueprint (Backend-Kontrolle) geladen
2025-06-20 12:06:22 - [drucker_steuerung] drucker_steuerung - [INFO] INFO - 🖨️ Drucker-Steuerungs-Blueprint (Backend-Kontrolle) geladen
2025-06-20 12:07:25 - [drucker_steuerung] drucker_steuerung - [INFO] INFO - 🖨️ Drucker-Steuerungs-Blueprint (Backend-Kontrolle) geladen

View File

@ -840,3 +840,4 @@
2025-06-20 12:02:32 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert
2025-06-20 12:05:14 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert
2025-06-20 12:06:22 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert
2025-06-20 12:07:25 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert

View File

@ -3911,3 +3911,35 @@
2025-06-20 12:06:19 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.103 nicht abrufbar: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000140D752EAD0>, 'Connection to 192.168.0.103 timed out. (connect timeout=2)'))
2025-06-20 12:06:21 - [hardware_integration] hardware_integration - [INFO] INFO - 🚀 Hardware Integration (Backend-Kontrolle) erfolgreich geladen
2025-06-20 12:06:23 - [hardware_integration] hardware_integration - [INFO] INFO - 🎯 DruckerSteuerung initialisiert - BACKEND ÜBERNIMMT KONTROLLE
2025-06-20 12:06:47 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ 192.168.0.100 ist über keine Methode erreichbar
2025-06-20 12:06:47 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ 192.168.0.100 ist über keine Methode erreichbar
2025-06-20 12:06:47 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Steckdose 192.168.0.100 ist im Netzwerk nicht erreichbar
2025-06-20 12:06:47 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Steckdose 192.168.0.100 ist im Netzwerk nicht erreichbar
2025-06-20 12:06:49 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.100 nicht abrufbar: HTTPConnectionPool(host='192.168.0.100', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9A47E00>, 'Connection to 192.168.0.100 timed out. (connect timeout=2)'))
2025-06-20 12:06:49 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.100 nicht abrufbar: HTTPConnectionPool(host='192.168.0.100', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9AE7110>, 'Connection to 192.168.0.100 timed out. (connect timeout=2)'))
2025-06-20 12:06:51 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.100 nicht abrufbar: HTTPConnectionPool(host='192.168.0.100', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9AE7C50>, 'Connection to 192.168.0.100 timed out. (connect timeout=2)'))
2025-06-20 12:06:51 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.100 nicht abrufbar: HTTPConnectionPool(host='192.168.0.100', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9B00D60>, 'Connection to 192.168.0.100 timed out. (connect timeout=2)'))
2025-06-20 12:06:53 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.100 nicht abrufbar: HTTPConnectionPool(host='192.168.0.100', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9B016E0>, 'Connection to 192.168.0.100 timed out. (connect timeout=2)'))
2025-06-20 12:06:53 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.100 nicht abrufbar: HTTPConnectionPool(host='192.168.0.100', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9A8C950>, 'Connection to 192.168.0.100 timed out. (connect timeout=2)'))
2025-06-20 12:06:59 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ 192.168.0.101 ist über keine Methode erreichbar
2025-06-20 12:06:59 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Steckdose 192.168.0.101 ist im Netzwerk nicht erreichbar
2025-06-20 12:06:59 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ 192.168.0.101 ist über keine Methode erreichbar
2025-06-20 12:06:59 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Steckdose 192.168.0.101 ist im Netzwerk nicht erreichbar
2025-06-20 12:07:01 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.101 nicht abrufbar: HTTPConnectionPool(host='192.168.0.101', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B99CE360>, 'Connection to 192.168.0.101 timed out. (connect timeout=2)'))
2025-06-20 12:07:01 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.101 nicht abrufbar: HTTPConnectionPool(host='192.168.0.101', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA05A0>, 'Connection to 192.168.0.101 timed out. (connect timeout=2)'))
2025-06-20 12:07:03 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.101 nicht abrufbar: HTTPConnectionPool(host='192.168.0.101', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA09E0>, 'Connection to 192.168.0.101 timed out. (connect timeout=2)'))
2025-06-20 12:07:03 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.101 nicht abrufbar: HTTPConnectionPool(host='192.168.0.101', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA0E20>, 'Connection to 192.168.0.101 timed out. (connect timeout=2)'))
2025-06-20 12:07:05 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.101 nicht abrufbar: HTTPConnectionPool(host='192.168.0.101', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA1260>, 'Connection to 192.168.0.101 timed out. (connect timeout=2)'))
2025-06-20 12:07:05 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.101 nicht abrufbar: HTTPConnectionPool(host='192.168.0.101', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA16A0>, 'Connection to 192.168.0.101 timed out. (connect timeout=2)'))
2025-06-20 12:07:11 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ 192.168.0.102 ist über keine Methode erreichbar
2025-06-20 12:07:11 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Steckdose 192.168.0.102 ist im Netzwerk nicht erreichbar
2025-06-20 12:07:11 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ 192.168.0.102 ist über keine Methode erreichbar
2025-06-20 12:07:11 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Steckdose 192.168.0.102 ist im Netzwerk nicht erreichbar
2025-06-20 12:07:13 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.102 nicht abrufbar: HTTPConnectionPool(host='192.168.0.102', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA2690>, 'Connection to 192.168.0.102 timed out. (connect timeout=2)'))
2025-06-20 12:07:13 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.102 nicht abrufbar: HTTPConnectionPool(host='192.168.0.102', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA2030>, 'Connection to 192.168.0.102 timed out. (connect timeout=2)'))
2025-06-20 12:07:15 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.102 nicht abrufbar: HTTPConnectionPool(host='192.168.0.102', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA28B0>, 'Connection to 192.168.0.102 timed out. (connect timeout=2)'))
2025-06-20 12:07:15 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.102 nicht abrufbar: HTTPConnectionPool(host='192.168.0.102', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B9BA2CF0>, 'Connection to 192.168.0.102 timed out. (connect timeout=2)'))
2025-06-20 12:07:17 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.102 nicht abrufbar: HTTPConnectionPool(host='192.168.0.102', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B99CFF00>, 'Connection to 192.168.0.102 timed out. (connect timeout=2)'))
2025-06-20 12:07:17 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Energiedaten von 192.168.0.102 nicht abrufbar: HTTPConnectionPool(host='192.168.0.102', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x000001B1B99CEBE0>, 'Connection to 192.168.0.102 timed out. (connect timeout=2)'))
2025-06-20 12:07:24 - [hardware_integration] hardware_integration - [INFO] INFO - 🚀 Hardware Integration (Backend-Kontrolle) erfolgreich geladen
2025-06-20 12:07:25 - [hardware_integration] hardware_integration - [INFO] INFO - 🎯 DruckerSteuerung initialisiert - BACKEND ÜBERNIMMT KONTROLLE

View File

@ -2032,3 +2032,8 @@
2025-06-20 12:06:41 - [job_queue_system] job_queue_system - [ERROR] ERROR - Fehler bei Konflikt-Erkennung: 'Job' object has no attribute 'priority'
2025-06-20 12:06:41 - [job_queue_system] job_queue_system - [ERROR] ERROR - Fehler bei Konflikt-Erkennung: 'Job' object has no attribute 'priority'
2025-06-20 12:06:41 - [job_queue_system] job_queue_system - [ERROR] ERROR - Fehler bei Konflikt-Erkennung: 'Job' object has no attribute 'priority'
2025-06-20 12:07:07 - [job_queue_system] job_queue_system - [ERROR] ERROR - Fehler bei Konflikt-Erkennung: 'Job' object has no attribute 'priority'
2025-06-20 12:07:22 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
2025-06-20 12:07:24 - [job_queue_system] job_queue_system - [INFO] INFO - ✅ Job & Queue System Module initialisiert
2025-06-20 12:07:24 - [job_queue_system] job_queue_system - [INFO] INFO - 📊 MASSIVE Konsolidierung: 4 Dateien → 1 Datei (75% Reduktion)
2025-06-20 12:07:25 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)

View File

@ -1111,3 +1111,13 @@ sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) FOREIGN KEY constraint f
2025-06-20 12:06:41 - [jobs] jobs - [WARNING] WARNING - ⚠️ Kritische Konflikte gefunden: ["Fehler bei Konflikt-Analyse: 'Job' object has no attribute 'priority'"]
2025-06-20 12:06:41 - [jobs] jobs - [WARNING] WARNING - ⚠️ Kritische Konflikte gefunden: ["Fehler bei Konflikt-Analyse: 'Job' object has no attribute 'priority'"]
2025-06-20 12:06:41 - [jobs] jobs - [WARNING] WARNING - ⚠️ Kritische Konflikte gefunden: ["Fehler bei Konflikt-Analyse: 'Job' object has no attribute 'priority'"]
2025-06-20 12:06:44 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-20 12:06:44 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 3 von 3 (Seite 1)
2025-06-20 12:07:07 - [jobs] jobs - [INFO] INFO - 🚀 Neue Job-Erstellung gestartet von Benutzer 1
2025-06-20 12:07:07 - [jobs] jobs - [WARNING] WARNING - ⚠️ Kritische Konflikte gefunden: ["Fehler bei Konflikt-Analyse: 'Job' object has no attribute 'priority'"]
2025-06-20 12:07:14 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-20 12:07:14 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 3 von 3 (Seite 1)
2025-06-20 12:07:44 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-20 12:07:44 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 3 von 3 (Seite 1)
2025-06-20 12:08:14 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-20 12:08:14 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 3 von 3 (Seite 1)

View File

@ -1035,3 +1035,5 @@
2025-06-20 12:05:14 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-20 12:06:22 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert
2025-06-20 12:06:22 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-20 12:07:25 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert
2025-06-20 12:07:25 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)

View File

@ -572,3 +572,4 @@ WHERE users.role = ?]
2025-06-20 12:03:09 - [permissions] permissions - [INFO] INFO - UserPermission für Admin-Benutzer 1 aktualisiert
2025-06-20 12:05:14 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert
2025-06-20 12:06:22 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert
2025-06-20 12:07:25 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert

View File

@ -4720,3 +4720,52 @@
2025-06-20 12:06:41 - [scheduler] scheduler - [INFO] INFO - ❌ Fehlgeschlagen: 0
2025-06-20 12:06:41 - [scheduler] scheduler - [WARNING] WARNING - ⚠️ KEINE Steckdose konnte initialisiert werden!
2025-06-20 12:06:41 - [scheduler] scheduler - [INFO] INFO - ============================================================
2025-06-20 12:06:49 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:06:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:06:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:06:53 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:06:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:06:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:07:19 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:07:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:07:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:07:24 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-20 12:07:25 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-20 12:07:25 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-20 12:07:25 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Systemstart...
2025-06-20 12:07:25 - [scheduler] scheduler - [INFO] INFO - 🔍 Prüfe 6 konfigurierte Steckdosen...
2025-06-20 12:07:25 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:07:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:07:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:07:28 - [scheduler] scheduler - [WARNING] WARNING - 📡 Drucker 1: Steckdose 192.168.0.100 nicht erreichbar
2025-06-20 12:07:31 - [scheduler] scheduler - [WARNING] WARNING - 📡 Drucker 2: Steckdose 192.168.0.101 nicht erreichbar
2025-06-20 12:07:34 - [scheduler] scheduler - [WARNING] WARNING - 📡 Drucker 3: Steckdose 192.168.0.102 nicht erreichbar
2025-06-20 12:07:37 - [scheduler] scheduler - [WARNING] WARNING - 📡 Drucker 4: Steckdose 192.168.0.103 nicht erreichbar
2025-06-20 12:07:40 - [scheduler] scheduler - [WARNING] WARNING - 📡 Drucker 5: Steckdose 192.168.0.104 nicht erreichbar
2025-06-20 12:07:43 - [scheduler] scheduler - [WARNING] WARNING - 📡 Drucker 6: Steckdose 192.168.0.106 nicht erreichbar
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - ============================================================
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - 🎯 STECKDOSEN-INITIALISIERUNG ABGESCHLOSSEN
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - 📊 Gesamt: 6 Steckdosen
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - ✅ Erfolgreich: 0
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - 📡 Nicht erreichbar: 6
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - ❌ Fehlgeschlagen: 0
2025-06-20 12:07:43 - [scheduler] scheduler - [WARNING] WARNING - ⚠️ KEINE Steckdose konnte initialisiert werden!
2025-06-20 12:07:43 - [scheduler] scheduler - [INFO] INFO - ============================================================
2025-06-20 12:07:49 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:07:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:07:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:07:55 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:07:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:07:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:08:19 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:08:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:08:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:08:19 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: Gastauftrag: Till Tomczaktet
2025-06-20 12:08:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:08:19 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten
2025-06-20 12:08:25 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: Gastauftrag: Till Tomczaktet
2025-06-20 12:08:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:08:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten
2025-06-20 12:08:25 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: Gastauftrag: Till Tomczaktet
2025-06-20 12:08:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Steckdose für Drucker 2: name 'tapo_controller' is not defined
2025-06-20 12:08:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten

View File

@ -1565,3 +1565,6 @@
2025-06-20 12:06:21 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
2025-06-20 12:06:21 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-20 12:06:22 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert
2025-06-20 12:07:24 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
2025-06-20 12:07:24 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-20 12:07:25 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert

View File

@ -4142,3 +4142,12 @@
2025-06-20 12:06:22 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-20 12:06:22 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-20 12:06:22 - [startup] startup - [INFO] INFO - ==================================================
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - ==================================================
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - [START] MYP Platform Backend wird gestartet...
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.13.3 (tags/v3.13.3:6280bb5, Apr 8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-20T12:07:25.305331
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-20 12:07:25 - [startup] startup - [INFO] INFO - ==================================================

View File

@ -1365,3 +1365,7 @@
2025-06-20 12:05:13 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-20 12:05:52 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-20 12:05:52 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-20 12:06:21 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-20 12:06:21 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-20 12:07:24 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-20 12:07:24 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)

View File

@ -511,3 +511,5 @@
2025-06-20 12:05:13 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-20 12:06:21 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-20 12:06:21 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-20 12:07:23 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-20 12:07:23 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet

File diff suppressed because one or more lines are too long

View File

@ -281,17 +281,38 @@ window.refreshJobs = async function() {
return '';
}
// Verbesserte Feldermapping für Frontend-Kompatibilität
const jobName = job.name || job.title || job.filename || job.file_name || 'Unbekannter Job';
const printerName = job.printer_name || (job.printer?.name) || 'Unbekannter Drucker';
const userName = job.user_name || (job.user?.name) || 'Unbekannter Benutzer';
const statusText = job.status_text || job.status || 'Unbekannt';
const createdDate = job.created_time || (job.created_at ? new Date(job.created_at).toLocaleDateString('de-DE') : 'Unbekannt');
const progress = job.progress || 0;
return `
<div class="job-card p-4 border rounded-lg bg-white dark:bg-slate-800 shadow-sm hover:shadow-md transition-shadow">
<h3 class="font-semibold text-gray-900 dark:text-white mb-2">
${job.filename || job.title || job.name || 'Unbekannter Job'}
</h3>
<div class="flex justify-between items-start mb-3">
<h3 class="font-semibold text-gray-900 dark:text-white text-lg">
${jobName}
</h3>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusBadgeClass(job.status)}">
${statusText}
</span>
</div>
<div class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<p><span class="font-medium">ID:</span> ${job.id || 'N/A'}</p>
<p><span class="font-medium">Status:</span> ${job.status || 'Unbekannt'}</p>
${job.printer_name ? `<p><span class="font-medium">Drucker:</span> ${job.printer_name}</p>` : ''}
${job.created_at ? `<p><span class="font-medium">Erstellt:</span> ${new Date(job.created_at).toLocaleDateString('de-DE')}</p>` : ''}
<p><span class="font-medium">Drucker:</span> ${printerName}</p>
<p><span class="font-medium">Benutzer:</span> ${userName}</p>
<p><span class="font-medium">Erstellt:</span> ${createdDate}</p>
${progress > 0 ? `<p><span class="font-medium">Fortschritt:</span> ${progress}%</p>` : ''}
</div>
${progress > 0 ? `
<div class="mt-3">
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="bg-blue-600 h-2 rounded-full" style="width: ${progress}%"></div>
</div>
</div>
` : ''}
</div>
`;
}).filter(card => card !== '').join('');
@ -342,13 +363,12 @@ window.refreshJobs = async function() {
<div class="text-red-400 dark:text-red-600 text-6xl mb-4">⚠️</div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Fehler beim Laden</h3>
<p class="text-gray-500 dark:text-gray-400 mb-4">${errorMessage}</p>
<button onclick="refreshJobs()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
<button onclick="refreshJobs()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Erneut versuchen
</button>
</div>
`;
}
} finally {
if (refreshButton) {
refreshButton.disabled = false;
@ -733,4 +753,24 @@ document.addEventListener('visibilitychange', function() {
}
});
console.log('🔄 Globale Refresh-Funktionen geladen');
console.log('🔄 Globale Refresh-Funktionen geladen');
/**
* Hilfsfunktion für Status-Badge-CSS-Klassen
*/
function getStatusBadgeClass(status) {
const statusClasses = {
'scheduled': 'bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-400',
'pending': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-400',
'running': 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400',
'printing': 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400',
'completed': 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400',
'finished': 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400',
'failed': 'bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400',
'cancelled': 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400',
'aborted': 'bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400',
'paused': 'bg-orange-100 text-orange-800 dark:bg-orange-900/20 dark:text-orange-400'
};
return statusClasses[status] || 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400';
}

View File

@ -1059,15 +1059,16 @@ class JobManager {
const fileArea = document.getElementById('file-upload-area');
const fileInput = document.getElementById('stl_file');
fileArea.addEventListener('click', () => fileInput.click());
fileArea.addEventListener('dragover', this.handleDragOver.bind(this));
fileArea.addEventListener('dragleave', this.handleDragLeave.bind(this));
fileArea.addEventListener('drop', this.handleFileDrop.bind(this));
fileInput.addEventListener('change', this.handleFileSelect.bind(this));
if (fileArea && fileInput) {
fileArea.addEventListener('click', () => fileInput.click());
fileArea.addEventListener('dragover', this.handleDragOver.bind(this));
fileArea.addEventListener('dragleave', this.handleDragLeave.bind(this));
fileArea.addEventListener('drop', this.handleFileDrop.bind(this));
fileInput.addEventListener('change', this.handleFileSelect.bind(this));
}
// Form Submissions
document.getElementById('newJobForm').addEventListener('submit', this.handleJobSubmit.bind(this));
document.getElementById('quickReservationForm').addEventListener('submit', this.handleQuickReservation.bind(this));
// Form Submissions - Robuste Registrierung
this.setupFormSubmitListeners();
// Modal Close Events
document.addEventListener('keydown', (e) => {
@ -1077,6 +1078,80 @@ class JobManager {
});
}
setupFormSubmitListeners() {
// Main Job Form Handler - Robust mit Retry-Mechanismus
const setupMainFormListener = () => {
const mainForm = document.getElementById('newJobForm');
if (mainForm) {
console.log('✅ Hauptformular gefunden - Event Listener wird registriert');
// Entferne vorherige Listener um Duplikate zu vermeiden
mainForm.removeEventListener('submit', this.handleJobSubmit.bind(this));
mainForm.addEventListener('submit', this.handleJobSubmit.bind(this));
return true;
}
return false;
};
// Quick Reservation Form Handler
const setupQuickFormListener = () => {
const quickForm = document.getElementById('quickReservationForm');
if (quickForm) {
console.log('✅ Schnell-Reservierung Formular gefunden - Event Listener wird registriert');
quickForm.removeEventListener('submit', this.handleQuickReservation.bind(this));
quickForm.addEventListener('submit', this.handleQuickReservation.bind(this));
return true;
}
return false;
};
// Sofort versuchen
const mainFormReady = setupMainFormListener();
const quickFormReady = setupQuickFormListener();
// Falls Formulare nicht sofort verfügbar sind, mit Observer überwachen
if (!mainFormReady || !quickFormReady) {
console.log('⏳ Einige Formulare nicht sofort verfügbar - verwende MutationObserver');
const observer = new MutationObserver(() => {
if (!mainFormReady && setupMainFormListener()) {
console.log('✅ Hauptformular nachträglich gefunden und registriert');
}
if (!quickFormReady && setupQuickFormListener()) {
console.log('✅ Schnell-Formular nachträglich gefunden und registriert');
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Observer nach 10 Sekunden stoppen
setTimeout(() => observer.disconnect(), 10000);
}
// Default-Startzeiten setzen
this.setDefaultStartTimes();
}
setDefaultStartTimes() {
// Startzeit für Quick Reservation
const startTimeInput = document.getElementById('quick-start-time');
if (startTimeInput) {
const now = new Date();
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
startTimeInput.value = now.toISOString().slice(0, 16);
}
// Startzeit für Hauptformular
const mainStartTimeInput = document.getElementById('start_time');
if (mainStartTimeInput) {
const now = new Date();
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
mainStartTimeInput.value = now.toISOString().slice(0, 16);
}
}
async loadJobs() {
try {
document.getElementById('jobs-loading').classList.remove('hidden');
@ -1893,6 +1968,10 @@ class JobManager {
async handleJobSubmit(e) {
e.preventDefault();
console.log('🚀 Job-Submit gestartet');
console.log('📋 Event:', e);
console.log('📋 Form:', e.target);
const formData = new FormData(e.target);
const jobData = {
printer_id: parseInt(formData.get('printer_id')),
@ -1901,23 +1980,54 @@ class JobManager {
name: formData.get('job_title') || 'Neuer Druckjob'
};
console.log('📋 Job-Daten:', jobData);
// Validierung
if (!jobData.printer_id) {
this.showError('Bitte wählen Sie einen Drucker aus');
return;
}
if (!jobData.start_iso) {
this.showError('Bitte geben Sie eine Startzeit an');
return;
}
if (!jobData.duration_minutes || jobData.duration_minutes <= 0) {
this.showError('Bitte geben Sie eine gültige Dauer ein');
return;
}
if (!jobData.name || jobData.name.trim() === '') {
this.showError('Bitte geben Sie einen Job-Titel ein');
return;
}
// Submit-Button deaktivieren um Doppel-Submits zu verhindern
const submitBtn = e.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="loading-spinner"></span> Erstelle...';
}
// File upload handling (optional)
const fileInput = document.getElementById('stl_file');
let uploadedFilePath = null;
if (fileInput.files.length > 0) {
if (fileInput && fileInput.files.length > 0) {
try {
console.log('📁 Datei-Upload gestartet');
// Datei hochladen
const uploadResult = await this.uploadJobFile(fileInput.files[0]);
if (uploadResult.success) {
uploadedFilePath = uploadResult.path;
console.log('Datei erfolgreich hochgeladen:', uploadResult.filename);
console.log('Datei erfolgreich hochgeladen:', uploadResult.filename);
} else {
this.showError(`Fehler beim Datei-Upload: ${uploadResult.error}`);
return;
}
} catch (error) {
console.error('Fehler beim Datei-Upload:', error);
console.error('Fehler beim Datei-Upload:', error);
this.showError('Fehler beim Hochladen der Datei');
return;
}
@ -1929,6 +2039,7 @@ class JobManager {
}
try {
console.log('🌐 Sende API-Request');
const response = await fetch('/api/jobs', {
method: 'POST',
headers: {
@ -1938,7 +2049,9 @@ class JobManager {
body: JSON.stringify(jobData)
});
console.log('📡 API Response Status:', response.status);
const data = await response.json();
console.log('📡 API Response Data:', data);
if (data.success) {
this.showSuccess('Job erfolgreich erstellt!');
@ -1948,13 +2061,34 @@ class JobManager {
e.target.reset();
// Collapse form
toggleFormExpansion();
const expandedForm = document.getElementById('expanded-form');
if (expandedForm && !expandedForm.classList.contains('hidden')) {
toggleFormExpansion();
}
// Show additional info if immediate start
if (data.immediate_start) {
setTimeout(() => {
this.showSuccess('Job wurde sofort gestartet und Drucker eingeschaltet!');
}, 1000);
}
} else {
this.showError(`Fehler beim Erstellen: ${data.error}`);
}
} catch (error) {
console.error('Error creating job:', error);
console.error('❌ Fehler beim Job-Submit:', error);
this.showError('Fehler beim Erstellen des Jobs');
} finally {
// Submit-Button wieder aktivieren
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.innerHTML = `
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
<span>Reservierung erstellen</span>
`;
}
}
}
@ -2156,16 +2290,39 @@ function showQuickReservation() {
}
function toggleFormExpansion() {
console.log('🔄 toggleFormExpansion() aufgerufen');
const expandedForm = document.getElementById('expanded-form');
const toggleBtn = document.getElementById('form-toggle-btn');
const toggleText = toggleBtn.querySelector('span');
const toggleIcon = toggleBtn.querySelector('svg');
console.log('📋 Expandierte Form:', expandedForm);
console.log('🔘 Toggle-Button:', toggleBtn);
if (expandedForm.classList.contains('hidden')) {
console.log('👁️ Formular wird angezeigt');
expandedForm.classList.remove('hidden');
toggleText.textContent = 'Reduziert';
toggleIcon.style.transform = 'rotate(180deg)';
// Event-Listener für das Formular erneut registrieren falls nötig
setTimeout(() => {
const form = document.getElementById('newJobForm');
if (form && !form.hasAttribute('data-listener-added')) {
console.log('🔧 Registriere Event-Listener für erweiterte Form');
form.addEventListener('submit', (e) => {
console.log('📨 Formular-Submit über direkten Listener');
if (window.jobManager) {
window.jobManager.handleJobSubmit(e);
} else {
console.error('❌ JobManager nicht verfügbar');
}
});
form.setAttribute('data-listener-added', 'true');
}
}, 100);
} else {
console.log('👁️‍🗨️ Formular wird versteckt');
expandedForm.classList.add('hidden');
toggleText.textContent = 'Erweitert';
toggleIcon.style.transform = 'rotate(0deg)';