🎉 Refactor & Update Backend Files, Documentation 📚
This commit is contained in:
@ -96,6 +96,12 @@ def guest_request_form():
|
||||
)
|
||||
|
||||
db_session.add(guest_request)
|
||||
db_session.flush() # Um ID zu erhalten
|
||||
|
||||
# OTP-Code sofort generieren für Status-Abfrage
|
||||
otp_code = guest_request.generate_otp()
|
||||
guest_request.otp_expires_at = datetime.now() + timedelta(hours=72) # 72h gültig
|
||||
|
||||
db_session.commit()
|
||||
|
||||
# Benachrichtigung für Genehmiger erstellen
|
||||
@ -109,10 +115,10 @@ def guest_request_form():
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"Neue Gastanfrage erstellt: ID {guest_request.id}, Name: {name}")
|
||||
logger.info(f"Neue Gastanfrage erstellt: ID {guest_request.id}, Name: {name}, OTP generiert")
|
||||
flash("Ihr Antrag wurde erfolgreich eingereicht!", "success")
|
||||
|
||||
# Weiterleitung zur Status-Seite
|
||||
# Weiterleitung zur Status-Seite mit OTP-Code-Info
|
||||
return redirect(url_for('guest.guest_request_status', request_id=guest_request.id))
|
||||
|
||||
except Exception as e:
|
||||
@ -295,6 +301,12 @@ def api_create_guest_request():
|
||||
)
|
||||
|
||||
db_session.add(guest_request)
|
||||
db_session.flush() # Um ID zu erhalten
|
||||
|
||||
# OTP-Code sofort generieren für Status-Abfrage
|
||||
otp_code = guest_request.generate_otp()
|
||||
guest_request.otp_expires_at = datetime.now() + timedelta(hours=72) # 72h gültig
|
||||
|
||||
db_session.commit()
|
||||
|
||||
# Benachrichtigung für Genehmiger erstellen
|
||||
@ -308,12 +320,14 @@ def api_create_guest_request():
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"Neue Gastanfrage erstellt: ID {guest_request.id}, Name: {name}")
|
||||
logger.info(f"Neue Gastanfrage erstellt: ID {guest_request.id}, Name: {name}, OTP generiert")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"request_id": guest_request.id,
|
||||
"status": guest_request.status,
|
||||
"otp_code": otp_code, # Code wird nur bei Erstellung zurückgegeben
|
||||
"status_check_url": url_for('guest.guest_status_check_page', _external=True),
|
||||
"redirect_url": url_for('guest.guest_request_status', request_id=guest_request.id)
|
||||
})
|
||||
|
||||
@ -852,4 +866,126 @@ def api_deny_request(request_id):
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Ablehnen der Gastanfrage: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
|
||||
|
||||
@guest_blueprint.route('/api/guest/status', methods=['POST'])
|
||||
def api_guest_status_by_otp():
|
||||
"""
|
||||
Öffentliche Route für Gäste um ihren Auftragsstatus mit OTP-Code zu prüfen.
|
||||
Keine Authentifizierung erforderlich.
|
||||
"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Keine Daten empfangen'
|
||||
}), 400
|
||||
|
||||
otp_code = data.get('otp_code', '').strip()
|
||||
email = data.get('email', '').strip() # Optional für zusätzliche Verifikation
|
||||
|
||||
if not otp_code:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'OTP-Code ist erforderlich'
|
||||
}), 400
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
# Alle Gastaufträge mit OTP-Codes finden
|
||||
guest_requests = db_session.query(GuestRequest).filter(
|
||||
GuestRequest.otp_code.isnot(None)
|
||||
).all()
|
||||
|
||||
found_request = None
|
||||
for request_obj in guest_requests:
|
||||
if request_obj.verify_otp(otp_code):
|
||||
# Zusätzliche E-Mail-Verifikation falls angegeben
|
||||
if email and request_obj.email and request_obj.email.lower() != email.lower():
|
||||
continue
|
||||
found_request = request_obj
|
||||
break
|
||||
|
||||
if not found_request:
|
||||
logger.warning(f"Ungültiger OTP-Code für Gast-Status-Abfrage: {otp_code[:4]}****")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Ungültiger Code oder E-Mail-Adresse'
|
||||
}), 404
|
||||
|
||||
# Status-Informationen für den Gast zusammenstellen
|
||||
status_info = {
|
||||
'id': found_request.id,
|
||||
'name': found_request.name,
|
||||
'file_name': found_request.file_name,
|
||||
'status': found_request.status,
|
||||
'created_at': found_request.created_at.isoformat() if found_request.created_at else None,
|
||||
'updated_at': found_request.updated_at.isoformat() if found_request.updated_at else None,
|
||||
'duration_min': found_request.duration_min,
|
||||
'reason': found_request.reason
|
||||
}
|
||||
|
||||
# Status-spezifische Informationen hinzufügen
|
||||
if found_request.status == 'approved':
|
||||
status_info.update({
|
||||
'approved_at': found_request.approved_at.isoformat() if found_request.approved_at else None,
|
||||
'approval_notes': found_request.approval_notes,
|
||||
'message': 'Ihr Auftrag wurde genehmigt! Sie können mit dem Drucken beginnen.',
|
||||
'can_start_job': found_request.otp_used_at is None # Noch nicht verwendet
|
||||
})
|
||||
|
||||
# Job-Informationen hinzufügen falls vorhanden
|
||||
if found_request.job_id:
|
||||
job = db_session.query(Job).options(joinedload(Job.printer)).filter_by(id=found_request.job_id).first()
|
||||
if job:
|
||||
status_info['job'] = {
|
||||
'id': job.id,
|
||||
'name': job.name,
|
||||
'status': job.status,
|
||||
'start_at': job.start_at.isoformat() if job.start_at else None,
|
||||
'end_at': job.end_at.isoformat() if job.end_at else None,
|
||||
'printer_name': job.printer.name if job.printer else None
|
||||
}
|
||||
|
||||
elif found_request.status == 'rejected':
|
||||
status_info.update({
|
||||
'rejected_at': found_request.rejected_at.isoformat() if found_request.rejected_at else None,
|
||||
'rejection_reason': found_request.rejection_reason,
|
||||
'message': 'Ihr Auftrag wurde leider abgelehnt.'
|
||||
})
|
||||
|
||||
elif found_request.status == 'pending':
|
||||
# Berechne wie lange der Auftrag schon wartet
|
||||
if found_request.created_at:
|
||||
waiting_time = datetime.now() - found_request.created_at
|
||||
hours_waiting = int(waiting_time.total_seconds() / 3600)
|
||||
status_info.update({
|
||||
'hours_waiting': hours_waiting,
|
||||
'message': f'Ihr Auftrag wird bearbeitet. Wartezeit: {hours_waiting} Stunden.'
|
||||
})
|
||||
else:
|
||||
status_info['message'] = 'Ihr Auftrag wird bearbeitet.'
|
||||
|
||||
# OTP als verwendet markieren (da erfolgreich abgefragt)
|
||||
db_session.commit()
|
||||
|
||||
logger.info(f"Gast-Status-Abfrage erfolgreich für Request {found_request.id}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'request': status_info
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei Gast-Status-Abfrage: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Fehler beim Abrufen des Status'
|
||||
}), 500
|
||||
|
||||
@guest_blueprint.route('/status-check')
|
||||
def guest_status_check_page():
|
||||
"""
|
||||
Öffentliche Seite für Gäste um ihren Auftragsstatus zu prüfen.
|
||||
"""
|
||||
return render_template('guest_status_check.html')
|
@ -19,6 +19,7 @@ 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")
|
||||
@ -613,4 +614,353 @@ def test_all_sockets_status():
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"Allgemeiner Fehler: {str(e)}"
|
||||
}), 500
|
||||
}), 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
|
||||
# =============================================================================
|
Reference in New Issue
Block a user