"Refactor job scheduler and printer monitor for improved performance (feat)"

This commit is contained in:
2025-05-29 21:30:44 +02:00
parent 962d395017
commit fe94a3b58c
4 changed files with 552 additions and 15 deletions

View File

@@ -3498,8 +3498,40 @@ def toggle_printer_power(printer_id):
return jsonify({"error": "Administratorrechte erforderlich"}), 403
try:
data = request.get_json()
state = data.get("state", True) # Standard: einschalten
# Robuste JSON-Datenverarbeitung
data = {}
try:
if request.is_json and request.get_json():
data = request.get_json()
elif request.form:
# Fallback für Form-Daten
data = request.form.to_dict()
except Exception as json_error:
printers_logger.warning(f"Fehler beim Parsen der JSON-Daten für Drucker {printer_id}: {str(json_error)}")
# Verwende Standard-Werte wenn JSON-Parsing fehlschlägt
data = {}
# Standard-Zustand ermitteln (Toggle-Verhalten)
db_session = get_db_session()
printer = db_session.query(Printer).get(printer_id)
if not printer:
db_session.close()
return jsonify({"error": "Drucker nicht gefunden"}), 404
# Aktuellen Status ermitteln für Toggle-Verhalten
current_status = getattr(printer, 'status', 'offline')
current_active = getattr(printer, 'active', False)
# Zielzustand bestimmen
if 'state' in data:
# Expliziter Zustand angegeben
state = bool(data.get("state", True))
else:
# Toggle-Verhalten: Umschalten basierend auf aktuellem Status
state = not (current_status == "available" and current_active)
db_session.close()
# Steckdose schalten
from utils.job_scheduler import toggle_plug
@@ -3507,20 +3539,31 @@ def toggle_printer_power(printer_id):
if success:
action = "eingeschaltet" if state else "ausgeschaltet"
printers_logger.info(f"Drucker {printer.name} (ID: {printer_id}) erfolgreich {action} von Admin {current_user.name}")
return jsonify({
"success": True,
"message": f"Drucker erfolgreich {action}",
"printer_id": printer_id,
"state": state
"printer_name": printer.name,
"state": state,
"action": action
})
else:
printers_logger.error(f"Fehler beim Schalten der Steckdose für Drucker {printer_id}")
return jsonify({
"error": "Fehler beim Schalten der Steckdose"
"success": False,
"error": "Fehler beim Schalten der Steckdose",
"printer_id": printer_id
}), 500
except Exception as e:
printers_logger.error(f"Fehler beim Schalten von Drucker {printer_id}: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
return jsonify({
"success": False,
"error": "Interner Serverfehler",
"details": str(e)
}), 500
@app.route("/api/admin/printers/<int:printer_id>/test-tapo", methods=["POST"])
@login_required
@@ -6293,6 +6336,7 @@ if __name__ == "__main__":
app_logger.error(f"❌ Fehler beim Shutdown: {str(e)}")
sys.exit(1)
# Signal-Handler registrieren (Windows-kompatibel)
if os.name == 'nt': # Windows
signal.signal(signal.SIGINT, signal_handler)
@@ -6696,3 +6740,270 @@ def cleanup_temp_files():
except Exception as e:
app_logger.error(f"Fehler beim Aufräumen temporärer Dateien: {str(e)}")
return jsonify({'error': f'Fehler beim Aufräumen: {str(e)}'}), 500
# ===== FEHLENDE DRUCKER-SPEZIFISCHE API-ENDPOINTS =====
@app.route("/api/printers/<int:printer_id>/jobs", methods=["GET"])
@login_required
def get_printer_jobs(printer_id):
"""Gibt alle Jobs für einen spezifischen Drucker zurück."""
try:
db_session = get_db_session()
# Prüfen ob Drucker existiert
printer = db_session.query(Printer).get(printer_id)
if not printer:
db_session.close()
return jsonify({"error": "Drucker nicht gefunden"}), 404
# Jobs für diesen Drucker abrufen
jobs = db_session.query(Job).filter(Job.printer_id == printer_id).order_by(Job.created_at.desc()).all()
jobs_data = []
for job in jobs:
job_data = {
"id": job.id,
"title": job.title,
"status": job.status,
"priority": job.priority,
"created_at": job.created_at.isoformat() if job.created_at else None,
"scheduled_time": job.scheduled_time.isoformat() if job.scheduled_time else None,
"started_at": job.started_at.isoformat() if job.started_at else None,
"finished_at": job.finished_at.isoformat() if job.finished_at else None,
"estimated_duration": job.estimated_duration,
"user_id": job.user_id,
"printer_id": job.printer_id,
"printer_name": printer.name
}
jobs_data.append(job_data)
db_session.close()
return jsonify({
"jobs": jobs_data,
"total": len(jobs_data),
"printer": {
"id": printer.id,
"name": printer.name,
"status": printer.status
}
})
except Exception as e:
printers_logger.error(f"Fehler beim Abrufen der Jobs für Drucker {printer_id}: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
@app.route("/api/printers/<int:printer_id>/stats", methods=["GET"])
@login_required
def get_printer_stats(printer_id):
"""Gibt Statistiken für einen spezifischen Drucker zurück."""
try:
db_session = get_db_session()
# Prüfen ob Drucker existiert
printer = db_session.query(Printer).get(printer_id)
if not printer:
db_session.close()
return jsonify({"error": "Drucker nicht gefunden"}), 404
# Statistiken berechnen
total_jobs = db_session.query(Job).filter(Job.printer_id == printer_id).count()
completed_jobs = db_session.query(Job).filter(
Job.printer_id == printer_id,
Job.status == "completed"
).count()
failed_jobs = db_session.query(Job).filter(
Job.printer_id == printer_id,
Job.status == "failed"
).count()
active_jobs = db_session.query(Job).filter(
Job.printer_id == printer_id,
Job.status.in_(["scheduled", "running"])
).count()
# Durchschnittliche Job-Dauer berechnen
avg_duration_result = db_session.query(func.avg(Job.estimated_duration)).filter(
Job.printer_id == printer_id,
Job.status == "completed",
Job.estimated_duration.isnot(None)
).scalar()
avg_duration = round(avg_duration_result, 2) if avg_duration_result else 0
# Erfolgsrate berechnen
success_rate = round((completed_jobs / total_jobs * 100), 2) if total_jobs > 0 else 0
# Letzte Aktivität
last_job = db_session.query(Job).filter(Job.printer_id == printer_id).order_by(Job.created_at.desc()).first()
last_activity = last_job.created_at.isoformat() if last_job and last_job.created_at else None
db_session.close()
stats_data = {
"printer": {
"id": printer.id,
"name": printer.name,
"status": printer.status,
"location": printer.location
},
"jobs": {
"total": total_jobs,
"completed": completed_jobs,
"failed": failed_jobs,
"active": active_jobs,
"success_rate": success_rate
},
"performance": {
"average_duration": avg_duration,
"last_activity": last_activity
},
"generated_at": datetime.now().isoformat()
}
return jsonify(stats_data)
except Exception as e:
printers_logger.error(f"Fehler beim Abrufen der Statistiken für Drucker {printer_id}: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
@app.route("/api/printers/<int:printer_id>/test", methods=["POST"])
@login_required
def test_printer_connection(printer_id):
"""Testet die Verbindung zu einem spezifischen Drucker."""
try:
db_session = get_db_session()
# Prüfen ob Drucker existiert
printer = db_session.query(Printer).get(printer_id)
if not printer:
db_session.close()
return jsonify({"error": "Drucker nicht gefunden"}), 404
# IP-Adresse für Test ermitteln
ip_to_test = printer.plug_ip if printer.plug_ip else getattr(printer, 'ip_address', None)
if not ip_to_test:
db_session.close()
return jsonify({
"success": False,
"error": "Keine IP-Adresse für Drucker konfiguriert",
"printer": {
"id": printer.id,
"name": printer.name
}
}), 400
# Verbindungstest durchführen
printers_logger.info(f"Teste Verbindung zu Drucker {printer.name} (ID: {printer_id}) auf IP {ip_to_test}")
status, active = check_printer_status(ip_to_test, timeout=10)
# Status in Datenbank aktualisieren
printer.status = "available" if status == "online" else "offline"
if hasattr(printer, 'active'):
printer.active = active
db_session.commit()
test_result = {
"success": status == "online",
"status": status,
"active": active,
"ip_address": ip_to_test,
"printer": {
"id": printer.id,
"name": printer.name,
"location": printer.location,
"model": printer.model
},
"test_time": datetime.now().isoformat(),
"message": f"Drucker ist {'online und erreichbar' if status == 'online' else 'offline oder nicht erreichbar'}"
}
db_session.close()
printers_logger.info(f"Verbindungstest für Drucker {printer.name}: {status}")
return jsonify(test_result)
except Exception as e:
printers_logger.error(f"Fehler beim Testen der Verbindung zu Drucker {printer_id}: {str(e)}")
return jsonify({
"success": False,
"error": "Interner Serverfehler beim Verbindungstest",
"details": str(e)
}), 500
# ===== ADMIN-SPEZIFISCHE DRUCKER-ENDPOINTS =====
@app.route("/api/admin/printers/create", methods=["POST"])
@login_required
@admin_required
def admin_create_printer_api():
"""Admin-Endpoint zum Erstellen neuer Drucker."""
try:
data = request.get_json()
if not data:
return jsonify({"error": "Keine Daten empfangen"}), 400
# Pflichtfelder prüfen
required_fields = ["name", "plug_ip"]
for field in required_fields:
if field not in data or not data[field]:
return jsonify({"error": f"Feld '{field}' ist erforderlich"}), 400
db_session = get_db_session()
# Prüfen, ob bereits ein Drucker mit diesem Namen existiert
existing_printer = db_session.query(Printer).filter(Printer.name == data["name"]).first()
if existing_printer:
db_session.close()
return jsonify({"error": "Ein Drucker mit diesem Namen existiert bereits"}), 400
# Neuen Drucker erstellen
new_printer = Printer(
name=data["name"],
model=data.get("model", ""),
location=data.get("location", ""),
mac_address=data.get("mac_address", ""),
plug_ip=data["plug_ip"],
status="offline",
active=True,
created_at=datetime.now()
)
db_session.add(new_printer)
db_session.commit()
# Sofortiger Status-Check
if new_printer.plug_ip:
status, active = check_printer_status(new_printer.plug_ip)
new_printer.status = "available" if status == "online" else "offline"
new_printer.active = active
db_session.commit()
printer_data = {
"id": new_printer.id,
"name": new_printer.name,
"model": new_printer.model,
"location": new_printer.location,
"mac_address": new_printer.mac_address,
"plug_ip": new_printer.plug_ip,
"status": new_printer.status,
"active": new_printer.active,
"created_at": new_printer.created_at.isoformat()
}
db_session.close()
printers_logger.info(f"Admin {current_user.name} hat Drucker '{new_printer.name}' erstellt")
return jsonify({
"success": True,
"message": "Drucker erfolgreich erstellt",
"printer": printer_data
}), 201
except Exception as e:
printers_logger.error(f"Fehler beim Erstellen eines Druckers durch Admin: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500