2025-06-04 10:03:22 +02:00

966 lines
36 KiB
Python

"""
Drucker-Blueprint für MYP Platform
Enthält alle Routen und Funktionen zur Druckerverwaltung, Statusüberwachung und Steuerung.
"""
import os
import json
import time
from datetime import datetime, timedelta
from flask import Blueprint, request, jsonify, current_app, abort, Response
from flask_login import login_required, current_user
from werkzeug.utils import secure_filename
from werkzeug.exceptions import NotFound, BadRequest
from sqlalchemy import func, desc, asc
from sqlalchemy.exc import SQLAlchemyError
from typing import Dict, List, Tuple, Any, Optional
from models import Printer, User, Job, get_db_session
from utils.logging_config import get_logger, measure_execution_time
from utils.permissions import require_permission, Permission, check_permission
from utils.printer_monitor import printer_monitor
from utils.drag_drop_system import drag_drop_manager
# Logger initialisieren
printers_logger = get_logger("printers")
# Blueprint erstellen
printers_blueprint = Blueprint("printers", __name__, url_prefix="/api/printers")
@printers_blueprint.route("/monitor/live-status", methods=["GET"])
@login_required
@measure_execution_time(logger=printers_logger, task_name="API-Live-Drucker-Status-Abfrage")
def get_live_printer_status():
"""
Liefert den aktuellen Live-Status aller Drucker.
Query-Parameter:
- use_cache: ob Cache verwendet werden soll (default: true)
Returns:
JSON mit Live-Status aller Drucker
"""
printers_logger.info(f"🔄 Live-Status-Abfrage von Benutzer {current_user.name} (ID: {current_user.id})")
# Parameter auslesen
use_cache_param = request.args.get("use_cache", "true").lower()
use_cache = use_cache_param == "true"
try:
# Live-Status über den PrinterMonitor abrufen
status_data = printer_monitor.get_live_printer_status(use_session_cache=use_cache)
# Zusammenfassung der Druckerstatus erstellen
summary = printer_monitor.get_printer_summary()
# Antwort mit Status und Zusammenfassung
response = {
"success": True,
"status": status_data,
"summary": summary,
"timestamp": datetime.now().isoformat(),
"cache_used": use_cache
}
printers_logger.info(f"✅ Live-Status-Abfrage erfolgreich: {len(status_data)} Drucker")
return jsonify(response)
except Exception as e:
printers_logger.error(f"❌ Fehler bei Live-Status-Abfrage: {str(e)}")
return jsonify({
"success": False,
"error": "Fehler bei Abfrage des Druckerstatus",
"message": str(e)
}), 500
@printers_blueprint.route("/control/<int:printer_id>/power", methods=["POST"])
@login_required
@require_permission(Permission.CONTROL_PRINTER) # Verwende die bereits vorhandene Berechtigung
@measure_execution_time(logger=printers_logger, task_name="API-Drucker-Stromversorgung-Steuerung")
def control_printer_power(printer_id):
"""
Steuert die Stromversorgung eines Druckers (ein-/ausschalten).
Args:
printer_id: ID des zu steuernden Druckers
JSON-Parameter:
- action: "on" oder "off"
Returns:
JSON mit Ergebnis der Steuerungsaktion
"""
printers_logger.info(f"🔌 Stromsteuerung für Drucker {printer_id} von Benutzer {current_user.name}")
# Parameter validieren
data = request.get_json()
if not data or "action" not in data:
return jsonify({
"success": False,
"error": "Parameter 'action' fehlt"
}), 400
action = data["action"]
if action not in ["on", "off"]:
return jsonify({
"success": False,
"error": "Ungültige Aktion. Erlaubt sind 'on' oder 'off'."
}), 400
try:
# Drucker aus Datenbank holen
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker mit ID {printer_id} nicht gefunden"
}), 404
# Prüfen, ob Drucker eine Steckdose konfiguriert hat
if not printer.plug_ip or not printer.plug_username or not printer.plug_password:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker {printer.name} hat keine Steckdose konfiguriert"
}), 400
# Steckdose steuern
from PyP100 import PyP110
try:
# TP-Link Tapo P110 Verbindung herstellen
p110 = PyP110.P110(printer.plug_ip, printer.plug_username, printer.plug_password)
p110.handshake() # Authentifizierung
p110.login() # Login
# Steckdose ein- oder ausschalten
if action == "on":
p110.turnOn()
success = True
message = "Steckdose erfolgreich eingeschaltet"
printer.status = "starting" # Status aktualisieren
else:
p110.turnOff()
success = True
message = "Steckdose erfolgreich ausgeschaltet"
printer.status = "offline" # Status aktualisieren
# Zeitpunkt der letzten Prüfung aktualisieren
printer.last_checked = datetime.now()
db_session.commit()
# Cache leeren, damit neue Status-Abfragen aktuell sind
printer_monitor.clear_all_caches()
printers_logger.info(f"{action.upper()}: Drucker {printer.name} erfolgreich {message}")
except Exception as e:
printers_logger.error(f"❌ Fehler bei Steckdosensteuerung für {printer.name}: {str(e)}")
db_session.close()
return jsonify({
"success": False,
"error": f"Fehler bei Steckdosensteuerung: {str(e)}"
}), 500
db_session.close()
return jsonify({
"success": True,
"message": message,
"printer_id": printer_id,
"printer_name": printer.name,
"action": action,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Allgemeiner Fehler bei Stromsteuerung: {str(e)}")
return jsonify({
"success": False,
"error": f"Allgemeiner Fehler: {str(e)}"
}), 500
@printers_blueprint.route("/test/socket/<int:printer_id>", methods=["GET"])
@login_required
@require_permission(Permission.ADMIN)
@measure_execution_time(logger=printers_logger, task_name="API-Steckdosen-Test-Status")
def test_socket_status(printer_id):
"""
Prüft den aktuellen Status einer Steckdose für Testzwecke (nur für Ausbilder/Administratoren).
Args:
printer_id: ID des Druckers dessen Steckdose getestet werden soll
Returns:
JSON mit detailliertem Status der Steckdose und Warnungen
"""
printers_logger.info(f"🔍 Steckdosen-Test-Status für Drucker {printer_id} von Admin {current_user.name}")
try:
# Drucker aus Datenbank holen
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker mit ID {printer_id} nicht gefunden"
}), 404
# Prüfen, ob Drucker eine Steckdose konfiguriert hat
if not printer.plug_ip or not printer.plug_username or not printer.plug_password:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker {printer.name} hat keine Steckdose konfiguriert",
"warning": "Steckdose kann nicht getestet werden - Konfiguration fehlt"
}), 400
# Prüfen, ob der Drucker gerade aktive Jobs hat
active_jobs = db_session.query(Job).filter(
Job.printer_id == printer_id,
Job.status.in_(["running", "printing", "active"])
).all()
db_session.close()
# Steckdosen-Status prüfen
from PyP100 import PyP110
socket_status = None
socket_info = None
error_message = None
try:
# TP-Link Tapo P110 Verbindung herstellen
p110 = PyP110.P110(printer.plug_ip, printer.plug_username, printer.plug_password)
p110.handshake() # Authentifizierung
p110.login() # Login
# Geräteinformationen abrufen
device_info = p110.getDeviceInfo()
socket_status = "online" if device_info["result"]["device_on"] else "offline"
# Energieverbrauch abrufen (falls verfügbar)
try:
energy_info = p110.getEnergyUsage()
current_power = energy_info.get("result", {}).get("current_power", 0)
except:
current_power = None
socket_info = {
"device_on": device_info["result"]["device_on"],
"signal_level": device_info["result"].get("signal_level", 0),
"current_power": current_power,
"device_id": device_info["result"].get("device_id", "Unbekannt"),
"model": device_info["result"].get("model", "Unbekannt"),
"hw_ver": device_info["result"].get("hw_ver", "Unbekannt"),
"fw_ver": device_info["result"].get("fw_ver", "Unbekannt")
}
except Exception as e:
printers_logger.warning(f"⚠️ Fehler bei Steckdosen-Status-Abfrage für {printer.name}: {str(e)}")
socket_status = "error"
error_message = str(e)
# Warnungen und Empfehlungen zusammenstellen
warnings = []
recommendations = []
risk_level = "low"
if active_jobs:
warnings.append(f"ACHTUNG: Drucker hat {len(active_jobs)} aktive(n) Job(s)!")
risk_level = "high"
recommendations.append("Warten Sie bis alle Jobs abgeschlossen sind bevor Sie die Steckdose ausschalten")
if socket_status == "online" and socket_info and socket_info.get("device_on"):
if socket_info.get("current_power", 0) > 10: # Mehr als 10W Verbrauch
warnings.append(f"Drucker verbraucht aktuell {socket_info['current_power']}W - vermutlich aktiv")
risk_level = "medium" if risk_level == "low" else risk_level
recommendations.append("Prüfen Sie den Druckerstatus bevor Sie die Steckdose ausschalten")
else:
recommendations.append("Drucker scheint im Standby-Modus zu sein - Test sollte sicher möglich sein")
if socket_status == "error":
warnings.append("Steckdose nicht erreichbar - Netzwerk oder Konfigurationsproblem")
recommendations.append("Prüfen Sie die Netzwerkverbindung und Steckdosen-Konfiguration")
if not warnings and socket_status == "offline":
recommendations.append("Steckdose ist ausgeschaltet - Test kann sicher durchgeführt werden")
printers_logger.info(f"✅ Steckdosen-Test-Status erfolgreich abgerufen für {printer.name}")
return jsonify({
"success": True,
"printer": {
"id": printer.id,
"name": printer.name,
"model": printer.model,
"location": printer.location,
"status": printer.status
},
"socket": {
"status": socket_status,
"info": socket_info,
"error": error_message,
"ip_address": printer.plug_ip
},
"safety": {
"risk_level": risk_level,
"warnings": warnings,
"recommendations": recommendations,
"active_jobs_count": len(active_jobs),
"safe_to_test": len(warnings) == 0
},
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Allgemeiner Fehler bei Steckdosen-Test-Status: {str(e)}")
return jsonify({
"success": False,
"error": f"Allgemeiner Fehler: {str(e)}"
}), 500
@printers_blueprint.route("/test/socket/<int:printer_id>/control", methods=["POST"])
@login_required
@require_permission(Permission.ADMIN)
@measure_execution_time(logger=printers_logger, task_name="API-Steckdosen-Test-Steuerung")
def test_socket_control(printer_id):
"""
Steuert eine Steckdose für Testzwecke (nur für Ausbilder/Administratoren).
Diese Funktion zeigt Warnungen an, erlaubt aber trotzdem die Steuerung für Tests.
Args:
printer_id: ID des Druckers dessen Steckdose gesteuert werden soll
JSON-Parameter:
- action: "on" oder "off"
- force: boolean - überschreibt Sicherheitswarnungen (default: false)
- test_reason: string - Grund für den Test (optional)
Returns:
JSON mit Ergebnis der Steuerungsaktion und Warnungen
"""
printers_logger.info(f"🧪 Steckdosen-Test-Steuerung für Drucker {printer_id} von Admin {current_user.name}")
# Parameter validieren
data = request.get_json()
if not data or "action" not in data:
return jsonify({
"success": False,
"error": "Parameter 'action' fehlt"
}), 400
action = data["action"]
if action not in ["on", "off"]:
return jsonify({
"success": False,
"error": "Ungültige Aktion. Erlaubt sind 'on' oder 'off'."
}), 400
force = data.get("force", False)
test_reason = data.get("test_reason", "Routinetest")
try:
# Drucker aus Datenbank holen
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker mit ID {printer_id} nicht gefunden"
}), 404
# Prüfen, ob Drucker eine Steckdose konfiguriert hat
if not printer.plug_ip or not printer.plug_username or not printer.plug_password:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker {printer.name} hat keine Steckdose konfiguriert"
}), 400
# Aktive Jobs prüfen
active_jobs = db_session.query(Job).filter(
Job.printer_id == printer_id,
Job.status.in_(["running", "printing", "active"])
).all()
# Sicherheitsprüfungen
warnings = []
should_block = False
if active_jobs and action == "off":
warnings.append(f"WARNUNG: {len(active_jobs)} aktive Job(s) würden abgebrochen!")
if not force:
should_block = True
if should_block:
db_session.close()
return jsonify({
"success": False,
"error": "Aktion blockiert aufgrund von Sicherheitsbedenken",
"warnings": warnings,
"hint": "Verwenden Sie 'force': true um die Aktion trotzdem auszuführen",
"requires_force": True
}), 409 # Conflict
# Steckdose steuern
from PyP100 import PyP110
try:
# TP-Link Tapo P110 Verbindung herstellen
p110 = PyP110.P110(printer.plug_ip, printer.plug_username, printer.plug_password)
p110.handshake() # Authentifizierung
p110.login() # Login
# Aktuellen Status vor der Änderung abrufen
device_info_before = p110.getDeviceInfo()
status_before = device_info_before["result"]["device_on"]
# Steckdose ein- oder ausschalten
if action == "on":
p110.turnOn()
success = True
message = "Steckdose für Test erfolgreich eingeschaltet"
new_printer_status = "starting"
else:
p110.turnOff()
success = True
message = "Steckdose für Test erfolgreich ausgeschaltet"
new_printer_status = "offline"
# Kurz warten und neuen Status prüfen
time.sleep(2)
device_info_after = p110.getDeviceInfo()
status_after = device_info_after["result"]["device_on"]
# Drucker-Status aktualisieren
printer.status = new_printer_status
printer.last_checked = datetime.now()
db_session.commit()
# Cache leeren, damit neue Status-Abfragen aktuell sind
printer_monitor.clear_all_caches()
# Test-Eintrag für Audit-Log
printers_logger.info(f"🧪 TEST DURCHGEFÜHRT: {action.upper()} für {printer.name} | "
f"Admin: {current_user.name} | Grund: {test_reason} | "
f"Force: {force} | Status: {status_before}{status_after}")
except Exception as e:
printers_logger.error(f"❌ Fehler bei Test-Steckdosensteuerung für {printer.name}: {str(e)}")
db_session.close()
return jsonify({
"success": False,
"error": f"Fehler bei Steckdosensteuerung: {str(e)}"
}), 500
db_session.close()
return jsonify({
"success": True,
"message": message,
"test_info": {
"admin": current_user.name,
"reason": test_reason,
"forced": force,
"status_before": status_before,
"status_after": status_after
},
"printer": {
"id": printer_id,
"name": printer.name,
"status": new_printer_status
},
"action": action,
"warnings": warnings,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Allgemeiner Fehler bei Test-Steckdosensteuerung: {str(e)}")
return jsonify({
"success": False,
"error": f"Allgemeiner Fehler: {str(e)}"
}), 500
@printers_blueprint.route("/test/all-sockets", methods=["GET"])
@login_required
@require_permission(Permission.ADMIN)
@measure_execution_time(logger=printers_logger, task_name="API-Alle-Steckdosen-Test-Status")
def test_all_sockets_status():
"""
Liefert den Test-Status aller konfigurierten Steckdosen (nur für Ausbilder/Administratoren).
Returns:
JSON mit Status aller Steckdosen und Gesamtübersicht
"""
printers_logger.info(f"🔍 Alle-Steckdosen-Test-Status von Admin {current_user.name}")
try:
# Alle Drucker mit Steckdosen-Konfiguration holen
db_session = get_db_session()
printers = db_session.query(Printer).filter(
Printer.plug_ip.isnot(None),
Printer.plug_username.isnot(None),
Printer.plug_password.isnot(None)
).all()
results = []
total_online = 0
total_offline = 0
total_error = 0
total_warnings = 0
from PyP100 import PyP110
for printer in printers:
# Aktive Jobs für diesen Drucker prüfen
active_jobs = db_session.query(Job).filter(
Job.printer_id == printer.id,
Job.status.in_(["running", "printing", "active"])
).count()
# Steckdosen-Status prüfen
socket_status = "unknown"
device_on = False
current_power = None
error_message = None
warnings = []
try:
p110 = PyP110.P110(printer.plug_ip, printer.plug_username, printer.plug_password)
p110.handshake()
p110.login()
device_info = p110.getDeviceInfo()
device_on = device_info["result"]["device_on"]
socket_status = "online" if device_on else "offline"
# Energieverbrauch abrufen
try:
energy_info = p110.getEnergyUsage()
current_power = energy_info.get("result", {}).get("current_power", 0)
except:
current_power = None
# Warnungen generieren
if active_jobs > 0:
warnings.append(f"{active_jobs} aktive Job(s)")
if device_on and current_power and current_power > 10:
warnings.append(f"Hoher Verbrauch: {current_power}W")
except Exception as e:
socket_status = "error"
error_message = str(e)
warnings.append(f"Verbindungsfehler: {str(e)[:50]}")
# Statistiken aktualisieren
if socket_status == "online":
total_online += 1
elif socket_status == "offline":
total_offline += 1
else:
total_error += 1
if warnings:
total_warnings += 1
results.append({
"printer": {
"id": printer.id,
"name": printer.name,
"model": printer.model,
"location": printer.location
},
"socket": {
"status": socket_status,
"device_on": device_on,
"current_power": current_power,
"ip_address": printer.plug_ip,
"error": error_message
},
"warnings": warnings,
"active_jobs": active_jobs,
"safe_to_test": len(warnings) == 0
})
db_session.close()
# Gesamtübersicht erstellen
summary = {
"total_sockets": len(results),
"online": total_online,
"offline": total_offline,
"error": total_error,
"with_warnings": total_warnings,
"safe_to_test": len(results) - total_warnings
}
printers_logger.info(f"✅ Alle-Steckdosen-Status erfolgreich abgerufen: {len(results)} Steckdosen")
return jsonify({
"success": True,
"sockets": results,
"summary": summary,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Fehler bei Alle-Steckdosen-Test-Status: {str(e)}")
return jsonify({
"success": False,
"error": f"Allgemeiner Fehler: {str(e)}"
}), 500
# =============================================================================
# DRAG & DROP API - JOB-REIHENFOLGE-MANAGEMENT
# =============================================================================
@printers_blueprint.route("/<int:printer_id>/jobs/order", methods=["GET"])
@login_required
@measure_execution_time(logger=printers_logger, task_name="API-Job-Reihenfolge-Abfrage")
def get_job_order(printer_id):
"""
Holt die aktuelle Job-Reihenfolge für einen Drucker.
Args:
printer_id: ID des Druckers
Returns:
JSON mit Jobs in der korrekten Reihenfolge
"""
printers_logger.info(f"📋 Job-Reihenfolge-Abfrage für Drucker {printer_id} von Benutzer {current_user.name}")
try:
# Drucker existiert prüfen
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker mit ID {printer_id} nicht gefunden"
}), 404
db_session.close()
# Job-Reihenfolge und Details holen
ordered_jobs = drag_drop_manager.get_ordered_jobs_for_printer(printer_id)
job_order_ids = drag_drop_manager.get_job_order(printer_id)
# Job-Details für Response aufbereiten
jobs_data = []
for job in ordered_jobs:
jobs_data.append({
"id": job.id,
"name": job.name,
"description": job.description,
"user_name": job.user.name if job.user else "Unbekannt",
"user_id": job.user_id,
"duration_minutes": job.duration_minutes,
"created_at": job.created_at.isoformat() if job.created_at else None,
"start_at": job.start_at.isoformat() if job.start_at else None,
"status": job.status,
"file_path": job.file_path
})
printers_logger.info(f"✅ Job-Reihenfolge erfolgreich abgerufen: {len(jobs_data)} Jobs für Drucker {printer.name}")
return jsonify({
"success": True,
"printer": {
"id": printer.id,
"name": printer.name,
"model": printer.model,
"location": printer.location
},
"jobs": jobs_data,
"job_order": job_order_ids,
"total_jobs": len(jobs_data),
"total_duration_minutes": sum(job.duration_minutes for job in ordered_jobs),
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Fehler bei Job-Reihenfolge-Abfrage für Drucker {printer_id}: {str(e)}")
return jsonify({
"success": False,
"error": f"Fehler beim Laden der Job-Reihenfolge: {str(e)}"
}), 500
@printers_blueprint.route("/<int:printer_id>/jobs/order", methods=["POST"])
@login_required
@require_permission(Permission.APPROVE_JOBS) # Nur Benutzer mit Job-Genehmigungsrechten können Reihenfolge ändern
@measure_execution_time(logger=printers_logger, task_name="API-Job-Reihenfolge-Update")
def update_job_order(printer_id):
"""
Aktualisiert die Job-Reihenfolge für einen Drucker per Drag & Drop.
Args:
printer_id: ID des Druckers
JSON-Parameter:
- job_ids: Liste der Job-IDs in der gewünschten Reihenfolge
Returns:
JSON mit Bestätigung der Aktualisierung
"""
printers_logger.info(f"🔄 Job-Reihenfolge-Update für Drucker {printer_id} von Benutzer {current_user.name}")
# Parameter validieren
data = request.get_json()
if not data or "job_ids" not in data:
return jsonify({
"success": False,
"error": "Parameter 'job_ids' fehlt"
}), 400
job_ids = data["job_ids"]
if not isinstance(job_ids, list):
return jsonify({
"success": False,
"error": "Parameter 'job_ids' muss eine Liste sein"
}), 400
if not all(isinstance(job_id, int) for job_id in job_ids):
return jsonify({
"success": False,
"error": "Alle Job-IDs müssen Zahlen sein"
}), 400
try:
# Drucker existiert prüfen
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker mit ID {printer_id} nicht gefunden"
}), 404
# Validierung: Alle Jobs gehören zum Drucker und sind editierbar
valid_jobs = db_session.query(Job).filter(
Job.id.in_(job_ids),
Job.printer_id == printer_id,
Job.status.in_(['scheduled', 'paused'])
).all()
db_session.close()
if len(valid_jobs) != len(job_ids):
invalid_ids = set(job_ids) - {job.id for job in valid_jobs}
return jsonify({
"success": False,
"error": f"Ungültige oder nicht editierbare Job-IDs: {list(invalid_ids)}"
}), 400
# Berechtigung prüfen: Benutzer kann nur eigene Jobs oder als Admin alle verschieben
if not current_user.is_admin:
user_job_ids = {job.id for job in valid_jobs if job.user_id == current_user.id}
if user_job_ids != set(job_ids):
unauthorized_ids = set(job_ids) - user_job_ids
return jsonify({
"success": False,
"error": f"Keine Berechtigung für Jobs: {list(unauthorized_ids)}"
}), 403
# Job-Reihenfolge aktualisieren
success = drag_drop_manager.update_job_order(printer_id, job_ids)
if success:
# Neue Reihenfolge zur Bestätigung laden
updated_order = drag_drop_manager.get_job_order(printer_id)
printers_logger.info(f"✅ Job-Reihenfolge erfolgreich aktualisiert für Drucker {printer.name}")
printers_logger.info(f" Neue Reihenfolge: {job_ids}")
printers_logger.info(f" Benutzer: {current_user.name} (ID: {current_user.id})")
return jsonify({
"success": True,
"message": "Job-Reihenfolge erfolgreich aktualisiert",
"printer": {
"id": printer.id,
"name": printer.name
},
"old_order": job_ids, # Eingabe des Benutzers
"new_order": updated_order, # Bestätigung aus Datenbank
"total_jobs": len(job_ids),
"updated_by": {
"id": current_user.id,
"name": current_user.name
},
"timestamp": datetime.now().isoformat()
})
else:
return jsonify({
"success": False,
"error": "Fehler beim Speichern der Job-Reihenfolge"
}), 500
except Exception as e:
printers_logger.error(f"❌ Fehler bei Job-Reihenfolge-Update für Drucker {printer_id}: {str(e)}")
return jsonify({
"success": False,
"error": f"Unerwarteter Fehler: {str(e)}"
}), 500
@printers_blueprint.route("/<int:printer_id>/jobs/summary", methods=["GET"])
@login_required
@measure_execution_time(logger=printers_logger, task_name="API-Drucker-Job-Zusammenfassung")
def get_printer_job_summary(printer_id):
"""
Erstellt eine detaillierte Zusammenfassung der Jobs für einen Drucker.
Args:
printer_id: ID des Druckers
Returns:
JSON mit Zusammenfassung, Statistiken und Zeitschätzungen
"""
printers_logger.info(f"📊 Drucker-Job-Zusammenfassung für Drucker {printer_id} von Benutzer {current_user.name}")
try:
# Drucker existiert prüfen
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
return jsonify({
"success": False,
"error": f"Drucker mit ID {printer_id} nicht gefunden"
}), 404
db_session.close()
# Zusammenfassung über Drag-Drop-Manager erstellen
summary = drag_drop_manager.get_printer_summary(printer_id)
printers_logger.info(f"✅ Drucker-Job-Zusammenfassung erfolgreich erstellt für {printer.name}")
return jsonify({
"success": True,
"printer": {
"id": printer.id,
"name": printer.name,
"model": printer.model,
"location": printer.location,
"status": printer.status
},
"summary": summary,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Fehler bei Drucker-Job-Zusammenfassung für Drucker {printer_id}: {str(e)}")
return jsonify({
"success": False,
"error": f"Fehler beim Erstellen der Zusammenfassung: {str(e)}"
}), 500
@printers_blueprint.route("/jobs/cleanup-orders", methods=["POST"])
@login_required
@require_permission(Permission.ADMIN)
@measure_execution_time(logger=printers_logger, task_name="API-Job-Reihenfolgen-Bereinigung")
def cleanup_job_orders():
"""
Bereinigt ungültige Job-Reihenfolgen (nur für Administratoren).
Entfernt Einträge für abgeschlossene oder gelöschte Jobs.
Returns:
JSON mit Bereinigungsergebnis
"""
printers_logger.info(f"🧹 Job-Reihenfolgen-Bereinigung von Admin {current_user.name}")
try:
# Bereinigung durchführen
drag_drop_manager.cleanup_invalid_orders()
printers_logger.info(f"✅ Job-Reihenfolgen-Bereinigung erfolgreich abgeschlossen")
return jsonify({
"success": True,
"message": "Job-Reihenfolgen erfolgreich bereinigt",
"admin": {
"id": current_user.id,
"name": current_user.name
},
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Fehler bei Job-Reihenfolgen-Bereinigung: {str(e)}")
return jsonify({
"success": False,
"error": f"Fehler bei der Bereinigung: {str(e)}"
}), 500
@printers_blueprint.route("/drag-drop/config", methods=["GET"])
@login_required
def get_drag_drop_config():
"""
Liefert die Konfiguration für das Drag & Drop System.
Returns:
JSON mit Drag & Drop Konfiguration und JavaScript/CSS
"""
printers_logger.info(f"⚙️ Drag-Drop-Konfiguration abgerufen von Benutzer {current_user.name}")
try:
from utils.drag_drop_system import get_drag_drop_javascript, get_drag_drop_css
# Benutzerberechtigungen prüfen
can_reorder_jobs = check_permission(current_user, Permission.APPROVE_JOBS)
can_upload_files = check_permission(current_user, Permission.CREATE_JOB)
config = {
"permissions": {
"can_reorder_jobs": can_reorder_jobs,
"can_upload_files": can_upload_files,
"is_admin": current_user.is_admin
},
"settings": {
"max_file_size": 50 * 1024 * 1024, # 50MB
"accepted_file_types": ["gcode", "stl", "3mf", "obj"],
"auto_upload": False,
"show_preview": True,
"enable_progress_tracking": True
},
"endpoints": {
"get_job_order": f"/api/printers/{{printer_id}}/jobs/order",
"update_job_order": f"/api/printers/{{printer_id}}/jobs/order",
"get_summary": f"/api/printers/{{printer_id}}/jobs/summary"
},
"javascript": get_drag_drop_javascript(),
"css": get_drag_drop_css()
}
return jsonify({
"success": True,
"config": config,
"user": {
"id": current_user.id,
"name": current_user.name,
"role": current_user.role
},
"timestamp": datetime.now().isoformat()
})
except Exception as e:
printers_logger.error(f"❌ Fehler bei Drag-Drop-Konfiguration: {str(e)}")
return jsonify({
"success": False,
"error": f"Fehler beim Laden der Konfiguration: {str(e)}"
}), 500
# =============================================================================
# ENDE DRAG & DROP API
# =============================================================================