"feat: Update job templates in frontend"

This commit is contained in:
Till Tomczak 2025-05-29 12:22:02 +02:00
parent 4f7602b047
commit 89b085dec9
2 changed files with 183 additions and 30 deletions

View File

@ -1,40 +1,23 @@
import os import os
import sys import sys
import logging import logging
import threading import atexit
import time
import subprocess
import socket
import json
import secrets
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta from datetime import datetime, timedelta
from functools import wraps from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, send_file, abort, session
from typing import Optional, Dict, List, Tuple, Any, Union from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session, send_file, Response, make_response
from flask_login import LoginManager, login_user, logout_user, login_required, current_user, UserMixin
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import sqlalchemy.exc from sqlalchemy.orm import sessionmaker, joinedload
import sqlalchemy
from sqlalchemy.orm import joinedload
from sqlalchemy import func from sqlalchemy import func
from PyP100 import PyP110
from flask_wtf.csrf import CSRFProtect
from flask_wtf.csrf import CSRFError
from config.settings import ( # Lokale Imports
SECRET_KEY, TAPO_USERNAME, TAPO_PASSWORD, PRINTERS, from models import init_database, create_initial_admin, User, Printer, Job, Stats, SystemLog, get_db_session, GuestRequest, UserPermission, Notification
FLASK_HOST, FLASK_PORT, FLASK_DEBUG, SESSION_LIFETIME, from utils.logging_config import setup_logging, get_logger
SCHEDULER_INTERVAL, SCHEDULER_ENABLED, get_ssl_context, FLASK_FALLBACK_PORT, from utils.printer_status import check_printer_status, check_multiple_printers_status
SSL_ENABLED, SSL_CERT_PATH, SSL_KEY_PATH from utils.decorators import measure_execution_time
) from utils.file_manager import FileManager
from utils.logging_config import setup_logging, get_logger, log_startup_info, debug_request, debug_response, measure_execution_time from utils.job_scheduler import JobScheduler, get_job_scheduler
from models import User, Printer, Job, Stats, GuestRequest, UserPermission, Notification, get_db_session, init_database, create_initial_admin from utils.queue_manager import start_queue_manager, stop_queue_manager, get_queue_manager
from utils.job_scheduler import scheduler from config.settings import SECRET_KEY, UPLOAD_FOLDER, ALLOWED_EXTENSIONS, ENVIRONMENT
from utils.template_helpers import register_template_helpers
from utils.database_utils import backup_manager, database_monitor, maintenance_scheduler
# Blueprints importieren # Blueprints importieren
from blueprints.guest import guest_blueprint from blueprints.guest import guest_blueprint
@ -4067,6 +4050,45 @@ def mark_all_notifications_read():
# ===== ENDE BENACHRICHTIGUNGS-API-ENDPUNKTE ===== # ===== ENDE BENACHRICHTIGUNGS-API-ENDPUNKTE =====
# ===== QUEUE-MANAGER-API-ENDPUNKTE =====
@app.route('/api/queue/status', methods=['GET'])
@login_required
def get_queue_status():
"""Gibt den aktuellen Status der Drucker-Warteschlangen zurück."""
try:
queue_manager = get_queue_manager()
status = queue_manager.get_queue_status()
return jsonify({
"success": True,
"queue_status": status
})
except Exception as e:
app_logger.error(f"Fehler beim Abrufen des Queue-Status: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Abrufen des Queue-Status: {str(e)}"
}), 500
@app.route('/api/queue/check-now', methods=['POST'])
@login_required
def trigger_queue_check():
"""Triggert eine sofortige Überprüfung der Warteschlangen."""
try:
# Bestehende check_waiting_jobs API verwenden
return check_waiting_jobs()
except Exception as e:
app_logger.error(f"Fehler beim manuellen Queue-Check: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim manuellen Queue-Check: {str(e)}"
}), 500
# ===== ENDE QUEUE-MANAGER-API-ENDPUNKTE =====
# ===== STARTUP UND MAIN ===== # ===== STARTUP UND MAIN =====
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
@ -4082,6 +4104,21 @@ if __name__ == "__main__":
# Template-Hilfsfunktionen registrieren # Template-Hilfsfunktionen registrieren
register_template_helpers(app) register_template_helpers(app)
# Queue-Manager für automatische Drucker-Überwachung starten
try:
queue_manager = start_queue_manager()
app_logger.info("✅ Printer Queue Manager erfolgreich gestartet")
# Shutdown-Handler registrieren
def cleanup_queue_manager():
app_logger.info("🔄 Beende Queue Manager...")
stop_queue_manager()
atexit.register(cleanup_queue_manager)
except Exception as e:
app_logger.error(f"❌ Fehler beim Starten des Queue-Managers: {str(e)}")
# Scheduler starten (falls aktiviert) # Scheduler starten (falls aktiviert)
if SCHEDULER_ENABLED: if SCHEDULER_ENABLED:
try: try:

View File

@ -26,6 +26,9 @@
<div class="relative overflow-hidden rounded-2xl p-6 bg-white/70 dark:bg-slate-800/70 backdrop-blur-lg border border-gray-200/80 dark:border-slate-700/30 shadow-xl transition-all duration-300"> <div class="relative overflow-hidden rounded-2xl p-6 bg-white/70 dark:bg-slate-800/70 backdrop-blur-lg border border-gray-200/80 dark:border-slate-700/30 shadow-xl transition-all duration-300">
<h2 class="text-xl font-semibold mb-6 text-slate-900 dark:text-white">Neue Reservierung anlegen</h2> <h2 class="text-xl font-semibold mb-6 text-slate-900 dark:text-white">Neue Reservierung anlegen</h2>
<!-- Queue-Status-Anzeige -->
<div id="queue-status-info"></div>
<form id="newJobForm" class="space-y-6"> <form id="newJobForm" class="space-y-6">
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3"> <div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
<!-- Drucker auswählen --> <!-- Drucker auswählen -->
@ -569,6 +572,119 @@ function checkWaitingJobs() {
}); });
} }
// Queue-Status anzeigen (neue Funktion)
function loadQueueStatus() {
fetch('/api/queue/status')
.then(response => response.json())
.then(data => {
if (data.success && data.queue_status) {
updateQueueStatusDisplay(data.queue_status);
}
})
.catch(error => {
console.error('Fehler beim Laden des Queue-Status:', error);
});
}
// Queue-Status-Anzeige aktualisieren (neue Funktion)
function updateQueueStatusDisplay(queueStatus) {
const statusContainer = document.getElementById('queue-status-info');
if (!statusContainer) return;
const { waiting_jobs, online_printers, total_printers, queue_manager_running } = queueStatus;
let statusHtml = '';
if (waiting_jobs > 0) {
statusHtml = `
<div class="bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-800 rounded-lg p-3 mb-4">
<div class="flex items-center">
<div class="flex-shrink-0 w-8 h-8 mr-3 flex items-center justify-center rounded-full bg-orange-100 dark:bg-orange-800">
<svg class="w-5 h-5 text-orange-600 dark:text-orange-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div class="flex-1">
<h4 class="text-sm font-semibold text-orange-800 dark:text-orange-200">
🔄 WARTESCHLANGEN-MODUS AKTIV
</h4>
<p class="text-xs text-orange-700 dark:text-orange-300 mt-1">
<span class="font-medium">${waiting_jobs}</span> Job${waiting_jobs === 1 ? '' : 's'} warten auf offline Drucker
</p>
<p class="text-xs text-orange-600 dark:text-orange-400 mt-1">
🟢 ${online_printers} von ${total_printers} Druckern online |
⏰ System prüft alle 2 Minuten automatisch
</p>
${queue_manager_running ?
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-200 mt-2"><span class="w-2 h-2 bg-green-500 rounded-full mr-1 animate-pulse"></span>Überwachung aktiv</span>' :
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-200 mt-2"><span class="w-2 h-2 bg-red-500 rounded-full mr-1"></span>Überwachung inaktiv</span>'
}
</div>
<button onclick="triggerQueueCheck()" class="ml-3 px-3 py-1 bg-orange-100 hover:bg-orange-200 text-orange-800 rounded-lg text-xs font-medium transition-colors">
Jetzt prüfen
</button>
</div>
</div>
`;
} else if (online_printers === total_printers && total_printers > 0) {
statusHtml = `
<div class="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-3 mb-4">
<div class="flex items-center">
<div class="flex-shrink-0 w-8 h-8 mr-3 flex items-center justify-center rounded-full bg-green-100 dark:bg-green-800">
<svg class="w-5 h-5 text-green-600 dark:text-green-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<div class="flex-1">
<h4 class="text-sm font-semibold text-green-800 dark:text-green-200">
✅ ALLE DRUCKER ONLINE
</h4>
<p class="text-xs text-green-700 dark:text-green-300 mt-1">
Alle ${total_printers} Drucker sind verfügbar - Jobs starten sofort
</p>
</div>
</div>
</div>
`;
}
statusContainer.innerHTML = statusHtml;
}
// Manuelle Queue-Überprüfung triggern (neue Funktion)
function triggerQueueCheck() {
const button = event.target;
const originalText = button.textContent;
button.disabled = true;
button.textContent = 'Prüfe...';
fetch('/api/queue/check-now', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.updated_jobs && data.updated_jobs.length > 0) {
showNotification(`🎉 ${data.updated_jobs.length} Job(s) aktiviert!`, 'success');
loadActiveJobs();
} else {
showNotification('✓ Queue-Check abgeschlossen - keine neuen Updates', 'info');
}
loadQueueStatus(); // Status neu laden
})
.catch(error => {
console.error('Fehler beim manuellen Queue-Check:', error);
showNotification('Fehler beim Queue-Check', 'error');
})
.finally(() => {
button.disabled = false;
button.textContent = originalText;
});
}
// Initialisierung des Formulars für neue Jobs // Initialisierung des Formulars für neue Jobs
function initNewJobForm() { function initNewJobForm() {
const form = document.getElementById('newJobForm'); const form = document.getElementById('newJobForm');