Initial commit
Build und Push Docker Image / build-and-push (push) Failing after 49s

This commit is contained in:
2026-08-20 12:24:38 +02:00
commit 36180448e1
29 changed files with 2917 additions and 0 deletions
+208
View File
@@ -0,0 +1,208 @@
# 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
```bash
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:
```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/`.