bryan.hoffmann 3b9dfb6e6f
Build und Push Docker Image / build-and-push (push) Failing after 4s
t2
2026-08-21 11:24:06 +02:00
t2
2026-08-21 11:24:06 +02:00
2026-08-20 12:24:38 +02:00
2026-08-20 12:24:38 +02:00
2026-08-20 12:24:38 +02:00
2026-08-20 12:24:38 +02:00
2026-08-20 12:24:38 +02:00
2026-08-20 12:24:38 +02:00

WDM Performance Produktionserfassung für ~40 Maschinen

Flask-Backend mit serverseitigem Rendering (Jinja2-Templates, normale HTML-Formulare, Post-Redirect-Get) kein Client-JavaScript, genau wie beim Essensportal-Projekt. Postgres-Datenbank.

Start

cp .env.example .env
# .env anpassen (Passwort etc.)
docker compose up -d --build

Die Anwendung ist danach unter http://<server>:8090/ erreichbar (Port über APP_PORT in .env änderbar).

Wichtig zur DB-Initialisierung: build/db/init.sql (= dein hochgeladenes Schema, ergänzt um ein paar Beispiel-Abweichungsgründe) wird nur beim allerersten Start ausgeführt, wenn das Datenvolume noch leer ist. Hast du bereits eine bestehende Postgres-Datenbank mit Daten, dann:

  • entweder das db-Volume vor dem ersten Start leer lassen und init.sql anpassen/ersetzen, oder
  • den db-Service in docker-compose.yml entfernen und stattdessen über DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASSWORD in der .env auf deine bestehende Datenbank zeigen.

Aufruf der einzelnen Seiten

  • Maschine N (140): http://<server>:8090/m<N> z.B. http://<server>:8090/m1
  • Produktionsleiter: http://<server>:8090/start

Alte/lange URL-Form: http://<server>:8090/m<N>_m_view_main (z.B. .../m1_m_view_main) funktioniert unverändert weiter und zeigt auf dieselbe Seite. Sie bleibt bewusst zusätzlich zur kurzen Form erhalten, damit bereits auf Tablets als Kiosk-Startseite/Lesezeichen hinterlegte URLs nicht angepasst werden müssen. Für neu eingerichtete Geräte reicht die kurze Form /m<N>.

Hinweis zum ursprünglich gewünschten #/...-Format: Ein Hash-Fragment (#/...) wird nie an den Server geschickt und funktioniert nur mit Client-JavaScript, das per Definition entfällt (kein JS). Deshalb sind die Seiten stattdessen unter echten Server-Pfaden mit denselben Bezeichnern erreichbar (ohne #).

Die Maschinennummer <N> entspricht der id in der Tabelle Maschinen. Für Tablets an den Maschinen einfach jeweils die passende URL als Startseite/Kiosk-Link einrichten.

Reverse Proxy: Falls du die App hinter einem Unterpfad (z.B. /app/wdm-performance/) betreiben willst, muss der Proxy diesen Präfix vor Weiterleitung an den Container entfernen (strip prefix), da das Backend mit wurzelrelativen Pfaden arbeitet. Beispiel nginx:

location /app/wdm-performance/ {
    proxy_pass http://wdm-app:5000/;
}

Funktionsumfang

Maschinenseite (/m<N>, alte Form /m<N>_m_view_main weiterhin gültig)

  • Überschrift = Maschinenname aus der DB
  • Kein aktiver Auftrag → Start-Button → Auftragsauswahl (offene/pausierte Aufträge dieser Maschine, sortiert nach Priorität/sid) → Auswahl markiert Auftrag als aktiv, schreibt start-Eintrag in MDE
  • Aktiver Auftrag → Start-Button ausgeblendet, stattdessen:
    • Prod. erfassen: Menge (numerisch, ≥0, ≥ bisherige Menge, ≤110% Soll)
      • Pflicht-Dropdown Abweichungsgrund, Server validiert verbindlich vor dem Schreiben in MDE
    • Auftrag pausieren: gleiche Eingabemaske, setzt Auftrag auf status=pause, aktiv=false
    • Auftrag beenden: bei Menge < Soll erscheint eine große Bestätigungsabfrage; nach Bestätigung wird die Sollmenge als produziert in MDE (status=beendet) vermerkt, Auftrag auf abgeschlossen / aktiv=false gesetzt

Produktionsleiter (#/start)

  • Auftrag anlegen: Maschine (Dropdown), Auftragsnummer, optional Artikel, Produktionsmenge, Stück/Stunde sowie optional eine Priorität (Position in der Warteschlange dieser Maschine, 1 = als nächstes dran) alles serverseitig validiert. Wird eine Priorität angegeben, wird der Auftrag an genau dieser Position eingereiht und alle bestehenden offenen/pausierten Aufträge dieser Maschine rücken automatisch nach (dichte Nummerierung 1..N, kein bloßes Setzen eines rohen Zahlenwerts). Ohne Angabe wird der Auftrag ans Ende der Warteschlange gehängt.
  • Aufträge importieren (/start_import): Aufträge aus einer Excel- (.xlsx) oder CSV-Datei per Drag & Drop oder Dateiauswahl bulk-anlegen, siehe eigener Abschnitt unten
  • Auftragsübersicht: alle Aufträge (inkl. Artikel, Maschinenname), standardmäßig nur offen/pausiert (umschaltbar auf "Alle"), optional nach Maschine gefiltert, löschbar und (mit Einschränkungen) bearbeitbar:
    • Status und Priorität sind hier nicht editierbar (nur informativ angezeigt) Status ändert sich ausschließlich über die eigentlichen Maschinen-Aktionen (Start/Pause/Beenden), Priorität ausschließlich über die Drag & Drop-Seite "Auftragspriorität".
    • Aktive Aufträge werden angezeigt, können aber nicht bearbeitet werden ein Bearbeitungsversuch (auch ein direkter POST) wird mit einer Fehlermeldung abgelehnt, solange der Auftrag gerade an der Maschine läuft.
    • Editierbar bleiben Maschine (inkl. automatischem Umhängen der kompletten MDE-Buchungshistorie auf die neue Maschine), Auftragsnummer, Artikel, Produktionsmenge und Stück/Stunde.
    • Mengen-Schutz: Wurde an einem Auftrag bereits eine Menge gemeldet (MDE-Historie vorhanden), darf die Produktionsmenge nicht mehr so weit reduziert werden, dass die bereits gemeldete Menge außerhalb der 110%-Toleranz läge (dieselbe Toleranzregel, die auch an der Maschine beim Erfassen gilt) die App zeigt dabei den konkret noch zulässigen Mindestwert an.
  • Buchungskorrektur: direkter Eingriff in die MDE-Tabelle (editieren/ löschen), optional nach Maschine gefiltert
  • Auftragspriorität: Reihenfolge der offenen/pausierten Aufträge je Maschine per Drag & Drop ändern (Auftrag anfassen und an neue Position ziehen) wird automatisch gespeichert. Funktioniert am PC/Laptop mit Maus; auf reinen Touch-Geräten ohne Maus ggf. eingeschränkt (Hinweis dazu steht auf der Seite selbst).
  • Zu Maschine springen: Direktlinks zu jeder Maschinenseite (zeigt an, ob die Maschine gerade läuft)
  • Maschinenverwaltung (/start_maschinen_verwaltung, passwortgeschützt): Maschinen anlegen, umbenennen/Standort ändern, löschen (nur möglich wenn keine Aufträge mehr an der Maschine hängen). Passwort über die Umgebungsvariable MASCHINEN_PASSWORT in der .env konfigurierbar (Standard admin bitte unbedingt ändern!). Für die Login-Session wird zusätzlich ein SECRET_KEY benötigt (ebenfalls in der .env setzen).

Aufträge importieren (/start_import)

Excel- (.xlsx) oder CSV-Datei mit (mindestens) diesen Spalten hochladen (Reihenfolge egal, Erkennung anhand von Stichworten in der Kopfzeile). Die Datei kann per Drag & Drop auf das gestrichelte Feld gezogen oder klassisch über die Dateiauswahl ausgewählt werden. Bei CSV werden Zeichenkodierung (UTF-8/BOM, sonst cp1252), Trennzeichen (Komma, Semikolon wie bei deutschem Excel-Export üblich oder Tab) sowie deutsches/internationales Zahlformat (1.234,56 oder 1234.56) automatisch erkannt.

Spalte Verwendung
Maschine muss exakt (Groß-/Kleinschreibung egal) einer bestehenden Maschine entsprechen, sonst wird die Zeile verworfen
man. Prio wird direkt als Priorität (sid) übernommen (das Feld erlaubt auch Kommazahlen, z.B. 15.1)
FA.-Nr. → Auftragsnummer. Existiert diese FA.-Nr. bereits irgendwo im System, wird die Zeile unangetastet übersprungen
Rest-Menge zusammen mit Ist-Menge → Produktionsmenge (Soll) = Rest + Ist
Ist-Menge wird als bereits produzierte Menge übernommen (Start-Buchung in MDE), damit Fortschritt und Mengen-Validierung an der Maschine korrekt weiterlaufen
Artikel wird 1:1 übernommen und angezeigt
Soll-pro-Stunde → Stück/Stunde (Soll)
Bezeichnung, Auftrag, Lieferdatum, Bemerkung werden ignoriert

Ist die Rest-Menge einer Zeile 0, wird der Auftrag direkt mit Status "Abgeschlossen" angelegt (inkl. passender Buchung in der MDE-Historie). Nach dem Import zeigt die Seite eine Liste, was importiert, was wegen unbekannter Maschine verworfen und was wegen bereits vorhandener FA.-Nr. übersprungen wurde.

Artikel

Neues, optionales Textfeld je Auftrag (Spalte artikel). Kann beim manuellen Anlegen eingetragen werden oder kommt aus dem Import; wird auf der Maschinenseite, in der Auftragsauswahl, der Auftragsübersicht, der Prioritätsliste und (sofern gerade ein Auftrag aktiv ist) auch in der Maschinenübersicht ("Zu Maschine springen") mit angezeigt.

Logo / Icon

build/static/logo.svg wird als Favicon und (in doppelter Standardgröße, 104×104px) über dem Seitentitel angezeigt. Einfach durch eine eigene Datei mit demselben Namen (logo.svg, idealerweise quadratisch) ersetzen, um das Firmenlogo einzubinden kein Code muss dafür angepasst werden.

JavaScript-Einsatz (Ausnahmen vom "kein Client-JS"-Grundsatz)

Zwei bewusste, punktuelle Ausnahmen von der sonst reinen Server-Side- Rendering-Philosophie:

  • Die Filter-Dropdowns (Maschine/Status) in Auftragsübersicht, Buchungs- korrektur und Auftragspriorität lösen die Filterung direkt beim Auswählen aus (onchange-Submit) reiner Komfort, der "Filtern"-Button bleibt als Fallback sichtbar und funktioniert auch ohne JavaScript.
  • Die Drag-&-Drop-Sortierung auf der Auftragspriorität-Seite benötigt zwingend JavaScript (ohne JS erscheint dort ein Hinweis, dass Sortieren nicht möglich ist alle anderen Seiten funktionieren weiterhin ohne JS).

Stabilität im Dauerbetrieb

  • Backend läuft unter gunicorn (mehrere Worker/Threads statt Flask-Devserver)
  • Datenbank-Connection-Pool mit automatischem Retry beim Start (verträgt verzögerten DB-Start ohne Absturz)
  • Docker-Healthchecks für App und DB, restart: unless-stopped
  • Reines Server-Side-Rendering ohne Build-Step und ohne Client-JavaScript jede Aktion ist ein klassischer Formular-Submit (Post-Redirect-Get), das ist auf Tablets mit älteren/eingeschränkten Browsern besonders robust

Struktur

wdm-performance/
├── Dockerfile              Build-Anweisungen (Kontext = Projektwurzel)
├── docker-compose.yml
├── README.md
├── .env.example
└── build/
    ├── app.py               Flask-Anwendung (alle Routen, Server-Side-Rendering)
    ├── db.py                 DB-Pool mit Retry-Logik
    ├── requirements.txt
    ├── db/init.sql             Schema (dein Dump + Beispiel-Abweichungsgründe)
    ├── templates/               Jinja2-HTML-Templates (kein JavaScript)
    └── static/
        └── style.css             Großflächiges Touch-Design

Wichtig: docker compose muss aus dem Projektwurzel-Verzeichnis heraus aufgerufen werden (dort, wo docker-compose.yml liegt), nicht aus build/.

S
Description
No description provided
Readme
104 KiB
Languages
Python 55.7%
HTML 32%
CSS 11.5%
Dockerfile 0.8%