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 <dir> 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
"$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).
|
`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`
|
`libs/platz.py` requires a command-line switch `--indir <dir>` (parsed with `optparse`,
|
||||||
env-var expansion) to:
|
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: `<Gruppe>` blocks containing `<Person>` (with
|
||||||
|
`Vorname`/`Nachname`/optional `Titel`) and an `<Anzahl>` (reservation count per person
|
||||||
|
template — this expands into that many identical bookings), plus optional `<Tisch>` 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
|
- `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,
|
(`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.
|
`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
|
- `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
|
tables (by group size and number of splits), cost of a lone person, and per-table
|
||||||
under-utilization penalties.
|
under-utilization penalties.
|
||||||
- A per-run working directory under `work/<name>/` (e.g. `work/test1/`, `work/test2/`)
|
|
||||||
containing:
|
`tische.ini`/`bestellung.xml`/`TischePersonen.txt`/`PersonenTische.csv` are fixed filenames
|
||||||
- `tische.ini` — table definitions: id, `Nummer`, `Hof` (venue/court), `Plaetze` (seats),
|
resolved as `os.path.join(indir, ...)` in `platz.py` — they are no longer configured in
|
||||||
`Nachbarliste` (neighbor table ids), `Koordinaten`.
|
`platz.cfg`.
|
||||||
- `bestellung.xml` — the guest order: `<Gruppe>` blocks containing `<Person>` (with
|
|
||||||
`Vorname`/`Nachname`/optional `Titel`) and an `<Anzahl>` (reservation count per person
|
|
||||||
template — this expands into that many identical bookings), plus optional `<Tisch>` to
|
|
||||||
pin a VIP group to a specific table.
|
|
||||||
- Output: `TischePersonen.txt` (table → seated people) and `PersonenTische.csv`
|
|
||||||
(person → table, sorted).
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
@@ -99,13 +106,16 @@ Domain-agnostic and reusable in principle:
|
|||||||
that table rather than going through normal GA placement.
|
that table rather than going through normal GA placement.
|
||||||
|
|
||||||
### Data flow through `platz.py` (`__main__`)
|
### 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`).
|
`os.path.expandvars`).
|
||||||
2. Load `Zyklus` (GA schedule), `Strafliste` (penalties), `Tische` (table layout).
|
3. Load `Zyklus` (GA schedule), `Strafliste` (penalties), `Tische` (table layout, from
|
||||||
3. Load `bestellung.xml` via `XMLConfig` → people, groups, VIP group, VIP seat map.
|
`<indir>/tische.ini`).
|
||||||
4. Build a `Farm(Zyklus, Sitzplatzverteilung, Tische=..., Gruppen=..., Strafen=...,
|
4. Load `<indir>/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.
|
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 `<indir>/TischePersonen.txt` and
|
||||||
|
`<indir>/PersonenTische.csv`.
|
||||||
|
|
||||||
Both `libs/platz.py` and `libs/Strukturdaten.py` also have self-test code under
|
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 —
|
`if __name__ == '__main__':` that exercises the classes directly with hardcoded sample data —
|
||||||
|
|||||||
@@ -51,18 +51,18 @@ required) by calling `setenv.bat`/`setenv.sh` first.
|
|||||||
```bat
|
```bat
|
||||||
:: Windows
|
:: Windows
|
||||||
bin\activate_venv.bat
|
bin\activate_venv.bat
|
||||||
bin\platz.bat
|
bin\platz.bat --indir work\test1
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Linux/macOS
|
# Linux/macOS
|
||||||
source bin/activate_venv.sh
|
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`,
|
`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
|
`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
|
Other helper scripts (`bin/setenv.*`, `bin/get_cmd.*`) follow the same environment
|
||||||
convention:
|
convention:
|
||||||
@@ -74,22 +74,8 @@ convention:
|
|||||||
|
|
||||||
### Configuring a run
|
### Configuring a run
|
||||||
|
|
||||||
`cfg/platz.cfg` selects which cycle/penalty config and which working directory
|
`--indir <dir>` is required and points at a directory containing the input files and
|
||||||
(`work/<name>/`) to use for a given run:
|
receiving the output files for one run — e.g. `work/test1/` or `work/test2/`:
|
||||||
|
|
||||||
```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/<name>/` folder, or add a new one following the same layout:
|
|
||||||
|
|
||||||
- `tische.ini` — one section per table: `Nummer`, `Hof` (venue/court), `Plaetze` (seat
|
- `tische.ini` — one section per table: `Nummer`, `Hof` (venue/court), `Plaetze` (seat
|
||||||
count), `Nachbarliste` (neighboring table ids), `Koordinaten`.
|
count), `Nachbarliste` (neighboring table ids), `Koordinaten`.
|
||||||
@@ -98,6 +84,18 @@ entries at another `work/<name>/` folder, or add a new one following the same la
|
|||||||
that template into). A `<Gruppe>` containing a `<Tisch>` tag is treated as a VIP group
|
that template into). A `<Gruppe>` containing a `<Tisch>` tag is treated as a VIP group
|
||||||
pinned to that table instead of being placed by the GA.
|
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
|
`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,
|
actions) and matching `Anzahl` (counts): `e` create, `s` select best, `S` select randomly,
|
||||||
`m` mutate, `j` keep parents, `z` advance generation.
|
`m` mutate, `j` keep parents, `z` advance generation.
|
||||||
@@ -108,7 +106,7 @@ a neighboring table, and per-table seat-utilization penalties.
|
|||||||
|
|
||||||
### Output
|
### 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.
|
- `TischePersonen.txt` — table → seated people, with venue/table/seat numbers.
|
||||||
- `PersonenTische.csv` — person → table, sorted alphabetically by name.
|
- `PersonenTische.csv` — person → table, sorted alphabetically by name.
|
||||||
|
|||||||
+1
-1
@@ -5,4 +5,4 @@ REM ================================================================
|
|||||||
|
|
||||||
call "%~dp0setenv.bat"
|
call "%~dp0setenv.bat"
|
||||||
|
|
||||||
python "%PLATZ_LIBS%\platz.py"
|
python "%PLATZ_LIBS%\platz.py" %*
|
||||||
|
|||||||
+1
-1
@@ -5,4 +5,4 @@
|
|||||||
|
|
||||||
source "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
|
source "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
|
||||||
|
|
||||||
python "$PLATZ_LIBS/platz.py"
|
python "$PLATZ_LIBS/platz.py" "$@"
|
||||||
|
|||||||
@@ -2,9 +2,5 @@
|
|||||||
Zyklusdatei=$PLATZ_CFG/zyklus.cfg
|
Zyklusdatei=$PLATZ_CFG/zyklus.cfg
|
||||||
Zyklusart=Easy
|
Zyklusart=Easy
|
||||||
Strafendatei=$PLATZ_CFG/strafen.cfg
|
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
Referenz fuer die Eingabe- und Ausgabedateien eines `platz`-Laufs, mit Beispielen aus
|
Referenz fuer die Eingabe- und Ausgabedateien eines `platz`-Laufs, mit Beispielen aus
|
||||||
`work/test1/` und `work/test2/`.
|
`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
|
||||||
|
Eingabe, `TischePersonen.txt` und `PersonenTische.csv` als Ausgabe. Beispielaufruf:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/platz.sh --indir work/test1
|
||||||
|
```
|
||||||
|
|
||||||
## Tischaufstellung: `tische.ini`
|
## Tischaufstellung: `tische.ini`
|
||||||
|
|
||||||
Ein INI-File mit einer Sektion pro Tisch. Sektionsname ist die Tisch-Id.
|
Ein INI-File mit einer Sektion pro Tisch. Sektionsname ist die Tisch-Id.
|
||||||
|
|||||||
+24
-13
@@ -12,13 +12,29 @@ __license__ = "Python"
|
|||||||
from ga import *
|
from ga import *
|
||||||
from Strukturdaten import *
|
from Strukturdaten import *
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
import optparse
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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()
|
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 ))
|
config.read( os.path.expandvars( ConfigPath ))
|
||||||
|
|
||||||
Zyklusconfig = os.path.normpath( config.get( 'Config', 'Zyklusdatei' ))
|
Zyklusconfig = os.path.normpath( config.get( 'Config', 'Zyklusdatei' ))
|
||||||
@@ -30,17 +46,13 @@ if __name__ == '__main__':
|
|||||||
## print Strafenconfig
|
## print Strafenconfig
|
||||||
|
|
||||||
# lade Eingabedateien
|
# lade Eingabedateien
|
||||||
Tischconfig = os.path.normpath(config.get( 'Config', 'Tischedatei' ))
|
Tischconfig = os.path.join( InDir, 'tische.ini' )
|
||||||
Tischconfig = os.path.expandvars(Tischconfig)
|
Bestellung = os.path.join( InDir, 'bestellung.xml' )
|
||||||
Bestellung = os.path.normpath(config.get( 'Config', 'XMLdatei_Bestellung' ))
|
|
||||||
Bestellung = os.path.expandvars( Bestellung )
|
|
||||||
|
|
||||||
# lade Ausgabedateien
|
# lade Ausgabedateien
|
||||||
AusgabeTP= os.path.normpath(config.get( 'Config', 'AusgabeTischePersonen' ))
|
AusgabeTP= os.path.join( InDir, 'TischePersonen.txt' )
|
||||||
AusgabeTP = os.path.expandvars(AusgabeTP)
|
AusgabePT = os.path.join( InDir, 'PersonenTische.csv' )
|
||||||
AusgabePT = os.path.normpath(config.get( 'Config', 'AusgabePersonTisch' ))
|
|
||||||
AusgabePT = os.path.expandvars(AusgabePT)
|
|
||||||
|
|
||||||
Z = Zyklus()
|
Z = Zyklus()
|
||||||
Z.laden( Zyklusconfig, Zyklusart )
|
Z.laden( Zyklusconfig, Zyklusart )
|
||||||
print Z
|
print Z
|
||||||
@@ -67,7 +79,6 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user