# Lösung: "undefined" Druckaufträge Problem ## 📋 Problem-Beschreibung Das Mercedes-Benz MYP System zeigte bei Druckaufträgen häufig "undefined" Werte an, insbesondere: - Job-Name als "undefined" - Drucker-Name als "undefined" - Dauer als "undefined Min" - Benutzer-Informationen als "undefined" ![Problem Screenshot](../utils/image/system_management/1750021899932.png) ## 🔍 Ursachen-Analyse ### 1. **Inkonsistente Datenfelder** Das Backend-Model `Job.to_dict()` gab andere Feldnamen zurück als das Frontend erwartete: **Backend gab zurück:** - `name` (statt `filename`/`title`) - `printer` (Objekt statt `printer_name`) - `user` (Objekt statt `user_name`) **Frontend erwartete:** - `filename`, `title` oder `file_name` - `printer_name` (String) - `user_name` (String) ### 2. **Fehlende Dashboard-Daten** Die Dashboard-Funktion in `app.py` war leer und lud keine Daten: ```python @app.route("/dashboard") @login_required def dashboard(): """Haupt-Dashboard""" return render_template("dashboard.html") # Keine Daten! ``` ### 3. **JavaScript Null-Checks unvollständig** ```javascript // Problematisch: ${job.filename || job.title || job.name || 'Unbekannter Job'} // Wenn alle undefined → undefined wird angezeigt ``` ## ✅ Implementierte Lösung ### 1. **Erweiterte Job.to_dict() Methode** (`models.py`) ```python def to_dict(self) -> dict: # Grundlegende Job-Informationen result = { "id": self.id, "name": self.name, "description": self.description, # ... weitere Grundfelder } # Frontend-kompatible Felder hinzufügen result.update({ # Alternative Namen für Frontend-Kompatibilität "title": self.name, "filename": self.name, "file_name": self.name, # Drucker-Informationen direkt verfügbar "printer_name": self.printer.name if self.printer else "Unbekannter Drucker", "printer_model": self.printer.model if self.printer else None, # Benutzer-Informationen direkt verfügbar "user_name": self.user.name if self.user else "Unbekannter Benutzer", "username": self.user.username if self.user else None, # Zeitstempel in deutschen Formaten "start_time": self.start_at.strftime('%d.%m.%Y %H:%M') if self.start_at else "Nicht festgelegt", "created_time": self.created_at.strftime('%d.%m.%Y %H:%M') if self.created_at else "Unbekannt", # Status-Text in Deutsch "status_text": self._get_status_text(self.status), # Berechnete Felder "progress": self._calculate_progress(), "remaining_minutes": self._calculate_remaining_minutes() }) return result def _get_status_text(self, status: str) -> str: """Wandelt englische Status-Codes in deutsche Texte um""" status_mapping = { 'scheduled': 'Geplant', 'pending': 'Wartend', 'running': 'Läuft', 'completed': 'Abgeschlossen', 'failed': 'Fehlgeschlagen', # ... weitere Mappings } return status_mapping.get(status, status.title() if status else 'Unbekannt') ``` ### 2. **Vollständige Dashboard-Funktion** (`app.py`) ```python @app.route("/dashboard") @login_required def dashboard(): """Haupt-Dashboard mit vollständigen Daten für die Anzeige""" try: db_session = get_db_session() # Aktive Jobs laden active_jobs_query = db_session.query(Job).filter( Job.status.in_(['scheduled', 'running', 'printing', 'pending']) ) 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() # Statistiken berechnen active_jobs_count = len(active_jobs) available_printers_count = len([p for p in printers if p.status in ['idle', 'ready']]) success_rate = round((completed_jobs_count / total_jobs_count * 100), 1) if total_jobs_count > 0 else 0 # Template-Daten mit Fallback-Werten template_data = { 'active_jobs_count': active_jobs_count, 'available_printers_count': available_printers_count, 'total_jobs_count': total_jobs_count, 'success_rate': success_rate, 'active_jobs': dashboard_active_jobs, 'printers': dashboard_printers, 'activities': activities, # ... weitere Daten } return render_template("dashboard.html", **template_data) except Exception as e: # Fallback-Dashboard mit sicheren Standardwerten 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)}" } return render_template("dashboard.html", **fallback_data) ``` ### 3. **Verbesserte JavaScript Null-Checks** (`global-refresh-functions.js`) ```javascript // 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; // Sichere Job-Karten-Darstellung return `

${jobName}

${statusText}

ID: ${job.id || 'N/A'}

Drucker: ${printerName}

Benutzer: ${userName}

Erstellt: ${createdDate}

${progress > 0 ? `

Fortschritt: ${progress}%

` : ''}
`; ``` ### 4. **Status-Badge-Hilfsfunktion** ```javascript 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', 'completed': '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', // ... weitere Status }; return statusClasses[status] || 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400'; } ``` ## 🎯 Ergebnis ### **Vorher:** - Job-Name: `undefined` - Drucker: `undefined` - Dauer: `undefined Min` - Status: Englisch und unformatiert ### **Nachher:** - Job-Name: "Druckjob vom 06.01.2025 14:30" (oder tatsächlicher Name) - Drucker: "Drucker 1" (tatsächlicher Drucker-Name) - Dauer: "45 Minuten" (korrekte Dauer) - Status: "Geplant", "Läuft", "Abgeschlossen" (deutsch, farbkodiert) - Fortschritt: Visuelle Fortschrittsbalken - Benutzer: Tatsächlicher Benutzername ## 🔧 Technische Details ### **Caching-Optimierung** - Job-Daten werden für 3 Minuten gecacht - Cache wird bei Status-Änderungen invalidiert - Reduzierte Datenbankabfragen ### **Fehlerbehandlung** - Fallback-Werte für alle kritischen Felder - Graceful Degradation bei Datenbankfehlern - Detaillierte Logging für Debugging ### **Performance** - Eager Loading für Beziehungen (User, Printer) - Bulk-Operationen statt N+1 Queries - Minimierte Frontend-Datenübertragung ## ✅ Tests durchgeführt 1. **Dashboard-Load**: ✅ Alle Statistiken korrekt 2. **Job-Anzeige**: ✅ Keine "undefined" Werte mehr 3. **Drucker-Status**: ✅ Korrekte Namen und Status 4. **Benutzer-Info**: ✅ Namen statt IDs angezeigt 5. **Status-Mapping**: ✅ Deutsche Übersetzungen 6. **Fehlerbehandlung**: ✅ Graceful Fallbacks ## 📚 Betroffene Dateien - `models.py` - Erweiterte Job.to_dict() Methode - `app.py` - Vollständige Dashboard-Funktion - `static/js/global-refresh-functions.js` - Verbesserte Frontend-Logik - `blueprints/jobs.py` - API-Endpunkte nutzen erweiterte Methoden - `templates/dashboard.html` - Template nutzt neue Datenfelder ## 🚀 Deployment Die Änderungen sind vollständig rückwärtskompatibel und erfordern nur einen Neustart der Anwendung. **Status: ✅ VOLLSTÄNDIG IMPLEMENTIERT UND GETESTET**