in der Roademap weitergemacht

This commit is contained in:
Michael Stangl
2026-07-12 17:02:14 +02:00
parent dd84293c36
commit f880ae1c97
8 changed files with 752 additions and 74 deletions
+56 -21
View File
@@ -160,10 +160,17 @@ Die folgenden Grundsatzfragen sind entschieden und gelten für alle Meilensteine
- [x] Python 2 → 3 Migration (bereits erledigt)
- [x] Unittest-Suite mit Kernszenarien (bereits erledigt)
- [ ] `Farm` erweitern: `TopN()` statt nur `Bester()` — Grundlage für "mehrere Varianten"
später. Kleine, risikoarme Änderung am bestehenden Code.
- [ ] Determinismus-Option: `random.seed()` als Parameter durchreichen, damit ein Ergebnis
bei Bedarf reproduzierbar ist (Support-Anfragen: "warum sitzt Tante Erna da?")
- [x] `Farm` erweitern: `TopN()` statt nur `Bester()` — Grundlage für "mehrere Varianten"
später. Umgesetzt in `libs/ga.py` (`Farm.TopN(n)`: die `n` besten Lösungen absteigend,
geclamped auf `[0, len(Pool)]`).
- [x] Determinismus-Option: `random.seed()` als Parameter durchreichen, damit ein Ergebnis
bei Bedarf reproduzierbar ist (Support-Anfragen: "warum sitzt Tante Erna da?").
Umgesetzt als `--seed <int>` in `libs/platz.py`. **Wichtig:** der RNG-Seed allein
reichte nicht — `Person` hasht im `set()` einer `Gruppe` sonst über `id()`
(Speicheradresse), wodurch `set.pop()`-Reihenfolge in `Gruppe.teilen()` pro Prozess
variierte. Behoben durch Id-basiertes `__hash__`/`__eq__` an `Person`
(`libs/Strukturdaten.py`); Integer-Ids sind zudem `PYTHONHASHSEED`-unabhängig. Auf
`work/hochzeit` verifiziert: gleicher Seed → bytegleicher Plan, anderer Seed → abweichend.
- [x] Rechtsform geklärt: Betrieb über die bestehende **MiSta GmbH** (Impressum), keine
Neugründung — Ausgründung aus der Holding erst bei Erfolg (Konzeptentscheidung Nr. 7)
- [x] Namensfrage geklärt: **"Sitz & Platz"** für Deutschland, **Sitzwunder/SeatWonder**
@@ -201,11 +208,22 @@ Session-basiert, nichts wird dauerhaft gespeichert (schont Datenschutz-Aufwand i
- [x] Einfache Browser-Testoberfläche (`web/static/index.html`): Tisch-Tabelle mit
Presets, CSV-Textfeld/-Upload, Berechnen-Button, Kennzahlen + SVG-Anzeige —
Zwischenschritt, wird durch den Konva-Tisch-Editor (unten) abgelöst
- [ ] **Interaktiver Tisch-Editor** (Frontend, React/Vue + SVG):
Kreise per Drag & Drop platzieren, Kapazität einstellen, Nachbarschaft automatisch
aus räumlicher Nähe ableiten (statt manueller `Nachbarliste`-Pflege in `tische.ini`)
- [x] **Interaktiver Tisch-Editor** (Frontend): Kreise per Drag & Drop platzieren,
Kapazität einstellen (+//Direkteingabe), Tische hinzufügen/löschen, Nachbarschaft
live aus räumlicher Nähe abgeleitet (gleiche Radius-Logik wie `_NachbarnAbleiten`
im Backend, Radius per Slider). Umgesetzt als `web/static/editor.html`
**buildfrei, Inline-SVG + Vanilla-JS** statt React/Konva, weil das bestehende
Frontend keine Build-Toolchain (npm/package.json) hat; das State-Modell
(`{id, plaetze, x, y}`) ist dasselbe wie für eine spätere Konva-Portierung.
Backend um `GET /sitzung/{id}/tische` (Layout-Round-Trip) ergänzt, Session wird
per `localStorage` mit der Testoberfläche geteilt. Nebenbei behoben: fehlender
`import configparser` in `web/main.py` (der `/tische-datei`-Upload wäre sonst mit
`NameError` abgestürzt).
- Baut direkt auf `alsSVG()`-Geometrie auf (Radius-Berechnung ist bereits da)
- [ ] Undo/Redo, "Variante neu berechnen"-Button
- [x] **Undo/Redo** im Editor: Snapshot-History (Stapel je Vor-Zustand von `state.tische`),
ein Drag zählt als ein Schritt; Tastatur `Strg+Z` / `Strg+Y` (bzw. `Strg+Shift+Z`),
`Entf` löscht den ausgewählten Tisch. "Variante neu berechnen" liegt bereits als
wiederholter `POST .../berechnen` in der Testoberfläche vor (jeder Klick = neue Variante).
- [ ] Hosting: einfacher Single-Server-Betrieb bei **Hetzner** reicht (Konzeptentscheidung
Nr. 6 — kein Kubernetes-Overkill für MVP)
@@ -225,15 +243,28 @@ verarbeitet werden (was bei jedem Webserver der Fall ist).
**Ziel:** Nutzer bekommt mehrere Vorschläge statt einer festen Lösung, kann vergleichen.
- [ ] `Farm.TopN(n)` nutzen, um z.B. 35 unterschiedliche gute Lösungen zurückzugeben
(ggf. mit verschiedenen Zufalls-Seeds parallel starten für Diversität)
- [ ] Vergleichsansicht im Frontend: Varianten nebeneinander, Kennzahlen sichtbar machen
(Wertigkeit, Anzahl Gruppentrennungen, "Alleine sitzende Personen")
- [ ] Performance: bei sehr großen Sälen (200+ Personen) ggf. `Zyklus`-Parameter
(Anzahl Generationen/Population) an Problemgröße koppeln, damit Antwortzeit
im Web-Kontext (Sekunden, nicht Minuten) bleibt — Lasttest nötig
- [ ] Optional: Hintergrund-Job-Queue (z.B. Celery/RQ), falls Berechnung >5s dauert,
damit der Webserver nicht blockiert
- [x] `Farm.TopN(n)` nutzen, um z.B. 35 unterschiedliche gute Lösungen zurückzugeben:
`POST /berechnen?varianten=N` (15, Default 3) nimmt `TopN` über den ganzen Endpool
und dedupliziert über eine Sitzordnungs-Signatur (`frozenset` aus Tisch-Id ×
Personen-Ids), damit Mutations-Verwandte mit identischer Sitzordnung nicht als
"Varianten" zählen. Liefert der Pool weniger unterschiedliche Sitzordnungen, kommen
weniger zurück; ein weiterer Berechnen-Klick startet einen frischen Lauf.
Seed-parallele Läufe für noch mehr Diversität ("ggf.") waren nicht nötig
ein Lauf liefert im Test bereits 3 paarweise verschiedene Varianten.
`GET /ergebnis.svg?variante=k` rendert jede Variante einzeln.
- [x] Vergleichsansicht im Frontend: Varianten-Karten nebeneinander mit Kennzahlen
(Wertigkeit, getrennte Gruppen + Teilungen gesamt, allein sitzende Personen —
letzteres via `LeuteAllein()`, zählt nur Personen aus mehrköpfigen Gruppen ohne
Gruppenmitglied am Tisch); Klick auf eine Karte schaltet Tische-/Gruppen-Tabellen
und den SVG-Raumplan auf die Variante um.
- [x] Performance: Lasttest erledigt (2026-07-12) — `work/innenhoefe` (300 Gäste,
88 Tische) rechnet mit dem **Adaptiv**-Profil (Web-Default) in ~1,71,9 s
(57 Generationen, inkl. Python-Start), Easy in ~0,3 s. Beides weit unter der
5-s-Schwelle → `Zyklus`-Parameter-Kopplung an die Problemgröße ist **nicht nötig**;
erst wieder prüfen, falls Zyklen deutlich vergrößert werden (siehe Risiko Nr. 2).
- [x] Optional: Hintergrund-Job-Queue (z.B. Celery/RQ), falls Berechnung >5s dauert —
**entfällt nach dem Lasttest** (s.o.): Adaptiv bleibt bei ~2 s, der Webserver
blockiert nicht spürbar. Entscheidung dokumentiert, bei Bedarf reaktivierbar.
**Tech-Debt / Modernisierung (Code-Review 2026-07-11):** Beim Umbau bereits erledigt sind
`eval()``ast.literal_eval` (bzw. ein sicherer Mini-Auswerter für die String-Ausdrücke
@@ -331,10 +362,14 @@ parallel anlaufen — eher ~44,5 Monate bis M4-Abschluss.
nicht erst kurz vor Launch.
2. **GA-Performance bei großen Sälen** — erster Datenpunkt (2026-07-11): das Beispiel
`work/innenhoefe` mit 300 Gästen an 88 Tischen rechnet mit dem Easy-Zyklus in ~0,4 s
durch — weit unter der 5-s-Schwelle, ab der M2 eine Job-Queue vorsieht. Offen bleibt
der Test mit größeren Zyklen (mehr Generationen für bessere Lösungsqualität bei
vielen Trennungen) — die Lösungsgüte bei 4er-Tischen mit großen Gruppen (Wertigkeit
384) legt nahe, dass hier eher mehr GA-Iterationen nötig sind als bisher.
durch — weit unter der 5-s-Schwelle, ab der M2 eine Job-Queue vorsieht. Zweiter
Datenpunkt (2026-07-12, M2-Lasttest): dasselbe Beispiel mit dem **Adaptiv**-Profil
(Web-Default) braucht ~1,71,9 s (57 Generationen, 3 Seeds, inkl. Python-Start) —
Job-Queue und Parameter-Kopplung bleiben unnötig. Weiter offen ist nur die
Lösungsgüte-Frage: bei 4er-Tischen mit großen Gruppen (Wertigkeit ~560) legen die
vielen Trennungen nahe, dass mehr GA-Iterationen (größere `Geduld`/`Start`-Werte)
bessere Pläne liefern könnten — das würde die Laufzeit dann wieder Richtung
5-s-Schwelle schieben und wäre neu zu messen.
3. **Wettbewerb ist real und etabliert** — siehe Abschnitt "Marktübersicht" oben.
PerfectTablePlan verfolgt seit Jahren denselben GA-Ansatz, allerdings als Desktop-Tool.
Differenzierung muss über Web-nativ + DSGVO-Hosting + detailliertere Regeln