Web-M1-Ausbau: JSON-Gaesteliste, GA-Verlauf, Python-Serversteuerung, tische.ini-Upload

- bestellung.xml -> bestellung.json: neuer JSONConfig (loest XMLConfig ab),
  alle work-Beispiele konvertiert, Doku (CLAUDE/README/dateiformate) umgestellt
- ga.py: Beobachtungsobjekt Entwicklung zeichnet den Loesungsverlauf auf,
  neuer Schalter --show-development erzeugt Entwicklung.svg (beste/mittlere/
  schlechteste Wertigkeit ueber die Zyklus-Aktionen)
- web/main.py: neuer Endpunkt /tische-datei (tische.ini-Upload, ast.literal_eval),
  Frontend kann jetzt eine tische.ini laden
- libs/webserver.py: plattformunabhaengige start/stop/status-Steuerung der
  Web-Testinstanz (ersetzt run_web_helper.ps1), raeumt auch verwaiste
  --reload-Prozessbaeume zuverlaessig auf
- Sicherheit/Modernisierung: eval() -> ast.literal_eval in Tische/Strafliste,
  sicherer Mini-Auswerter fuer die String-Ausdruecke in zyklus.cfg,
  defaultdict(set) in Plaetze
- SVG: Tischnummer fett zentriert im Tischkreis statt "Tisch x" darueber
- Tests: JSONConfig, Entwicklung und der Zyklus-Auswerter abgedeckt (9 Tests)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-07-11 12:49:42 +02:00
parent 00dffc59eb
commit 9de80d2f2a
41 changed files with 5054 additions and 4033 deletions
+75 -93
View File
@@ -4,7 +4,7 @@ Referenz fuer die Eingabe- und Ausgabedateien eines `platz`-Laufs, mit Beispiele
`work/test1/` und `work/test2/`.
Alle vier Dateien liegen unter dem mit `--indir <dir>` angegebenen Verzeichnis (z.B.
`work/test1/` oder `work/test2/`) unter festen Namen: `tische.ini`, `bestellung.xml` als
`work/test1/` oder `work/test2/`) unter festen Namen: `tische.ini`, `bestellung.json` als
Eingabe, `TischePersonen.txt` und `PersonenTische.csv` als Ausgabe. Beispielaufruf:
```bash
@@ -67,108 +67,90 @@ Werte wie `Nachbarliste` und `Koordinaten` werden mit `eval()` geladen (siehe
`Tische.laden()` in `libs/Strukturdaten.py`) — es steht also echter Python-Ausdruckscode im
File, keine reine INI-Syntax.
## Gaesteliste: `bestellung.xml`
## Gaesteliste: `bestellung.json`
Ein `<Bestellung>` mit mehreren `<Gruppe>`-Bloecken. Jede Gruppe enthaelt:
Ein JSON-Objekt mit dem Schluessel `"gruppen"`, einer Liste von Gruppen-Objekten. Jede
Gruppe enthaelt:
- ein oder mehrere `<Person>`-Elemente mit `<Vorname>`, `<Nachname>` und optionalem
`<Titel>`
- optional `<Anzahl>N</Anzahl>`, um die zuletzt beschriebene Person zu einer Sammelbuchung
von `N` (anonymen/gleichnamigen) Personen zu vervielfaeltigen
- optional `<Tisch>Id</Tisch>`, um die Gruppe als VIP-Gruppe fest an den angegebenen Tisch
zu binden (sie wird dann **nicht** vom GA platziert)
- `"personen"`: Liste von Personen-Objekten mit `"vorname"`, `"nachname"` und optionalem
`"titel"` (Pflicht: Vor- und Nachname)
- optional `"name"`: Anzeigename der Gruppe (z.B. `"Familie Huber"`); nur fuer Ausgabe und
Web-Anzeige, nicht fuer die Optimierung relevant
- optional `"anzahl"`: N, um **jede** Person der Gruppe zu einer Sammelbuchung von N
(gleichnamigen) Buchungen zu vervielfaeltigen — praktisch fuer anonyme Kontingente
- optional `"tisch"`: Tisch-Id, um die Gruppe als VIP-Gruppe fest an diesen Tisch zu binden
(sie wird dann **nicht** vom GA platziert)
XML-Kommentare (`<!-- ... -->`) sind erlaubt und werden beim Parsen ignoriert.
Geladen wird das File von `JSONConfig` in `libs/Strukturdaten.py` (Nachfolger des frueheren
`XMLConfig`; das alte `bestellung.xml`-Format wurde vollstaendig durch JSON abgeloest).
Die Datei sollte UTF-8 sein; ein evtl. vorhandenes BOM (Excel) wird geschluckt.
Minimalbeispiel mit Sammelbuchungen, aus `work/test1/bestellung.xml`:
Minimalbeispiel mit Sammelbuchungen, aus `work/test1/bestellung.json`:
```xml
<Bestellung>
<Gruppe>
<Person>
<Vorname>A</Vorname>
<Nachname>AA</Nachname>
<Titel>Dipl. Ing.</Titel>
</Person>
<Anzahl>10</Anzahl>
</Gruppe>
<Gruppe>
<Person>
<Vorname>B</Vorname>
<Nachname>BB</Nachname>
<Titel>Prof.</Titel>
</Person>
<Anzahl>4</Anzahl>
</Gruppe>
<Gruppe>
<Person>
<Vorname>C</Vorname>
<Nachname>CC</Nachname>
<Titel>Dr.</Titel>
</Person>
<Anzahl>5</Anzahl>
</Gruppe>
</Bestellung>
```json
{
"gruppen": [
{
"anzahl": 10,
"personen": [
{ "vorname": "A", "nachname": "AA", "titel": "Dipl. Ing." }
]
},
{
"anzahl": 4,
"personen": [
{ "vorname": "B", "nachname": "BB", "titel": "Prof." }
]
},
{
"anzahl": 5,
"personen": [
{ "vorname": "C", "nachname": "CC", "titel": "Dr." }
]
}
]
}
```
Umfangreicheres Beispiel mit benannten Personen und einer VIP-Gruppe, aus
`work/test2/bestellung.xml`:
Umfangreicheres Beispiel mit benannten Gruppen und einer VIP-Gruppe, aus
`work/test2/bestellung.json`:
```xml
<Bestellung>
<!-- einfache Gruppe aus drei Personen
mit festem Wunsch fuer einen Tisch-->
<Gruppe>
<Person>
<Vorname>Michael</Vorname>
<Nachname>Stangl</Nachname>
<Titel>Dipl. Ing.</Titel>
</Person>
<Person>
<Vorname>Magnus</Vorname>
<Nachname>Mueller</Nachname>
<Titel>Professor</Titel>
</Person>
<Person>
<Vorname>Bernd Robert</Vorname>
<Nachname>Hoehn</Nachname>
<Titel>Professor</Titel>
</Person>
<Tisch>1</Tisch>
</Gruppe>
<!-- einfache Gruppe aus zwei Personen -->
<Gruppe>
<Person>
<Vorname>Heinz</Vorname>
<Nachname>Bruegge</Nachname>
</Person>
<Person>
<Vorname>Hubert</Vorname>
<Nachname>K</Nachname>
</Person>
</Gruppe>
<!--Gruppe aus drei anonymen Personen-->
<Gruppe>
<Person>
<Vorname>Bestelli</Vorname>
<Nachname>Bloedkopf</Nachname>
</Person>
<Anzahl>3</Anzahl>
</Gruppe>
</Bestellung>
```json
{
"gruppen": [
{
"name": "einfache Gruppe aus drei Personen mit festem Wunsch fuer einen Tisch",
"tisch": 1,
"personen": [
{ "vorname": "Michael", "nachname": "Stangl", "titel": "Dipl. Ing." },
{ "vorname": "Magnus", "nachname": "Mueller", "titel": "Professor" },
{ "vorname": "Bernd Robert", "nachname": "Hoehn", "titel": "Professor" }
]
},
{
"name": "einfache Gruppe aus zwei Personen",
"personen": [
{ "vorname": "Heinz", "nachname": "Bruegge" },
{ "vorname": "Hubert", "nachname": "K" }
]
},
{
"name": "Gruppe aus drei anonymen Personen",
"anzahl": 3,
"personen": [
{ "vorname": "Bestelli", "nachname": "Bloedkopf" }
]
}
]
}
```
Die erste Gruppe hier ist per `<Tisch>1</Tisch>` fest an Tisch 1 gebunden (VIP-Gruppe); die
letzte Gruppe erzeugt ueber `<Anzahl>3</Anzahl>` drei gleichnamige "Bestelli Bloedkopf"
Buchungen.
Die erste Gruppe hier ist per `"tisch": 1` fest an Tisch 1 gebunden (VIP-Gruppe); die letzte
Gruppe erzeugt ueber `"anzahl": 3` drei gleichnamige "Bestelli Bloedkopf" Buchungen.
> Fuer den Web-Weg (Meilenstein 1) gibt es zusaetzlich einen CSV-Import (`libs/CSVConfig.py`,
> Spalten `Vorname;Nachname;Titel;Gruppe;Tisch;Anzahl`) — inhaltlich aequivalent, nur
> flach statt verschachtelt.
## Ausgabe: `TischePersonen.txt`