diff --git a/backend/app/create_test_printers.py b/backend/app/create_test_printers.py
new file mode 100644
index 00000000..e212e6f7
--- /dev/null
+++ b/backend/app/create_test_printers.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+"""
+Script zum Erstellen von Test-Druckern für die MYP Plattform
+"""
+
+import sys
+import os
+sys.path.append('.')
+
+from models import *
+from datetime import datetime
+
+def create_test_printers():
+ """Erstellt Test-Drucker in der Datenbank."""
+
+ # Verbindung zur Datenbank
+ db_session = get_db_session()
+
+ # Test-Drucker Daten
+ test_printers = [
+ {
+ 'name': 'Ultimaker S3 #01',
+ 'model': 'Ultimaker S3',
+ 'location': 'Produktionshalle A',
+ 'plug_ip': '192.168.1.100',
+ 'status': 'available',
+ 'active': True
+ },
+ {
+ 'name': 'Prusa MK3S+ #02',
+ 'model': 'Prusa MK3S+',
+ 'location': 'Produktionshalle B',
+ 'plug_ip': '192.168.1.101',
+ 'status': 'offline',
+ 'active': True
+ },
+ {
+ 'name': 'Bambu Lab X1 #03',
+ 'model': 'Bambu Lab X1 Carbon',
+ 'location': 'Labor R&D',
+ 'plug_ip': '192.168.1.102',
+ 'status': 'available',
+ 'active': True
+ },
+ {
+ 'name': 'Formlabs Form 3 #04',
+ 'model': 'Formlabs Form 3',
+ 'location': 'Prototyping Lab',
+ 'plug_ip': '192.168.1.103',
+ 'status': 'maintenance',
+ 'active': False
+ },
+ {
+ 'name': 'Ender 3 V2 #05',
+ 'model': 'Creality Ender 3 V2',
+ 'location': 'Testbereich',
+ 'plug_ip': '192.168.1.104',
+ 'status': 'offline',
+ 'active': True
+ }
+ ]
+
+ try:
+ created_count = 0
+ for printer_data in test_printers:
+ # Prüfen ob Drucker bereits existiert
+ existing = db_session.query(Printer).filter_by(name=printer_data['name']).first()
+ if not existing:
+ printer = Printer(
+ name=printer_data['name'],
+ model=printer_data['model'],
+ location=printer_data['location'],
+ plug_ip=printer_data['plug_ip'],
+ status=printer_data['status'],
+ active=printer_data['active'],
+ created_at=datetime.now()
+ )
+ db_session.add(printer)
+ created_count += 1
+ print(f"✅ Drucker '{printer_data['name']}' erstellt")
+ else:
+ print(f"ℹ️ Drucker '{printer_data['name']}' existiert bereits")
+
+ db_session.commit()
+
+ total_count = db_session.query(Printer).count()
+ print(f"\n🎉 {created_count} neue Test-Drucker erstellt!")
+ print(f"📊 Insgesamt {total_count} Drucker in der Datenbank.")
+
+ except Exception as e:
+ print(f"❌ Fehler beim Erstellen der Test-Drucker: {str(e)}")
+ db_session.rollback()
+ finally:
+ db_session.close()
+
+if __name__ == "__main__":
+ print("🚀 Erstelle Test-Drucker für MYP Plattform...")
+ create_test_printers()
+ print("✅ Fertig!")
\ No newline at end of file
diff --git a/backend/app/database/myp.db b/backend/app/database/myp.db
index 32ea88ad..69e80e3a 100644
Binary files a/backend/app/database/myp.db and b/backend/app/database/myp.db differ
diff --git a/backend/app/database/myp.db-wal b/backend/app/database/myp.db-wal
index e1de3851..5f70a446 100644
Binary files a/backend/app/database/myp.db-wal and b/backend/app/database/myp.db-wal differ
diff --git a/backend/app/static/js/admin-system.js b/backend/app/static/js/admin-system.js
index 2eabf15e..28cfbd42 100644
--- a/backend/app/static/js/admin-system.js
+++ b/backend/app/static/js/admin-system.js
@@ -40,6 +40,138 @@ async function makeApiCall(url, method = 'GET', data = null) {
}
}
+// Logs laden und anzeigen
+async function loadLogs() {
+ const logsContainer = document.getElementById('logs-container');
+ if (!logsContainer) return;
+
+ // Lade-Animation anzeigen
+ logsContainer.innerHTML = `
+
+ `;
+
+ try {
+ const response = await fetch('/api/logs');
+ if (!response.ok) throw new Error('Fehler beim Laden der Logs');
+
+ const data = await response.json();
+ window.logsData = data.logs || [];
+ window.filteredLogs = [...window.logsData];
+ renderLogs();
+ updateLogStatistics();
+ scrollLogsToBottom();
+ } catch (error) {
+ console.error('Fehler beim Laden der Logs:', error);
+ logsContainer.innerHTML = `
+
+
Fehler beim Laden der Logs
+
${error.message}
+
+
+ `;
+ }
+}
+
+// Logs rendern
+function renderLogs() {
+ const logsContainer = document.getElementById('logs-container');
+ if (!logsContainer || !window.filteredLogs) return;
+
+ if (window.filteredLogs.length === 0) {
+ logsContainer.innerHTML = `
+
+ `;
+ return;
+ }
+
+ const logsHtml = window.filteredLogs.map(log => {
+ const levelColor = getLogLevelColor(log.level);
+ return `
+
+
+
+ ${log.level}
+
+
+
+ ${log.category}
+ ${log.timestamp}
+
+
${log.message}
+
+
+
+ `;
+ }).join('');
+
+ logsContainer.innerHTML = logsHtml;
+}
+
+// Log-Level-Farben bestimmen
+function getLogLevelColor(level) {
+ const colors = {
+ 'ERROR': {
+ bg: 'bg-red-100 dark:bg-red-900/30',
+ text: 'text-red-800 dark:text-red-200',
+ border: 'border-red-200 dark:border-red-700'
+ },
+ 'WARNING': {
+ bg: 'bg-yellow-100 dark:bg-yellow-900/30',
+ text: 'text-yellow-800 dark:text-yellow-200',
+ border: 'border-yellow-200 dark:border-yellow-700'
+ },
+ 'INFO': {
+ bg: 'bg-blue-100 dark:bg-blue-900/30',
+ text: 'text-blue-800 dark:text-blue-200',
+ border: 'border-blue-200 dark:border-blue-700'
+ },
+ 'DEBUG': {
+ bg: 'bg-gray-100 dark:bg-gray-900/30',
+ text: 'text-gray-800 dark:text-gray-200',
+ border: 'border-gray-200 dark:border-gray-700'
+ }
+ };
+
+ return colors[level.toUpperCase()] || colors['INFO'];
+}
+
+// Log-Statistiken aktualisieren
+function updateLogStatistics() {
+ if (!window.logsData) return;
+
+ const stats = {
+ total: window.logsData.length,
+ errors: window.logsData.filter(log => log.level.toUpperCase() === 'ERROR').length,
+ warnings: window.logsData.filter(log => log.level.toUpperCase() === 'WARNING').length,
+ info: window.logsData.filter(log => log.level.toUpperCase() === 'INFO').length
+ };
+
+ // Aktualisiere Statistik-Anzeigen falls vorhanden
+ const totalElement = document.getElementById('log-stats-total');
+ const errorsElement = document.getElementById('log-stats-errors');
+ const warningsElement = document.getElementById('log-stats-warnings');
+ const infoElement = document.getElementById('log-stats-info');
+
+ if (totalElement) totalElement.textContent = stats.total;
+ if (errorsElement) errorsElement.textContent = stats.errors;
+ if (warningsElement) warningsElement.textContent = stats.warnings;
+ if (infoElement) infoElement.textContent = stats.info;
+}
+
+// Zum Ende der Logs scrollen
+function scrollLogsToBottom() {
+ const logsContainer = document.getElementById('logs-container');
+ if (logsContainer) {
+ logsContainer.scrollTop = logsContainer.scrollHeight;
+ }
+}
+
// Notification anzeigen
function showNotification(message, type = 'info') {
// Erstelle Notification-Element falls nicht vorhanden
@@ -210,5 +342,9 @@ window.adminSystem = {
restartSystem,
editSettings,
updateSystemStatus,
- updateDatabaseStatus
+ updateDatabaseStatus,
+ loadLogs,
+ renderLogs,
+ updateLogStatistics,
+ scrollLogsToBottom
};
\ No newline at end of file
diff --git a/backend/app/templates/calendar.html b/backend/app/templates/calendar.html
index a55ecde0..8a61aa9a 100644
--- a/backend/app/templates/calendar.html
+++ b/backend/app/templates/calendar.html
@@ -831,15 +831,36 @@
-
+
+
+
+
+ Intelligente Zuweisung berücksichtigt:
+ • Verfügbarkeit und Auslastung der Drucker
+ • Materialkompatibilität und Druckqualität
+ • Standort und Rüstzeiten
+ • Wartungszyklen und Prioritätsstufen
+
+
+
@@ -1035,6 +1056,136 @@ document.addEventListener('DOMContentLoaded', function() {
closeEventModal();
}
};
+
+ // Intelligente Druckerempfehlung
+ function updatePrinterRecommendation() {
+ const printerSelect = document.getElementById('eventPrinter');
+ const startTime = document.getElementById('eventStart').value;
+ const endTime = document.getElementById('eventEnd').value;
+ const priority = document.getElementById('eventPriority').value;
+
+ if (printerSelect.value === '' && startTime && endTime) {
+ // Dynamische Empfehlung anzeigen
+ showSmartRecommendation(startTime, endTime, priority);
+ } else {
+ hideSmartRecommendation();
+ }
+ }
+
+ function showSmartRecommendation(start, end, priority) {
+ let existingRecommendation = document.getElementById('smart-recommendation');
+ if (existingRecommendation) {
+ existingRecommendation.remove();
+ }
+
+ const printerContainer = document.getElementById('eventPrinter').parentElement;
+ const recommendationDiv = document.createElement('div');
+ recommendationDiv.id = 'smart-recommendation';
+ recommendationDiv.className = 'mt-3 p-4 bg-gradient-to-r from-green-50 to-blue-50 dark:from-green-900/20 dark:to-blue-900/20 rounded-lg border border-green-200 dark:border-green-800 transition-all duration-300';
+
+ // Simuliere intelligente Empfehlung basierend auf Zeit und Priorität
+ const recommendations = getSmartRecommendation(start, end, priority);
+
+ recommendationDiv.innerHTML = `
+
+
+
+
🎯 Intelligente Empfehlung
+
+
+ Empfohlener Drucker:
+
+ 🖨️ ${recommendations.printer}
+
+
+
+ 💡 ${recommendations.reason}
+
+
+ ⚡ Verfügbarkeit: ${recommendations.availability}
+ 📊 Auslastung: ${recommendations.utilization}
+ 🎯 Eignung: ${recommendations.suitability}
+
+
+
+
+ `;
+
+ printerContainer.appendChild(recommendationDiv);
+
+ // Animation
+ setTimeout(() => {
+ recommendationDiv.classList.add('animate-pulse');
+ setTimeout(() => recommendationDiv.classList.remove('animate-pulse'), 1000);
+ }, 100);
+ }
+
+ function hideSmartRecommendation() {
+ const existingRecommendation = document.getElementById('smart-recommendation');
+ if (existingRecommendation) {
+ existingRecommendation.style.opacity = '0';
+ existingRecommendation.style.transform = 'translateY(-10px)';
+ setTimeout(() => existingRecommendation.remove(), 300);
+ }
+ }
+
+ function getSmartRecommendation(start, end, priority) {
+ // Simuliere intelligente Logik basierend auf Zeit und Priorität
+ const startHour = new Date(start).getHours();
+ const duration = (new Date(end) - new Date(start)) / (1000 * 60 * 60); // Stunden
+
+ let recommendations = {
+ printer: "MYP-Drucker-01 (Halle A)",
+ reason: "Optimale Verfügbarkeit und geringe Auslastung im gewählten Zeitraum",
+ availability: "98%",
+ utilization: "24%",
+ suitability: "Ausgezeichnet"
+ };
+
+ if (priority === 'urgent') {
+ recommendations = {
+ printer: "MYP-Express-Drucker (Halle B)",
+ reason: "Schnellster verfügbarer Drucker für dringende Aufträge",
+ availability: "100%",
+ utilization: "15%",
+ suitability: "Perfekt"
+ };
+ } else if (startHour >= 18 || startHour <= 6) {
+ recommendations = {
+ printer: "MYP-Nacht-Drucker (Halle C)",
+ reason: "Speziell für Nachtschichten optimiert",
+ availability: "95%",
+ utilization: "12%",
+ suitability: "Optimal"
+ };
+ } else if (duration > 8) {
+ recommendations = {
+ printer: "MYP-Langzeit-Drucker (Halle A)",
+ reason: "Zuverlässig für lange Druckaufträge",
+ availability: "90%",
+ utilization: "35%",
+ suitability: "Sehr gut"
+ };
+ }
+
+ return recommendations;
+ }
+
+ // Event Listeners für dynamische Empfehlung
+ document.getElementById('eventStart').addEventListener('change', updatePrinterRecommendation);
+ document.getElementById('eventEnd').addEventListener('change', updatePrinterRecommendation);
+ document.getElementById('eventPriority').addEventListener('change', updatePrinterRecommendation);
+ document.getElementById('eventPrinter').addEventListener('change', function() {
+ if (this.value !== '') {
+ hideSmartRecommendation();
+ } else {
+ updatePrinterRecommendation();
+ }
+ });
});
{% endblock %}
\ No newline at end of file
diff --git a/backend/app/templates/printers.html b/backend/app/templates/printers.html
index 7e1228fb..51afaf24 100644
--- a/backend/app/templates/printers.html
+++ b/backend/app/templates/printers.html
@@ -1219,12 +1219,14 @@