📚 Erweiterte Dokumentation für den Button "Reservierung erstellen" und Verbesserungen bei der OTP-Drucker-Verfügbarkeitsprüfung. 🐛 Behebung von Problemen mit "undefined" Druckaufträgen und Optimierung der Logdateien für bessere Nachverfolgbarkeit. 📈
This commit is contained in:
@@ -1 +1,259 @@
|
||||
|
||||
# Button "Reservierung erstellen" - Problem-Analyse und Lösung
|
||||
|
||||
## Problem-Beschreibung
|
||||
|
||||
Der Button "Reservierung erstellen" in der Jobs-Seite funktioniert nicht. Das Formular wird nicht abgeschickt, wenn der Benutzer auf den Button klickt.
|
||||
|
||||
## Problem-Analyse
|
||||
|
||||
### 1. **Formular ist standardmäßig versteckt**
|
||||
|
||||
Das erweiterte Formular mit dem Submit-Button ist standardmäßig mit `class="hidden"` versteckt:
|
||||
|
||||
```html
|
||||
<div id="expanded-form" class="hidden">
|
||||
<form id="newJobForm" class="space-y-6">
|
||||
```
|
||||
|
||||
### 2. **Event-Listener wird möglicherweise zu früh registriert**
|
||||
|
||||
Der Event-Listener wird in `setupEventListeners()` registriert, aber das Form könnte zu diesem Zeitpunkt noch nicht im DOM sein.
|
||||
|
||||
### 3. **Fehlende Robust-Behandlung**
|
||||
|
||||
Die ursprüngliche Implementierung hat keine robuste Behandlung für dynamisch geladene Formulare.
|
||||
|
||||
## Identifizierte Ursachen
|
||||
|
||||
1. **Timing-Problem**: Das Formular ist beim ersten Laden der Seite versteckt
|
||||
2. **Event-Listener nicht registriert**: Wenn das Formular versteckt ist, wird der Event-Listener möglicherweise nicht korrekt registriert
|
||||
3. **Fehlende Form-Validierung**: Keine Client-seitige Validierung vor dem Submit
|
||||
|
||||
## Lösung
|
||||
|
||||
### Schritt 1: Button-Funktionalität prüfen
|
||||
|
||||
Der Benutzer muss zunächst das erweiterte Formular anzeigen:
|
||||
|
||||
1. **Option A**: Auf "Erweitert" klicken im Formular-Header
|
||||
2. **Option B**: Auf "Vollständiger Auftrag" in den Quick-Actions klicken
|
||||
|
||||
### Schritt 2: Event-Listener verbessern
|
||||
|
||||
```javascript
|
||||
// Robuste Event-Listener-Registrierung
|
||||
setupFormSubmitListeners() {
|
||||
const setupMainFormListener = () => {
|
||||
const mainForm = document.getElementById('newJobForm');
|
||||
if (mainForm) {
|
||||
console.log('✅ Hauptformular gefunden - Event Listener wird registriert');
|
||||
mainForm.removeEventListener('submit', this.handleJobSubmit.bind(this));
|
||||
mainForm.addEventListener('submit', this.handleJobSubmit.bind(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Mit MutationObserver für dynamische Inhalte
|
||||
const observer = new MutationObserver(() => {
|
||||
if (setupMainFormListener()) {
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Schritt 3: Formular-Validierung verbessern
|
||||
|
||||
```javascript
|
||||
async handleJobSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Debug-Ausgaben
|
||||
console.log('🚀 Job-Submit gestartet');
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
const jobData = {
|
||||
printer_id: parseInt(formData.get('printer_id')),
|
||||
start_iso: formData.get('start_time'),
|
||||
duration_minutes: parseInt(formData.get('duration')),
|
||||
name: formData.get('job_title') || 'Neuer Druckjob'
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Submit-Button deaktivieren
|
||||
const submitBtn = e.target.querySelector('button[type="submit"]');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="loading-spinner"></span> Erstelle...';
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/jobs', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': this.getCSRFToken()
|
||||
},
|
||||
body: JSON.stringify(jobData)
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showSuccess('Job erfolgreich erstellt!');
|
||||
this.loadJobs(); // Refresh job list
|
||||
e.target.reset(); // Reset form
|
||||
} else {
|
||||
this.showError(`Fehler beim Erstellen: ${data.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Fehler beim Job-Submit:', error);
|
||||
this.showError('Fehler beim Erstellen des Jobs');
|
||||
} finally {
|
||||
// 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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Schritt 4: ToggleFormExpansion verbessern
|
||||
|
||||
```javascript
|
||||
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');
|
||||
|
||||
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
|
||||
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) => {
|
||||
if (window.jobManager) {
|
||||
window.jobManager.handleJobSubmit(e);
|
||||
}
|
||||
});
|
||||
form.setAttribute('data-listener-added', 'true');
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
expandedForm.classList.add('hidden');
|
||||
toggleText.textContent = 'Erweitert';
|
||||
toggleIcon.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Sofort-Lösung für Benutzer
|
||||
|
||||
**Aktuelle Workaround-Schritte:**
|
||||
|
||||
1. Auf der Jobs-Seite (/jobs) nach unten scrollen
|
||||
2. Im Bereich "Neuen Druckauftrag erstellen" auf **"Erweitert"** klicken
|
||||
3. Das erweiterte Formular wird angezeigt
|
||||
4. Alle erforderlichen Felder ausfüllen:
|
||||
- Drucker auswählen
|
||||
- Startzeit eingeben
|
||||
- Dauer in Minuten
|
||||
- Job-Titel eingeben
|
||||
5. Auf **"Reservierung erstellen"** klicken
|
||||
|
||||
**Alternative:**
|
||||
|
||||
- Auf **"Vollständiger Auftrag"** in den Quick-Actions klicken
|
||||
|
||||
## Debug-Informationen
|
||||
|
||||
### Browser-Konsole überwachen
|
||||
|
||||
Nach dem Laden der Seite sollten folgende Meldungen in der Browser-Konsole erscheinen:
|
||||
|
||||
```
|
||||
✅ Hauptformular gefunden - Event Listener wird registriert
|
||||
✅ Schnell-Reservierung Formular gefunden - Event Listener wird registriert
|
||||
```
|
||||
|
||||
### Bei Formular-Submit:
|
||||
|
||||
```
|
||||
🚀 Job-Submit gestartet
|
||||
📋 Job-Daten: {printer_id: 1, start_iso: "2024-...", ...}
|
||||
🌐 Sende API-Request
|
||||
📡 API Response Status: 201
|
||||
📡 API Response Data: {success: true, ...}
|
||||
```
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Backend-Endpoint
|
||||
|
||||
- **URL**: `POST /api/jobs`
|
||||
- **Blueprint**: `blueprints.jobs.create_job()`
|
||||
- **Authentifizierung**: Login erforderlich
|
||||
- **CSRF-Schutz**: Aktiv
|
||||
|
||||
### Erforderliche Felder
|
||||
|
||||
- `printer_id` (Integer): ID des gewählten Druckers
|
||||
- `start_iso` (String): Startzeit im ISO-Format
|
||||
- `duration_minutes` (Integer): Dauer in Minuten
|
||||
- `name` (String): Job-Titel
|
||||
|
||||
### Optionale Felder
|
||||
|
||||
- `description` (String): Beschreibung
|
||||
- `file_path` (String): Pfad zur hochgeladenen Datei
|
||||
|
||||
## Status
|
||||
|
||||
- **Problem identifiziert**: ✅
|
||||
- **Ursache analysiert**: ✅
|
||||
- **Lösung implementiert**: ⏳ (In Bearbeitung)
|
||||
- **Tests durchgeführt**: ⏳ (Ausstehend)
|
||||
- **Dokumentation erstellt**: ✅
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. Code-Änderungen vollständig implementieren
|
||||
2. Funktionalität testen
|
||||
3. Event-Listener-Registrierung verifizieren
|
||||
4. Cross-Browser-Kompatibilität prüfen
|
||||
5. Performance-Impact bewerten
|
||||
|
@@ -1 +1,249 @@
|
||||
|
||||
# OTP-Aktivierung und Drucker-Verfügbarkeitsprüfung - Verbesserungen
|
||||
|
||||
## Problem
|
||||
|
||||
**Ursprüngliches Problem:** Jobs konnten mit OTP-Codes gestartet werden, obwohl alle Drucker offline waren, was zu nicht-startbaren Jobs führte und schlechte Benutzererfahrung verursachte.
|
||||
|
||||
## Implementierte Lösung
|
||||
|
||||
### 1. Erweiterte OTP-Aktivierungs-API
|
||||
|
||||
**Datei:** `blueprints/guest.py` - Funktion `api_start_job_with_code()`
|
||||
|
||||
#### Neue Funktionalitäten:
|
||||
|
||||
- **Drucker-Verfügbarkeitsprüfung vor Job-Start**
|
||||
- **Spezifische Drucker-Status-Validierung**
|
||||
- **Automatische Drucker-Zuweisung bei verfügbaren Alternativen**
|
||||
- **Detaillierte Fehlermeldungen mit Kontextinformationen**
|
||||
|
||||
#### Prüflogik:
|
||||
|
||||
1. **Spezifischer Drucker zugewiesen:**
|
||||
- Prüfung ob der zugewiesene Drucker erreichbar ist
|
||||
- Verweigerung des Starts bei offline Druckern
|
||||
- Klare Fehlermeldung mit Drucker-Namen
|
||||
|
||||
2. **Kein spezifischer Drucker:**
|
||||
- Prüfung aller aktiven Drucker mit Steckdosen-Steuerung
|
||||
- Automatische Zuweisung des ersten verfügbaren Druckers
|
||||
- Verweigerung wenn alle Drucker offline sind
|
||||
|
||||
#### Fehlertypen:
|
||||
|
||||
- `printer_offline`: Spezifischer Drucker nicht erreichbar
|
||||
- `all_printers_offline`: Alle konfigurierten Drucker offline
|
||||
- `no_printers_configured`: Keine aktiven Drucker konfiguriert
|
||||
- `printer_check_failed`: Technischer Fehler bei Status-Prüfung
|
||||
|
||||
### 2. Verbesserte Frontend-Fehlerbehandlung
|
||||
|
||||
**Datei:** `templates/guest_start_job.html`
|
||||
|
||||
#### Neue Features:
|
||||
|
||||
- **Kontextspezifische Fehlermeldungen** je nach Fehlertyp
|
||||
- **Benutzerfreundliche Erklärungen** für technische Probleme
|
||||
- **Handlungsempfehlungen** für Benutzer
|
||||
- **Visuelle Unterscheidung** verschiedener Fehlerklassen
|
||||
|
||||
#### Beispiel-Fehlermeldungen:
|
||||
|
||||
```javascript
|
||||
// Spezifischer Drucker offline
|
||||
"Der zugewiesene Drucker 'Drucker 1' ist derzeit offline und kann nicht gestartet werden.
|
||||
Bitte wenden Sie sich an den Administrator oder versuchen Sie es später erneut."
|
||||
|
||||
// Alle Drucker offline
|
||||
"Derzeit sind alle 3 Drucker offline (Drucker 1, Drucker 2, Drucker 3).
|
||||
Jobs können momentan nicht gestartet werden. Bitte warten Sie, bis mindestens ein Drucker wieder online ist."
|
||||
```
|
||||
|
||||
### 3. Erweiterte Admin-Genehmigungsfunktion
|
||||
|
||||
**Datei:** `blueprints/guest.py` - Funktion `api_approve_request()`
|
||||
|
||||
#### Verbesserungen:
|
||||
|
||||
- **Echtzeit-Drucker-Status bei Genehmigung**
|
||||
- **Intelligente Drucker-Zuweisung** (bevorzugt online Drucker)
|
||||
- **Warnung bei Zuweisung offline Drucker**
|
||||
- **Fallback-Mechanismus** für offline Zeiten
|
||||
|
||||
#### Automatische Drucker-Zuweisung:
|
||||
|
||||
1. **Priorität 1:** Online-Drucker mit aktiver Steckdose
|
||||
2. **Priorität 2:** Verfügbare offline Drucker (als Fallback)
|
||||
3. **Verweigerung:** Wenn keine Drucker konfiguriert sind
|
||||
|
||||
### 4. Neue Admin-API für Drucker-Status
|
||||
|
||||
**Datei:** `blueprints/guest.py` - Funktion `api_get_printer_status_for_admin()`
|
||||
|
||||
#### Funktionalitäten:
|
||||
|
||||
- **Echtzeit-Status aller Drucker** für Admin-Dashboard
|
||||
- **Übersichtliche Zusammenfassung** (online/offline/unkonfiguriert)
|
||||
- **Empfehlungen** für Admin-Aktionen
|
||||
- **Sortierung** nach Verfügbarkeit
|
||||
|
||||
#### Response-Struktur:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"printers": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Drucker 1",
|
||||
"status": "online",
|
||||
"can_be_assigned": true,
|
||||
"status_message": "Online (OFF)",
|
||||
"reachable": true,
|
||||
"power_state": "off"
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"total": 3,
|
||||
"online": 1,
|
||||
"offline": 2,
|
||||
"unconfigured": 0
|
||||
},
|
||||
"recommendations": [
|
||||
"Nur 1 von 3 Druckern sind verfügbar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Integration mit bestehenden Systemen
|
||||
|
||||
### Hardware-Integration
|
||||
|
||||
- **Nutzung der bestehenden `DruckerSteuerung`-Klasse**
|
||||
- **Kompatibilität mit Tapo-Steckdosen-System**
|
||||
- **Erweiterte Fehlerbehandlung** für Netzwerkprobleme
|
||||
|
||||
### Logging und Monitoring
|
||||
|
||||
- **Detaillierte Logs** für alle Verfügbarkeitsprüfungen
|
||||
- **Strukturierte Fehlermeldungen** für bessere Debugging
|
||||
- **Admin-Benachrichtigungen** bei kritischen Problemen
|
||||
|
||||
### Database-Kompatibilität
|
||||
|
||||
- **Keine Schema-Änderungen erforderlich**
|
||||
- **Nutzung bestehender Printer- und Job-Tabellen**
|
||||
- **Backward-Kompatibilität** mit existierenden Daten
|
||||
|
||||
## Vorteile der Implementierung
|
||||
|
||||
### Für Benutzer:
|
||||
|
||||
1. **Keine frustrierenden fehlgeschlagenen Job-Starts**
|
||||
2. **Klare Informationen** über Probleme und Lösungsansätze
|
||||
3. **Bessere Erwartungshaltung** durch Status-Transparenz
|
||||
4. **Reduzierte Support-Anfragen** durch selbsterklärende Meldungen
|
||||
|
||||
### Für Administratoren:
|
||||
|
||||
1. **Echtzeit-Drucker-Übersicht** in der Admin-Oberfläche
|
||||
2. **Automatische intelligente Drucker-Zuweisung**
|
||||
3. **Proaktive Warnungen** bei System-Problemen
|
||||
4. **Reduzierter manueller Aufwand** durch Automatisierung
|
||||
|
||||
### Für das System:
|
||||
|
||||
1. **Vermeidung von hängenden Jobs**
|
||||
2. **Bessere Ressourcen-Ausnutzung**
|
||||
3. **Robustere Fehlerbehandlung**
|
||||
4. **Skalierbare Architektur** für zusätzliche Drucker
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Implementierte Sicherheitsprüfungen:
|
||||
|
||||
- **Netzwerk-Erreichbarkeit** via `check_outlet_status()`
|
||||
- **Tapo-API-Validierung** mit Retry-Mechanismus
|
||||
- **Database-Transaction-Sicherheit**
|
||||
- **Input-Validierung** für alle Parameter
|
||||
|
||||
### Performance-Optimierungen:
|
||||
|
||||
- **Parallelisierte Status-Prüfungen** für mehrere Drucker
|
||||
- **Caching von Hardware-Status** (geerbt von bestehender Implementierung)
|
||||
- **Minimierte Database-Queries** durch efficient loading
|
||||
- **Timeout-Handling** für langsame Netzwerkverbindungen
|
||||
|
||||
### Error Recovery:
|
||||
|
||||
- **Graceful Degradation** bei Hardware-Problemen
|
||||
- **Fallback-Mechanismen** für offline Drucker
|
||||
- **Retry-Logik** für temporäre Netzwerkfehler
|
||||
- **Logging für Post-Mortem-Analyse**
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Umgebungsvariablen:
|
||||
|
||||
```bash
|
||||
# Keine neuen Variablen erforderlich - nutzt bestehende Tapo-Konfiguration
|
||||
TAPO_USERNAME=admin
|
||||
TAPO_PASSWORD=***
|
||||
```
|
||||
|
||||
### Admin-Interface:
|
||||
|
||||
Neue API-Endpunkte verfügbar unter:
|
||||
- `GET /api/admin/printer-status` - Drucker-Status für Admin-Dashboard
|
||||
- Erweiterte Responses bei Job-Genehmigungen mit Status-Informationen
|
||||
|
||||
## Testszenarien
|
||||
|
||||
### 1. Normaler Betrieb:
|
||||
- ✅ Job-Start mit online Drucker funktioniert wie bisher
|
||||
- ✅ Admin sieht Drucker-Status in Echtzeit
|
||||
|
||||
### 2. Alle Drucker offline:
|
||||
- ✅ OTP-Aktivierung wird verweigert mit klarer Meldung
|
||||
- ✅ Admin bekommt Warnung bei Genehmigungsversuch
|
||||
|
||||
### 3. Spezifischer Drucker offline:
|
||||
- ✅ Job-Start wird verweigert mit Drucker-Namen
|
||||
- ✅ Alternative Drucker werden nicht automatisch gewählt
|
||||
|
||||
### 4. Netzwerkprobleme:
|
||||
- ✅ Timeout-Handling verhindert hängende Requests
|
||||
- ✅ Fallback auf cached Status wenn verfügbar
|
||||
|
||||
## Wartung und Monitoring
|
||||
|
||||
### Log-Monitoring:
|
||||
|
||||
```bash
|
||||
# Neue Log-Patterns für Überwachung:
|
||||
grep "Drucker-Verfügbarkeitsprüfung" logs/guest.log
|
||||
grep "Admin-Drucker-Zuweisung" logs/guest.log
|
||||
grep "alle.*Drucker.*offline" logs/guest.log
|
||||
```
|
||||
|
||||
### Metriken für Dashboard:
|
||||
|
||||
- Anzahl verweigerter Job-Starts wegen offline Druckern
|
||||
- Durchschnittliche Drucker-Verfügbarkeit
|
||||
- Häufigkeit automatischer Drucker-Zuweisungen
|
||||
|
||||
## Zukunftserweiterungen
|
||||
|
||||
### Geplante Verbesserungen:
|
||||
|
||||
1. **Push-Benachrichtigungen** an Admins bei kritischen Offline-Situationen
|
||||
2. **Automatische Retry-Mechanismen** für Jobs bei Drucker-Recovery
|
||||
3. **Erweiterte Drucker-Priorisierung** basierend auf Warteschlangen
|
||||
4. **Integration mit Drucker-Wartungszeitplänen**
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ Vollständig implementiert und getestet
|
||||
**Version:** 1.0
|
||||
**Datum:** 2024-12-19
|
||||
**Autor:** System-Administrator
|
@@ -1 +1,260 @@
|
||||
|
||||
# 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` (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 `
|
||||
<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**
|
||||
|
||||
```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**
|
Reference in New Issue
Block a user