9.1 KiB
9.1 KiB
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"
🔍 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
(stattfilename
/title
)printer
(Objekt stattprinter_name
)user
(Objekt stattuser_name
)
Frontend erwartete:
filename
,title
oderfile_name
printer_name
(String)user_name
(String)
2. Fehlende Dashboard-Daten
Die Dashboard-Funktion in app.py
war leer und lud keine Daten:
@app.route("/dashboard")
@login_required
def dashboard():
"""Haupt-Dashboard"""
return render_template("dashboard.html") # Keine Daten!
3. JavaScript Null-Checks unvollständig
// 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
)
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
)
@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
)
// 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 `
<div class="job-card p-4 border rounded-lg bg-white dark:bg-slate-800 shadow-sm hover:shadow-md transition-shadow">
<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">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>
</div>
`;
4. Status-Badge-Hilfsfunktion
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
- Dashboard-Load: ✅ Alle Statistiken korrekt
- Job-Anzeige: ✅ Keine "undefined" Werte mehr
- Drucker-Status: ✅ Korrekte Namen und Status
- Benutzer-Info: ✅ Namen statt IDs angezeigt
- Status-Mapping: ✅ Deutsche Übersetzungen
- Fehlerbehandlung: ✅ Graceful Fallbacks
📚 Betroffene Dateien
models.py
- Erweiterte Job.to_dict() Methodeapp.py
- Vollständige Dashboard-Funktionstatic/js/global-refresh-functions.js
- Verbesserte Frontend-Logikblueprints/jobs.py
- API-Endpunkte nutzen erweiterte Methodentemplates/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