From ae74f4fc0c5e6dfd60bfb85c0e80aee9ad4f53b8 Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Thu, 29 May 2025 19:09:47 +0200 Subject: [PATCH] "feat: Implement test printer functionality in admin system" --- backend/app/create_test_printers.py | 99 ++++++++++++++++ backend/app/database/myp.db | Bin 106496 -> 106496 bytes backend/app/database/myp.db-wal | Bin 61832 -> 8272 bytes backend/app/static/js/admin-system.js | 138 +++++++++++++++++++++- backend/app/templates/calendar.html | 163 +++++++++++++++++++++++++- backend/app/templates/printers.html | 55 +++++---- 6 files changed, 425 insertions(+), 30 deletions(-) create mode 100644 backend/app/create_test_printers.py 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 32ea88ad552e98e588198650d9aa1bd9e33393d9..69e80e3aa5936eadee9b3db337375040afb6106b 100644 GIT binary patch delta 509 zcmZoTz}9epZ9}WPX|799mXTpWie-UWYOqI&d$68gqPf3IK)y>sR&JW9d6Yq@Ye8j1_pMZ`bGsdG;OziDok)|YgAxGRUc54nOBlpq+mLEqQCm&DZU=s_%ySi zYM%Vo7r$l9sOn*s8G|ib=7--hCRELn|NB));db1nfNkrYI2iNO(sDBMQrUP|q~RP^ VWDW~5hZ&i}#0}K5`Nw~b1ps)BdAR@p delta 345 zcmZoTz}9epZ9}WPX+gS|k6%TZTYg%4K}uP%Ygw9GXuiLBuycUErLmEhxsidJk5Oo( zQGR%4T2*ebx2wNTSmNYe@)mp+R;ET)CgysUre=l)2AdQZ6@(cW7}$7DG4Pl3z2i&e zy~7*Kb854oz&0MKMm9D+26<6yb!~NVacNFRhJd2Xypq%+1+&SHKI)T&d_90lSg|WH z1uNO=>mk_4#)7J3vW8y;P?Q->^fXwM2~9N5AEcHMP1M4tVpG7j^-i3O`Dtl6nR%&f bJS@_14l6Q;1)0N)%wggN>e>9`KgR+9>q%E> diff --git a/backend/app/database/myp.db-wal b/backend/app/database/myp.db-wal index e1de3851288c5456228f5a38886ec3966a4e4f9e..5f70a446e8cfa9a7c331c2b2dba0cf1310262d29 100644 GIT binary patch delta 463 zcmeBp%zVKiz`UNVi9z>~1OtNr0}wF2yrKSewvlD^X};Tk{_8)p0g4F&u@p?)uuE-A zMy!Y`FHn$!?;`{ME&g@|T$qztk{X{_ zT9Ticmy%i$4;GC#G*mEDVJD=@2&hVp4X8>StSd7GrX9r1OUzA;Pt8rt%mJ!WFqCFx z5!Z&;S&vW%lb2&*5jTX$A|zp^0CjUQvxqbB7ndZK6dP(WvB)|L7ZhdYm02{{zEvY$G0Lh0q<%>e?f{HCRePB{S? zrOtU(83v`51zsk3rd8qo<~arieom=Td4_3bsm=yb5dj{Bsg41j-Wg8%L7}OWxfHCq P&8_F_YzPhkJ>fB*y_009U<00Izz00bZa0SG|Al|Z4r;XYwwTfq}- zYmExg(tC!I=~U+Fw4EEVllD|-*v=f3N3!YC%NywM20}O3-Q&}%Y8$}_Re^=b7Cw#su_Ef zfv$=4NPBl|>OfzwVRla#$<$4a3xrLXhKWK=c37U`p_TuRVCR-YCl5FMz1A0)6+~}P z@CN6kuity#6#+Ja00bZa0SG_<0uX=z1Xim+L2B>|>mS${d@|V9wyq@_jptIC zyq%LsoDglNog0@FRgpqchn?G>%CIMijwp#xndqjh$V5>hrbRV1YIPBdDhhF|E2W@I zKeD5v?Cth~+%vHVk@aHmS>xBR&S%&nVOgbmIBXU~kt>nKxXUH#E=$y%CF*>My2}zZ zLTN$tR4+txS%~T^MB@ulT^7RjVHCvX>V>E-3kf?5QTal`t_vaT)HGEuBMSJ8 z7ed_@Vyap}Y^+`gby)~;7DD+#h}%L8t=OhNdgJ@VeOPbx_lXr!oOqxzzkqV>`3qmY z9y)sudr=I0AOx-lz7KpE_%v{uE#LtG2tWV=5P$##AOHafKmY;|fWYk*@HVe&ZI~ZG z6Zr)XztGGtH1P|K_cpIBX#v-aznPtur^8svqy_)oj9-CyftNo& zmOuLH;0*Etci~kYhYSG-KmY;|fB*y_009U<00JvPz-O8X=Gah*XH&b4yOPXIGzWbA(&&K|WAuq73*Hi2q0uX=z1Rwwb z2tWV=5P$##AW*MB&AdP*6;Qk%L1n>xUSRTT&&i(S;uk(&zcVk8_nxn}V7w0k5P$## zAOHafKmY;|SQP@u3%Jh9&FRVUF91#Q{009U<00Izz00bbg1c4Qh7hp%ThIQc! zl32YNug`bu7i-4r&St#MH{*4e&3KJaTC0tWynw4_e1w>_6fwVmE5#+vFEH}no4-E$ rOX?!>0!z3$Vp9k}00Izz00bZa0SG_<0uX?}trOr=0V2OpPhQ|3vXxJw 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 = ` +
+

Keine Logs gefunden

+
+ `; + 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 @@