From 1e2eedc62bd4e43fda092ff8bc230c9c166a2036 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Thu, 9 Jul 2026 12:13:17 +0200 Subject: [PATCH] Replace fixed input/output paths in platz.cfg with --indir switch Tischedatei, XMLdatei_Bestellung, AusgabeTischePersonen and AusgabePersonTisch are no longer configured in platz.cfg. Instead, platz.py now requires a --indir command-line switch pointing at a directory that must contain tische.ini and bestellung.xml, and into which TischePersonen.txt/PersonenTische.csv are written - e.g. work/test1 or work/test2. This makes it trivial to run against different guest lists without editing platz.cfg. platz.bat/platz.sh forward extra arguments so --indir can be passed through. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 44 +++++++++++++++++++++++++++----------------- README.md | 38 ++++++++++++++++++-------------------- bin/platz.bat | 2 +- bin/platz.sh | 2 +- cfg/platz.cfg | 4 ---- doc/dateiformate.md | 8 ++++++++ libs/platz.py | 37 ++++++++++++++++++++++++------------- 7 files changed, 79 insertions(+), 56 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8f2b251..6145998 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,24 +39,31 @@ Entry point is `libs/platz.py`, invoked via the scripts in `bin/` (each provided "$PLATZ_LIBS/platz.py"`. This is the actual program entry point (replaces the old `bin/run` / `bin/run.bat`, which hardcoded absolute paths and have been removed). -Configuration is driven by `cfg/platz.cfg`, which points (via `$PLATZ_CFG`/`$PLATZ_WORK` -env-var expansion) to: +`libs/platz.py` requires a command-line switch `--indir ` (parsed with `optparse`, +since this is Python 2) pointing at a directory that holds the per-run input files and +receives the output files — e.g. `work/test1/`, `work/test2/`, or any new folder following +the same layout: +- `tische.ini` — table definitions: id, `Nummer`, `Hof` (venue/court), `Plaetze` (seats), + `Nachbarliste` (neighbor table ids), `Koordinaten`. +- `bestellung.xml` — the guest order: `` blocks containing `` (with + `Vorname`/`Nachname`/optional `Titel`) and an `` (reservation count per person + template — this expands into that many identical bookings), plus optional `` to + pin a VIP group to a specific table. +- Output (written back into the same `--indir` directory): `TischePersonen.txt` (table → + seated people) and `PersonenTische.csv` (person → table, sorted). + +`cfg/platz.cfg` (still located via `$PLATZ_CFG`, independent of `--indir`) only selects the +GA cycle/penalty config: - `cfg/zyklus.cfg` — defines the GA run schedule ("Zyklus"): a sequence of actions (`e`=erzeugen/create, `s`=selektieren/select-best, `S`=random-select, `m`=mutieren/mutate, `j`=behalten/keep parents, `z`=neue Generation/advance generation) each with a count. - `cfg/strafen.cfg` — the penalty ("Strafpunkte") table: cost of splitting a group across tables (by group size and number of splits), cost of a lone person, and per-table under-utilization penalties. -- A per-run working directory under `work//` (e.g. `work/test1/`, `work/test2/`) - containing: - - `tische.ini` — table definitions: id, `Nummer`, `Hof` (venue/court), `Plaetze` (seats), - `Nachbarliste` (neighbor table ids), `Koordinaten`. - - `bestellung.xml` — the guest order: `` blocks containing `` (with - `Vorname`/`Nachname`/optional `Titel`) and an `` (reservation count per person - template — this expands into that many identical bookings), plus optional `` to - pin a VIP group to a specific table. - - Output: `TischePersonen.txt` (table → seated people) and `PersonenTische.csv` - (person → table, sorted). + +`tische.ini`/`bestellung.xml`/`TischePersonen.txt`/`PersonenTische.csv` are fixed filenames +resolved as `os.path.join(indir, ...)` in `platz.py` — they are no longer configured in +`platz.cfg`. ## Architecture @@ -99,13 +106,16 @@ Domain-agnostic and reusable in principle: that table rather than going through normal GA placement. ### Data flow through `platz.py` (`__main__`) -1. Read `platz.cfg` to locate the other config/data files (env-var expansion via +1. Parse `--indir` (required) from the command line via `optparse`. +2. Read `platz.cfg` to locate `zyklus.cfg`/`strafen.cfg` (env-var expansion via `os.path.expandvars`). -2. Load `Zyklus` (GA schedule), `Strafliste` (penalties), `Tische` (table layout). -3. Load `bestellung.xml` via `XMLConfig` → people, groups, VIP group, VIP seat map. -4. Build a `Farm(Zyklus, Sitzplatzverteilung, Tische=..., Gruppen=..., Strafen=..., +3. Load `Zyklus` (GA schedule), `Strafliste` (penalties), `Tische` (table layout, from + `/tische.ini`). +4. Load `/bestellung.xml` via `XMLConfig` → people, groups, VIP group, VIP seat map. +5. Build a `Farm(Zyklus, Sitzplatzverteilung, Tische=..., Gruppen=..., Strafen=..., VIPListe=..., VIPs=...)`, which runs the whole GA cycle in its constructor. -5. Take `Farm.Bester()` and call `.speichern(...)` to write the output files. +6. Take `Farm.Bester()` and call `.speichern(...)` to write `/TischePersonen.txt` and + `/PersonenTische.csv`. Both `libs/platz.py` and `libs/Strukturdaten.py` also have self-test code under `if __name__ == '__main__':` that exercises the classes directly with hardcoded sample data — diff --git a/README.md b/README.md index 95f5bcc..440beb9 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,18 @@ required) by calling `setenv.bat`/`setenv.sh` first. ```bat :: Windows bin\activate_venv.bat -bin\platz.bat +bin\platz.bat --indir work\test1 ``` ```bash # Linux/macOS source bin/activate_venv.sh -bin/platz.sh +bin/platz.sh --indir work/test1 ``` `bin/platz.bat` / `bin/platz.sh` call `setenv` to set `PLATZ`, `PLATZ_CFG`, `PLATZ_LIBS`, `PLATZ_WORK`, `PLATZ_IN`, `PLATZ_OUT`, add `PLATZ_LIBS` to `PYTHONPATH`, and then run -`libs/platz.py`. +`libs/platz.py`, forwarding any extra arguments (like `--indir`) to it. Other helper scripts (`bin/setenv.*`, `bin/get_cmd.*`) follow the same environment convention: @@ -74,22 +74,8 @@ convention: ### Configuring a run -`cfg/platz.cfg` selects which cycle/penalty config and which working directory -(`work//`) to use for a given run: - -```ini -[Config] -Zyklusdatei=$PLATZ_CFG/zyklus.cfg -Zyklusart=Easy -Strafendatei=$PLATZ_CFG/strafen.cfg -Tischedatei=$PLATZ_WORK/test1/tische.ini -XMLdatei_Bestellung=$PLATZ_WORK/test1/bestellung.xml -AusgabeTischePersonen=$PLATZ_WORK/test1/TischePersonen.txt -AusgabePersonTisch=$PLATZ_WORK/test1/PersonenTische.csv -``` - -To run against a different dataset, point the `Tischedatei`/`XMLdatei_Bestellung`/output -entries at another `work//` folder, or add a new one following the same layout: +`--indir ` is required and points at a directory containing the input files and +receiving the output files for one run — e.g. `work/test1/` or `work/test2/`: - `tische.ini` — one section per table: `Nummer`, `Hof` (venue/court), `Plaetze` (seat count), `Nachbarliste` (neighboring table ids), `Koordinaten`. @@ -98,6 +84,18 @@ entries at another `work//` folder, or add a new one following the same la that template into). A `` containing a `` tag is treated as a VIP group pinned to that table instead of being placed by the GA. +To run against a different dataset, create a new folder with a `tische.ini` and +`bestellung.xml` following the same layout and pass it via `--indir`. + +`cfg/platz.cfg` still selects the GA cycle/penalty config (independent of `--indir`): + +```ini +[Config] +Zyklusdatei=$PLATZ_CFG/zyklus.cfg +Zyklusart=Easy +Strafendatei=$PLATZ_CFG/strafen.cfg +``` + `cfg/zyklus.cfg` defines the GA run schedule ("Zyklus") as an `Abfolge` (sequence of actions) and matching `Anzahl` (counts): `e` create, `s` select best, `S` select randomly, `m` mutate, `j` keep parents, `z` advance generation. @@ -108,7 +106,7 @@ a neighboring table, and per-table seat-utilization penalties. ### Output -A run writes two files into the configured working directory: +A run writes two files into the `--indir` directory: - `TischePersonen.txt` — table → seated people, with venue/table/seat numbers. - `PersonenTische.csv` — person → table, sorted alphabetically by name. diff --git a/bin/platz.bat b/bin/platz.bat index 0bf7533..fe058bf 100644 --- a/bin/platz.bat +++ b/bin/platz.bat @@ -5,4 +5,4 @@ REM ================================================================ call "%~dp0setenv.bat" -python "%PLATZ_LIBS%\platz.py" +python "%PLATZ_LIBS%\platz.py" %* diff --git a/bin/platz.sh b/bin/platz.sh index ea804ec..62e4093 100644 --- a/bin/platz.sh +++ b/bin/platz.sh @@ -5,4 +5,4 @@ source "$(dirname "${BASH_SOURCE[0]}")/setenv.sh" -python "$PLATZ_LIBS/platz.py" +python "$PLATZ_LIBS/platz.py" "$@" diff --git a/cfg/platz.cfg b/cfg/platz.cfg index c3fca62..9bf04b3 100644 --- a/cfg/platz.cfg +++ b/cfg/platz.cfg @@ -2,9 +2,5 @@ Zyklusdatei=$PLATZ_CFG/zyklus.cfg Zyklusart=Easy Strafendatei=$PLATZ_CFG/strafen.cfg -Tischedatei=$PLATZ_WORK/test1/tische.ini -XMLdatei_Bestellung=$PLATZ_WORK/test1/bestellung.xml -AusgabeTischePersonen=$PLATZ_WORK/test1/TischePersonen.txt -AusgabePersonTisch=$PLATZ_WORK/test1/PersonenTische.csv diff --git a/doc/dateiformate.md b/doc/dateiformate.md index 90376dc..492765e 100644 --- a/doc/dateiformate.md +++ b/doc/dateiformate.md @@ -3,6 +3,14 @@ Referenz fuer die Eingabe- und Ausgabedateien eines `platz`-Laufs, mit Beispielen aus `work/test1/` und `work/test2/`. +Alle vier Dateien liegen unter dem mit `--indir ` angegebenen Verzeichnis (z.B. +`work/test1/` oder `work/test2/`) unter festen Namen: `tische.ini`, `bestellung.xml` als +Eingabe, `TischePersonen.txt` und `PersonenTische.csv` als Ausgabe. Beispielaufruf: + +```bash +bin/platz.sh --indir work/test1 +``` + ## Tischaufstellung: `tische.ini` Ein INI-File mit einer Sektion pro Tisch. Sektionsname ist die Tisch-Id. diff --git a/libs/platz.py b/libs/platz.py index 8b84aa7..32deaa2 100755 --- a/libs/platz.py +++ b/libs/platz.py @@ -12,13 +12,29 @@ __license__ = "Python" from ga import * from Strukturdaten import * import ConfigParser +import optparse if __name__ == '__main__': - # lade Vorgaben fuer Optimierungszyklus und + # Kommandozeilenoption --indir: Verzeichnis mit den Eingabedateien + # (tische.ini, bestellung.xml) und Ziel fuer die Ausgabedateien + # (TischePersonen.txt, PersonenTische.csv) + parser = optparse.OptionParser() + parser.add_option( "--indir", dest="indir", default=None, + help="Verzeichnis mit tische.ini und bestellung.xml; " + "dort werden auch TischePersonen.txt und PersonenTische.csv abgelegt" ) + (options, args) = parser.parse_args() + + if options.indir is None: + parser.error( "--indir ist erforderlich" ) + InDir = os.path.expandvars( os.path.expanduser( options.indir ) ) + if not os.path.isdir( InDir ): + parser.error( "--indir '%s' ist kein Verzeichnis" % InDir ) + + # lade Vorgaben fuer Optimierungszyklus und config = ConfigParser.ConfigParser() - ConfigPath= os.path.join( os.getenv('PLATZ_CFG'), 'platz.cfg') + ConfigPath= os.path.join( os.getenv('PLATZ_CFG'), 'platz.cfg') config.read( os.path.expandvars( ConfigPath )) Zyklusconfig = os.path.normpath( config.get( 'Config', 'Zyklusdatei' )) @@ -30,17 +46,13 @@ if __name__ == '__main__': ## print Strafenconfig # lade Eingabedateien - Tischconfig = os.path.normpath(config.get( 'Config', 'Tischedatei' )) - Tischconfig = os.path.expandvars(Tischconfig) - Bestellung = os.path.normpath(config.get( 'Config', 'XMLdatei_Bestellung' )) - Bestellung = os.path.expandvars( Bestellung ) + Tischconfig = os.path.join( InDir, 'tische.ini' ) + Bestellung = os.path.join( InDir, 'bestellung.xml' ) # lade Ausgabedateien - AusgabeTP= os.path.normpath(config.get( 'Config', 'AusgabeTischePersonen' )) - AusgabeTP = os.path.expandvars(AusgabeTP) - AusgabePT = os.path.normpath(config.get( 'Config', 'AusgabePersonTisch' )) - AusgabePT = os.path.expandvars(AusgabePT) - + AusgabeTP= os.path.join( InDir, 'TischePersonen.txt' ) + AusgabePT = os.path.join( InDir, 'PersonenTische.csv' ) + Z = Zyklus() Z.laden( Zyklusconfig, Zyklusart ) print Z @@ -67,7 +79,6 @@ if __name__ == '__main__': else: pass - - +