Compare commits

...

2 Commits

Author SHA1 Message Date
Michael Stangl 9de80d2f2a 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>
2026-07-11 12:49:42 +02:00
Michael Stangl 00dffc59eb Web-MVP (M1): FastAPI-API, Testoberflaeche, tische.ini-Upload, zwei neue Beispiele
- web/main.py: Session-basierte REST-API um den GA-Kern (POST /sitzung,
  .../tische, .../tische-datei, .../personen, .../berechnen,
  GET .../ergebnis[.svg]); Sessions in-memory mit 24h-TTL
- web/static/index.html: einfache Browser-Testoberflaeche (Tisch-Tabelle
  mit Presets, tische.ini-Upload, CSV-Gaesteliste, Kennzahlen, SVG)
- bin/run_web.bat/.sh + run_web_helper.ps1: start/stop/status inkl.
  Aufraeumen verwaister uvicorn-Reload-Kindprozesse (Zombie-Sockets)
- work/hochzeit: 120 Hochzeitsgaeste, Ehrentafel, Kindertisch/Teenager-
  Tisch fest gesetzt, Kinderalter normalverteilt, ~85% Auslastung
- work/innenhoefe: 300 Gaeste in zwei trapezfoermigen Innenhoefen mit
  4er-Tischen, 12 VIPs vor der Buehne, Umfeld.svg mit Tanzflaeche/Band
- Roadmap: Konzeptentscheidungen 1-11 (Zielgruppe, Einmalzahlung, MoR,
  Hetzner, Zwei-Marken-Strategie Sitz&Platz/SeatWonder), Namensrecherche
  im Anhang, M1-Fortschritt und GA-Lasttest-Datenpunkt dokumentiert

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:21:45 +02:00
44 changed files with 7122 additions and 415 deletions
+1
View File
@@ -6,3 +6,4 @@ work/ausgabe.txt
out/ out/
.vscode/ .vscode/
*.swp *.swp
/log/
+14 -10
View File
@@ -62,10 +62,11 @@ receives the output files — e.g. `work/test1/`, `work/test2/`, or any new fold
the same layout: the same layout:
- `tische.ini` — table definitions: id, `Nummer`, `Hof` (venue/court), `Plaetze` (seats), - `tische.ini` — table definitions: id, `Nummer`, `Hof` (venue/court), `Plaetze` (seats),
`Nachbarliste` (neighbor table ids), `Koordinaten`. `Nachbarliste` (neighbor table ids), `Koordinaten`.
- `bestellung.xml` — the guest order: `<Gruppe>` blocks containing `<Person>` (with - `bestellung.json` — the guest order: `{ "gruppen": [ ... ] }`, each group holding a
`Vorname`/`Nachname`/optional `Titel`) and an `<Anzahl>` (reservation count per person `"personen"` list (each with `vorname`/`nachname`/optional `titel`), an optional
template — this expands into that many identical bookings), plus optional `<Tisch>` to `"anzahl"` (reservation count — expands every person in the group into that many identical
pin a VIP group to a specific table. bookings), an optional `"name"` (display label), and an optional `"tisch"` to pin a VIP
group to a specific table. Replaces the old `bestellung.xml`/`XMLConfig`.
- Output (written back into the same `--indir` directory): `TischePersonen.txt` (table → - Output (written back into the same `--indir` directory): `TischePersonen.txt` (table →
seated people) and `PersonenTische.csv` (person → table, sorted). seated people) and `PersonenTische.csv` (person → table, sorted).
@@ -78,7 +79,7 @@ GA cycle/penalty config:
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.
`tische.ini`/`bestellung.xml`/`TischePersonen.txt`/`PersonenTische.csv` are fixed filenames `tische.ini`/`bestellung.json`/`TischePersonen.txt`/`PersonenTische.csv` are fixed filenames
resolved as `os.path.join(indir, ...)` in `platz.py` — they are no longer configured in resolved as `os.path.join(indir, ...)` in `platz.py` — they are no longer configured in
`platz.cfg`. `platz.cfg`.
@@ -130,10 +131,12 @@ Domain-agnostic and reusable in principle:
touching, people as colored circles (color = group, via `SVGGruppenFarben`) placed evenly touching, people as colored circles (color = group, via `SVGGruppenFarben`) placed evenly
around the inside of their table's circle. The table-to-table grid spacing is derived around the inside of their table's circle. The table-to-table grid spacing is derived
from the largest table radius so tables never overlap regardless of their `Koordinaten`. from the largest table radius so tables never overlap regardless of their `Koordinaten`.
- `XMLConfig` — parses `bestellung.xml` into `Personen`/`Gruppen`/VIP-group/VIP-seat-map, - `JSONConfig` — parses `bestellung.json` into
expanding each `<Person>` template by its sibling `<Anzahl>` into that many individual `Personen`/`Gruppen`/VIP-group/VIP-seat-map/group-names, expanding each person by the
bookings, and treating any `<Gruppe>` containing a `<Tisch>` tag as a VIP group pinned to group's `"anzahl"` into that many individual bookings, and treating any group with a
that table rather than going through normal GA placement. `"tisch"` key as a VIP group pinned to that table rather than going through normal GA
placement. Returns a 5th element (`GruppenNamen`, `{Gid: display name}`) that the older
`XMLConfig` did not; `CSVConfig` (the web CSV adapter) returns the same 5-tuple.
### Data flow through `platz.py` (`__main__`) ### Data flow through `platz.py` (`__main__`)
1. Parse `--indir` (required) from the command line via `optparse`. 1. Parse `--indir` (required) from the command line via `optparse`.
@@ -141,7 +144,8 @@ Domain-agnostic and reusable in principle:
`os.path.expandvars`). `os.path.expandvars`).
3. Load `Zyklus` (GA schedule), `Strafliste` (penalties), `Tische` (table layout, from 3. Load `Zyklus` (GA schedule), `Strafliste` (penalties), `Tische` (table layout, from
`<indir>/tische.ini`). `<indir>/tische.ini`).
4. Load `<indir>/bestellung.xml` via `XMLConfig` → people, groups, VIP group, VIP seat map. 4. Load `<indir>/bestellung.json` via `JSONConfig` → people, groups, VIP group, VIP seat
map, group names.
5. Build a `Farm(Zyklus, Sitzplatzverteilung, Tische=..., Gruppen=..., Strafen=..., 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.
6. Take `Farm.Bester()` and call `.speichern(...)` to write `<indir>/TischePersonen.txt` and 6. Take `Farm.Bester()` and call `.speichern(...)` to write `<indir>/TischePersonen.txt` and
+8 -7
View File
@@ -23,9 +23,10 @@ platz/
``` ```
- `libs/platz.py` — entry point; wires together config, input data, and the GA run. - `libs/platz.py` — entry point; wires together config, input data, and the GA run.
- `libs/ga.py` — generic genetic-algorithm scaffolding (`Loesung`, `Zyklus`, `Farm`). - `libs/ga.py` — generic genetic-algorithm scaffolding (`Loesung`, `Zyklus`, `Farm`,
`Entwicklung`).
- `libs/Strukturdaten.py` — domain model for the seating problem (`Person`, `Gruppe`, - `libs/Strukturdaten.py` — domain model for the seating problem (`Person`, `Gruppe`,
`Tisch`, `Strafliste`, `Sitzplatzverteilung`, `XMLConfig`). `Tisch`, `Strafliste`, `Sitzplatzverteilung`, `JSONConfig`).
## Running the tests ## Running the tests
@@ -89,13 +90,13 @@ 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 - `tische.ini` — one section per table: `Nummer`, `Hof` (venue/court), `Plaetze` (seat
count), `Nachbarliste` (neighboring table ids), `Koordinaten`. count), `Nachbarliste` (neighboring table ids), `Koordinaten`.
- `bestellung.xml` — `<Gruppe>` blocks, each with a `<Person>` template - `bestellung.json` — `{ "gruppen": [ ... ] }`, each group with a `"personen"` list
(`Vorname`/`Nachname`/optional `Titel`) and an `<Anzahl>` (how many bookings to expand (`vorname`/`nachname`/optional `titel`), an optional `"anzahl"` (how many bookings to
that template into). A `<Gruppe>` containing a `<Tisch>` tag is treated as a VIP group expand each person into), an optional `"name"` (display label) and an optional `"tisch"`
pinned to that table instead of being placed by the GA. (pins the group as a VIP group to that table instead of placing it via the GA).
To run against a different dataset, create a new folder with a `tische.ini` and 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`. `bestellung.json` following the same layout and pass it via `--indir`.
`cfg/platz.cfg` still selects the GA cycle/penalty config (independent of `--indir`): `cfg/platz.cfg` still selects the GA cycle/penalty config (independent of `--indir`):
+27
View File
@@ -0,0 +1,27 @@
@echo off
REM ================================================================
REM Verwaltet die lokale Web-Testinstanz (FastAPI/uvicorn, M1)
REM
REM run_web.bat start - startet den Server im Hintergrund
REM run_web.bat stop - beendet den Server
REM run_web.bat status - zeigt ob und unter welcher PID er laeuft
REM run_web.bat - startet im Vordergrund mit Auto-Reload
REM (Strg+C zum Beenden)
REM
REM http://localhost:8000 Testoberflaeche
REM http://localhost:8000/docs interaktive API-Doku (Swagger)
REM
REM Die eigentliche Prozessverwaltung liegt plattformunabhaengig in
REM libs/webserver.py (laeuft unverandert auch unter Linux).
REM ================================================================
call "%~dp0setenv.bat" > nul
if not exist "%PLATZ%\.venv\Scripts\activate.bat" (
echo FEHLER: kein .venv gefunden - bitte zuerst bin\install_py.bat ausfuehren
exit /b 1
)
call "%PLATZ%\.venv\Scripts\activate.bat"
python "%PLATZ_LIBS%\webserver.py" %*
exit /b %errorlevel%
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# ================================================================
# Verwaltet die lokale Web-Testinstanz (FastAPI/uvicorn, M1)
#
# run_web.sh start - startet den Server im Hintergrund
# run_web.sh stop - beendet den Server
# run_web.sh status - zeigt ob und unter welcher PID er laeuft
# run_web.sh - startet im Vordergrund mit Auto-Reload
# (Strg+C zum Beenden)
#
# http://localhost:8000 Testoberflaeche
# http://localhost:8000/docs interaktive API-Doku (Swagger)
#
# Die eigentliche Prozessverwaltung liegt plattformunabhaengig in
# libs/webserver.py (laeuft unveraendert auch unter Windows).
# ================================================================
source "$(dirname "${BASH_SOURCE[0]}")/setenv.sh" > /dev/null
if [ ! -f "$PLATZ/.venv/bin/activate" ]; then
echo "FEHLER: kein .venv gefunden - bitte zuerst bin/install_py.sh ausfuehren"
exit 1
fi
source "$PLATZ/.venv/bin/activate"
exec python "$PLATZ_LIBS/webserver.py" "$@"
+75 -93
View File
@@ -4,7 +4,7 @@ Referenz fuer die Eingabe- und Ausgabedateien eines `platz`-Laufs, mit Beispiele
`work/test1/` und `work/test2/`. `work/test1/` und `work/test2/`.
Alle vier Dateien liegen unter dem mit `--indir <dir>` angegebenen Verzeichnis (z.B. 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: Eingabe, `TischePersonen.txt` und `PersonenTische.csv` als Ausgabe. Beispielaufruf:
```bash ```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 `Tische.laden()` in `libs/Strukturdaten.py`) — es steht also echter Python-Ausdruckscode im
File, keine reine INI-Syntax. 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 - `"personen"`: Liste von Personen-Objekten mit `"vorname"`, `"nachname"` und optionalem
`<Titel>` `"titel"` (Pflicht: Vor- und Nachname)
- optional `<Anzahl>N</Anzahl>`, um die zuletzt beschriebene Person zu einer Sammelbuchung - optional `"name"`: Anzeigename der Gruppe (z.B. `"Familie Huber"`); nur fuer Ausgabe und
von `N` (anonymen/gleichnamigen) Personen zu vervielfaeltigen Web-Anzeige, nicht fuer die Optimierung relevant
- optional `<Tisch>Id</Tisch>`, um die Gruppe als VIP-Gruppe fest an den angegebenen Tisch - optional `"anzahl"`: N, um **jede** Person der Gruppe zu einer Sammelbuchung von N
zu binden (sie wird dann **nicht** vom GA platziert) (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 ```json
<Bestellung> {
"gruppen": [
<Gruppe> {
<Person> "anzahl": 10,
<Vorname>A</Vorname> "personen": [
<Nachname>AA</Nachname> { "vorname": "A", "nachname": "AA", "titel": "Dipl. Ing." }
<Titel>Dipl. Ing.</Titel> ]
</Person> },
<Anzahl>10</Anzahl> {
</Gruppe> "anzahl": 4,
"personen": [
<Gruppe> { "vorname": "B", "nachname": "BB", "titel": "Prof." }
<Person> ]
<Vorname>B</Vorname> },
<Nachname>BB</Nachname> {
<Titel>Prof.</Titel> "anzahl": 5,
</Person> "personen": [
<Anzahl>4</Anzahl> { "vorname": "C", "nachname": "CC", "titel": "Dr." }
</Gruppe> ]
}
<Gruppe> ]
<Person> }
<Vorname>C</Vorname>
<Nachname>CC</Nachname>
<Titel>Dr.</Titel>
</Person>
<Anzahl>5</Anzahl>
</Gruppe>
</Bestellung>
``` ```
Umfangreicheres Beispiel mit benannten Personen und einer VIP-Gruppe, aus Umfangreicheres Beispiel mit benannten Gruppen und einer VIP-Gruppe, aus
`work/test2/bestellung.xml`: `work/test2/bestellung.json`:
```xml ```json
<Bestellung> {
"gruppen": [
<!-- einfache Gruppe aus drei Personen {
mit festem Wunsch fuer einen Tisch--> "name": "einfache Gruppe aus drei Personen mit festem Wunsch fuer einen Tisch",
<Gruppe> "tisch": 1,
<Person> "personen": [
<Vorname>Michael</Vorname> { "vorname": "Michael", "nachname": "Stangl", "titel": "Dipl. Ing." },
<Nachname>Stangl</Nachname> { "vorname": "Magnus", "nachname": "Mueller", "titel": "Professor" },
<Titel>Dipl. Ing.</Titel> { "vorname": "Bernd Robert", "nachname": "Hoehn", "titel": "Professor" }
</Person> ]
<Person> },
<Vorname>Magnus</Vorname> {
<Nachname>Mueller</Nachname> "name": "einfache Gruppe aus zwei Personen",
<Titel>Professor</Titel> "personen": [
</Person> { "vorname": "Heinz", "nachname": "Bruegge" },
<Person> { "vorname": "Hubert", "nachname": "K" }
<Vorname>Bernd Robert</Vorname> ]
<Nachname>Hoehn</Nachname> },
<Titel>Professor</Titel> {
</Person> "name": "Gruppe aus drei anonymen Personen",
<Tisch>1</Tisch> "anzahl": 3,
</Gruppe> "personen": [
{ "vorname": "Bestelli", "nachname": "Bloedkopf" }
<!-- 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>
``` ```
Die erste Gruppe hier ist per `<Tisch>1</Tisch>` fest an Tisch 1 gebunden (VIP-Gruppe); die Die erste Gruppe hier ist per `"tisch": 1` fest an Tisch 1 gebunden (VIP-Gruppe); die letzte
letzte Gruppe erzeugt ueber `<Anzahl>3</Anzahl>` drei gleichnamige "Bestelli Bloedkopf" Gruppe erzeugt ueber `"anzahl": 3` drei gleichnamige "Bestelli Bloedkopf" Buchungen.
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` ## Ausgabe: `TischePersonen.txt`
+6 -3
View File
@@ -2,15 +2,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="251.2" height="90.4" viewBox="0 0 251.2 90.4"> <svg xmlns="http://www.w3.org/2000/svg" width="251.2" height="90.4" viewBox="0 0 251.2 90.4">
<rect width="100%" height="100%" fill="white"/> <rect width="100%" height="100%" fill="white"/>
<circle cx="45.2" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="45.2" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="45.2" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 1</text>
<circle cx="45.2" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="45.2" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="125.6" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 2</text>
<circle cx="125.6" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="125.6" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="206" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="206" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="206" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 3</text>
<circle cx="206" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/> <circle cx="206" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="206" cy="58.4" r="12" fill="#4363d8" stroke="black" stroke-width="1"/> <circle cx="206" cy="58.4" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="206" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="206" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -2,27 +2,33 @@
<svg xmlns="http://www.w3.org/2000/svg" width="251.2" height="412" viewBox="0 0 251.2 412"> <svg xmlns="http://www.w3.org/2000/svg" width="251.2" height="412" viewBox="0 0 251.2 412">
<rect width="100%" height="100%" fill="white"/> <rect width="100%" height="100%" fill="white"/>
<circle cx="125.6" cy="366.8" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="125.6" cy="366.8" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="125.6" y="329.6" text-anchor="middle" font-size="10" fill="black">Tisch 1</text>
<circle cx="125.6" cy="353.6" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="353.6" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="380" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="380" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="366.8" r="12" fill="white" fill-opacity="0.75"/>
<text x="125.6" y="366.8" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="206" cy="286.4" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="206" cy="286.4" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="206" y="249.2" text-anchor="middle" font-size="10" fill="black">Tisch 2</text>
<circle cx="206" cy="273.2" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="206" cy="273.2" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="206" cy="299.6" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="206" cy="299.6" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="206" cy="286.4" r="12" fill="white" fill-opacity="0.75"/>
<text x="206" y="286.4" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="206" cy="125.6" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="206" cy="125.6" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="206" y="88.4" text-anchor="middle" font-size="10" fill="black">Tisch 3</text>
<circle cx="206" cy="112.4" r="12" fill="#4363d8" stroke="black" stroke-width="1"/> <circle cx="206" cy="112.4" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="206" cy="138.8" r="12" fill="#4363d8" stroke="black" stroke-width="1"/> <circle cx="206" cy="138.8" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="206" cy="125.6" r="12" fill="white" fill-opacity="0.75"/>
<text x="206" y="125.6" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="125.6" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 4</text>
<circle cx="125.6" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="58.4" r="12" fill="#f58231" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="58.4" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="125.6" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="45.2" cy="125.6" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="45.2" cy="125.6" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="45.2" y="88.4" text-anchor="middle" font-size="10" fill="black">Tisch 5</text>
<circle cx="45.2" cy="112.4" r="12" fill="#f58231" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="112.4" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="138.8" r="12" fill="#f58231" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="138.8" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="125.6" r="12" fill="white" fill-opacity="0.75"/>
<text x="45.2" y="125.6" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="45.2" cy="286.4" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="45.2" cy="286.4" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="45.2" y="249.2" text-anchor="middle" font-size="10" fill="black">Tisch 6</text>
<circle cx="45.2" cy="273.2" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="273.2" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="299.6" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="299.6" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="286.4" r="12" fill="white" fill-opacity="0.75"/>
<text x="45.2" y="286.4" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="500" viewBox="0 0 900 500" font-family="sans-serif">
<rect width="100%" height="100%" fill="white"/>
<text x="450" y="30" text-anchor="middle" font-size="16" fill="#333">Entwicklung der Loesungsfindung</text>
<line x1="60" y1="60" x2="60" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440" x2="840" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440.0" x2="840" y2="440.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="444.0" text-anchor="end" font-size="11" fill="#666">-200</text>
<line x1="60" y1="364.0" x2="840" y2="364.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="368.0" text-anchor="end" font-size="11" fill="#666">-168</text>
<line x1="60" y1="288.0" x2="840" y2="288.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="292.0" text-anchor="end" font-size="11" fill="#666">-136</text>
<line x1="60" y1="212.0" x2="840" y2="212.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="216.0" text-anchor="end" font-size="11" fill="#666">-104</text>
<line x1="60" y1="136.0" x2="840" y2="136.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="140.0" text-anchor="end" font-size="11" fill="#666">-72</text>
<line x1="60" y1="60.0" x2="840" y2="60.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="64.0" text-anchor="end" font-size="11" fill="#666">-40</text>
<text x="18" y="250" text-anchor="middle" font-size="12" fill="#333" transform="rotate(-90 18 250)">Wertigkeit (hoeher = besser)</text>
<text x="450" y="482" text-anchor="middle" font-size="12" fill="#333">Zyklus-Aktion (Zeit)</text>
<text x="60.0" y="456" text-anchor="middle" font-size="10" fill="#999">e</text>
<text x="450.0" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="840.0" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<polyline points="60.0,178.8 450.0,131.2 840.0,60.0" fill="none" stroke="#2f6f4f" stroke-width="2"/>
<polyline points="60.0,313.3 450.0,155.0 840.0,95.6" fill="none" stroke="#c08a2d" stroke-width="2"/>
<polyline points="60.0,440.0 450.0,178.8 840.0,131.2" fill="none" stroke="#a03030" stroke-width="2"/>
<line x1="680" y1="70" x2="704" y2="70" stroke="#2f6f4f" stroke-width="2"/>
<text x="710" y="74" font-size="11" fill="#333">beste Loesung</text>
<line x1="680" y1="88" x2="704" y2="88" stroke="#c08a2d" stroke-width="2"/>
<text x="710" y="92" font-size="11" fill="#333">Durchschnitt</text>
<line x1="680" y1="106" x2="704" y2="106" stroke="#a03030" stroke-width="2"/>
<text x="710" y="110" font-size="11" fill="#333">schlechteste Loesung</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+8 -4
View File
@@ -2,19 +2,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="331.6" height="90.4" viewBox="0 0 331.6 90.4"> <svg xmlns="http://www.w3.org/2000/svg" width="331.6" height="90.4" viewBox="0 0 331.6 90.4">
<rect width="100%" height="100%" fill="white"/> <rect width="100%" height="100%" fill="white"/>
<circle cx="45.2" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="45.2" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="45.2" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 1</text>
<circle cx="45.2" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="45.2" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="45.2" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="45.2" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="125.6" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="125.6" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 2</text>
<circle cx="125.6" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/> <circle cx="125.6" cy="58.4" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="125.6" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="125.6" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="206" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="206" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="206" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 3</text>
<circle cx="206" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="206" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="206" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="206" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="206" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="206" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
<circle cx="286.4" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/> <circle cx="286.4" cy="45.2" r="35.2" fill="white" stroke="black" stroke-width="1.5"/>
<text x="286.4" y="8" text-anchor="middle" font-size="10" fill="black">Tisch 4</text>
<circle cx="286.4" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="286.4" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="286.4" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/> <circle cx="286.4" cy="58.4" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="286.4" cy="45.2" r="12" fill="white" fill-opacity="0.75"/>
<text x="286.4" y="45.2" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">0</text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

+31 -12
View File
@@ -186,17 +186,21 @@ Die folgenden Grundsatzfragen sind entschieden und gelten für alle Meilensteine
**Ziel:** Eine Person kann übers Web eine Hochzeit planen, ohne Login. Ergebnis: PDF/SVG-Export. **Ziel:** Eine Person kann übers Web eine Hochzeit planen, ohne Login. Ergebnis: PDF/SVG-Export.
Session-basiert, nichts wird dauerhaft gespeichert (schont Datenschutz-Aufwand in dieser Phase). Session-basiert, nichts wird dauerhaft gespeichert (schont Datenschutz-Aufwand in dieser Phase).
- [ ] REST-API um den bestehenden Kern (FastAPI empfohlen: async, OpenAPI-Doku gratis, - [x] REST-API um den bestehenden Kern (FastAPI) — umgesetzt in `web/main.py`,
gut für Traffic-Spitzen an Wochenenden) Start per `bin/run_web.bat`/`.sh` (uvicorn --reload, Port 8000), Swagger unter `/docs`
- `POST /sitzung` — legt eine Arbeits-Session an (Tische + Personen) - `POST /sitzung` — legt eine Arbeits-Session an (in-memory, TTL 24h)
- `POST /sitzung/{id}/tische` — Tisch-Layout (Ersatz für `tische.ini`) - `POST /sitzung/{id}/tische` — Tisch-Layout als JSON (Ersatz für `tische.ini`);
Nachbarliste wird aus räumlicher Nähe der Koordinaten abgeleitet
- `POST /sitzung/{id}/personen` — CSV-Upload (Ersatz für `bestellung.xml`) - `POST /sitzung/{id}/personen` — CSV-Upload (Ersatz für `bestellung.xml`)
- `POST /sitzung/{id}/berechnen` — startet die GA-Optimierung - `POST /sitzung/{id}/berechnen` — startet die GA-Optimierung (jeder Aufruf = neue Variante)
- `GET /sitzung/{id}/ergebnis.svg` — liefert `alsSVG()`-Resultat - `GET /sitzung/{id}/ergebnis` + `GET /sitzung/{id}/ergebnis.svg` — Ergebnis als JSON/SVG
- [ ] **CSV-Format definieren und Parser bauen** (Analogon zu `XMLConfig`): - [x] **CSV-Format definiert und Parser gebaut** `libs/CSVConfig.py` (Adapter analog
Spalten z.B. `Vorname;Nachname;Titel;Gruppe;Tisch(optional=VIP)` `XMLConfig`): Spalten `Vorname;Nachname;Titel;Gruppe;Tisch;Anzahl`; `Tisch` = VIP-
→ wird zu `Personen`/`Gruppen`/VIP-Map, exakt wie `XMLConfig.HandleBestellung()` Fixplatz, `Anzahl` = Sammelbuchungs-Expansion, leere `Gruppe` = Einzelgast;
heute — reiner Adapter, kein Kernumbau. `MagNicht`/`MagGern` werden akzeptiert aber noch ignoriert (Konzeptentscheidung Nr. 10)
- [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): - [ ] **Interaktiver Tisch-Editor** (Frontend, React/Vue + SVG):
Kreise per Drag & Drop platzieren, Kapazität einstellen, Nachbarschaft automatisch Kreise per Drag & Drop platzieren, Kapazität einstellen, Nachbarschaft automatisch
aus räumlicher Nähe ableiten (statt manueller `Nachbarliste`-Pflege in `tische.ini`) aus räumlicher Nähe ableiten (statt manueller `Nachbarliste`-Pflege in `tische.ini`)
@@ -231,6 +235,17 @@ verarbeitet werden (was bei jedem Webserver der Fall ist).
- [ ] Optional: Hintergrund-Job-Queue (z.B. Celery/RQ), falls Berechnung >5s dauert, - [ ] Optional: Hintergrund-Job-Queue (z.B. Celery/RQ), falls Berechnung >5s dauert,
damit der Webserver nicht blockiert damit der Webserver nicht blockiert
**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
in `zyklus.cfg`) und `defaultdict(set)` in `Plaetze`. Bewusst *nicht* umgesetzt, für M2 zu
prüfen: **GA-Kern auf [DEAP](https://deap.readthedocs.io/) umstellen** (fertige
Population/Selektion/Statistik — würde das eigene `Farm`/`Entwicklung`-Gerüst ablösen und
`TopN` sowie die Diversitäts-Seeds quasi geschenkt liefern); **SVG-Erzeugung** in
`Sitzplatzverteilung.alsSVG`/`Entwicklung.alsSVG` von String-Konkatenation auf `svgwrite`
oder Jinja2-Templates; **Container-/Datenklassen** (`Person`, `Gruppe`, …) als
`@dataclass`. `Sitzplatzverteilung.TopX` bleibt bewusst handgeschrieben (die monoton
sinkende Schwelle prägt das Mutationsverhalten, siehe Kommentar im Code).
**Aufwand:** ~34 Wochen, aufbauend auf Meilenstein 1. **Aufwand:** ~34 Wochen, aufbauend auf Meilenstein 1.
--- ---
@@ -314,8 +329,12 @@ parallel anlaufen — eher ~44,5 Monate bis M4-Abschluss.
1. **Datenschutz wird unterschätzt** — sobald echte Gästelisten dauerhaft gespeichert 1. **Datenschutz wird unterschätzt** — sobald echte Gästelisten dauerhaft gespeichert
werden (ab M3), ist das kein Nebenthema mehr. Frühzeitig Rechtsberatung einholen, werden (ab M3), ist das kein Nebenthema mehr. Frühzeitig Rechtsberatung einholen,
nicht erst kurz vor Launch. nicht erst kurz vor Launch.
2. **GA-Performance bei großen Sälen**noch nicht lastgetestet mit z.B. 300+ Personen 2. **GA-Performance bei großen Sälen**erster Datenpunkt (2026-07-11): das Beispiel
und vielen Tischen; ggf. Optimierungsbedarf vor M2. `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.
3. **Wettbewerb ist real und etabliert** — siehe Abschnitt "Marktübersicht" oben. 3. **Wettbewerb ist real und etabliert** — siehe Abschnitt "Marktübersicht" oben.
PerfectTablePlan verfolgt seit Jahren denselben GA-Ansatz, allerdings als Desktop-Tool. PerfectTablePlan verfolgt seit Jahren denselben GA-Ansatz, allerdings als Desktop-Tool.
Differenzierung muss über Web-nativ + DSGVO-Hosting + detailliertere Regeln Differenzierung muss über Web-nativ + DSGVO-Hosting + detailliertere Regeln
+8 -8
View File
@@ -1,6 +1,6 @@
""" """
Liest eine Gaesteliste im CSV-Format ein — der Web-Ersatz fuer bestellung.xml Liest eine Gaesteliste im CSV-Format ein — der Web-Ersatz fuer bestellung.json
(Meilenstein 1 der Roadmap, Adapter analog zu XMLConfig in Strukturdaten.py). (Meilenstein 1 der Roadmap, Adapter analog zu JSONConfig in Strukturdaten.py).
CSV-Format (Kopfzeile noetig, Spaltenreihenfolge egal, Trennzeichen ; oder ,): CSV-Format (Kopfzeile noetig, Spaltenreihenfolge egal, Trennzeichen ; oder ,):
@@ -12,10 +12,10 @@ CSV-Format (Kopfzeile noetig, Spaltenreihenfolge egal, Trennzeichen ; oder ,):
zusammen und sollen moeglichst zusammen sitzen. zusammen und sollen moeglichst zusammen sitzen.
Leer = Einzelperson (bildet eine eigene Ein-Personen-Gruppe). Leer = Einzelperson (bildet eine eigene Ein-Personen-Gruppe).
- Tisch: optionale Tischnummer — die Person ist VIP und wird fest an - Tisch: optionale Tischnummer — die Person ist VIP und wird fest an
diesen Tisch gesetzt (wie <Tisch> in bestellung.xml). diesen Tisch gesetzt (wie "tisch" in bestellung.json).
VIPs laufen nicht durch die GA-Platzierung. VIPs laufen nicht durch die GA-Platzierung.
- Anzahl: optionale Reservierungsanzahl — expandiert die Zeile in so - Anzahl: optionale Reservierungsanzahl — expandiert die Zeile in so
viele identische Buchungen (wie <Anzahl> in bestellung.xml, viele identische Buchungen (wie "anzahl" in bestellung.json,
z.B. fuer anonyme Sammelbuchungen). Leer = 1. z.B. fuer anonyme Sammelbuchungen). Leer = 1.
Zusaetzlich werden die Spalten "MagNicht" und "MagGern" akzeptiert, Zusaetzlich werden die Spalten "MagNicht" und "MagGern" akzeptiert,
@@ -35,7 +35,7 @@ from Strukturdaten import Person, Personen, Gruppe, Gruppen
class CSVConfig: class CSVConfig:
"""aus CSV Text/Datei alles einlesen (Rueckgabe wie XMLConfig.laden, """aus CSV Text/Datei alles einlesen (Rueckgabe wie JSONConfig.laden,
plus Gruppennamen fuer die Anzeige im Web-Frontend)""" plus Gruppennamen fuer die Anzeige im Web-Frontend)"""
PflichtSpalten = ['vorname', 'nachname'] PflichtSpalten = ['vorname', 'nachname']
@@ -46,11 +46,11 @@ class CSVConfig:
"""parst den CSV-Text und baut Personen/Gruppen/VIPs auf """parst den CSV-Text und baut Personen/Gruppen/VIPs auf
Rueckgabe: [ PN, GN, VIPGruppe, VipHash, GruppenNamen ] Rueckgabe: [ PN, GN, VIPGruppe, VipHash, GruppenNamen ]
- PN: Personen-Container (ohne VIPs, wie XMLConfig) - PN: Personen-Container (ohne VIPs, wie JSONConfig)
- GN: Gruppen-Container (ohne VIP-Gruppe, wie XMLConfig) - GN: Gruppen-Container (ohne VIP-Gruppe, wie JSONConfig)
- VIPGruppe: Gruppe(0) mit allen fest gesetzten Personen - VIPGruppe: Gruppe(0) mit allen fest gesetzten Personen
- VipHash: { PersonId: Tischnummer } fuer VIPsSetzen() - VipHash: { PersonId: Tischnummer } fuer VIPsSetzen()
- GruppenNamen: { GruppenId: Anzeigename } (neu gegenueber XMLConfig) - GruppenNamen: { GruppenId: Anzeigename }
""" """
# Trennzeichen automatisch bestimmen: die Kopfzeile entscheidet # Trennzeichen automatisch bestimmen: die Kopfzeile entscheidet
Kopfzeile = Text.lstrip('').splitlines()[0] if Text.strip() else '' Kopfzeile = Text.lstrip('').splitlines()[0] if Text.strip() else ''
+133 -88
View File
@@ -11,11 +11,12 @@ __license__ = "Python"
from itertools import chain from itertools import chain
from random import * from random import *
import ast
import configparser, os import configparser, os
import copy import copy
import math import math
from collections import defaultdict
from functools import total_ordering from functools import total_ordering
from xml.dom import minidom
# Strukturklassen: Person, Gruppe, Tisch # Strukturklassen: Person, Gruppe, Tisch
@@ -256,11 +257,14 @@ class Tische:
config.read( os.path.expanduser(FileName) ) config.read( os.path.expanduser(FileName) )
for section in config.sections(): for section in config.sections():
Tid = int(section) Tid = int(section)
Nummer = eval(config.get( section, 'Nummer' )) # ast.literal_eval statt eval: tische.ini kann eine hochgeladene
nPl = eval(config.get( section, 'Plaetze' )) # Fremddatei sein (Web-M1), da darf kein beliebiger Code laufen.
Koords = eval(config.get( section, 'Koordinaten' )) # Erlaubt weiterhin Zahlen, Tupel und Listen wie zuvor.
Nachbarn = eval(config.get( section, 'Nachbarliste' )) Nummer = ast.literal_eval(config.get( section, 'Nummer' ))
Hofid = eval(config.get( section, 'Hof' )) nPl = ast.literal_eval(config.get( section, 'Plaetze' ))
Koords = ast.literal_eval(config.get( section, 'Koordinaten' ))
Nachbarn = ast.literal_eval(config.get( section, 'Nachbarliste' ))
Hofid = ast.literal_eval(config.get( section, 'Hof' ))
T = Tisch( Tid, Plaetze=nPl, Koordinaten=Koords, Nachbarliste=Nachbarn, Hof=Hofid, Nummer=Nummer ) T = Tisch( Tid, Plaetze=nPl, Koordinaten=Koords, Nachbarliste=Nachbarn, Hof=Hofid, Nummer=Nummer )
self.Tisch_dazu( T ) self.Tisch_dazu( T )
def vonId(self, SearchedId): def vonId(self, SearchedId):
@@ -281,19 +285,21 @@ class Tische:
class Plaetze: class Plaetze:
"""Informationen ueber freie oder benachbarte Plaetze""" """Informationen ueber freie oder benachbarte Plaetze"""
def __init__(self, Tische): def __init__(self, Tische):
# mache Listen mit den Tischen mit n Freien Plaetzen # je Anzahl freier Plaetze ein Set von Tisch-Ids. defaultdict(set)
self.__TIDsNFreie = [] # erspart die manuelle Vorbelegung und liefert fuer noch nie
# gesehene Fuellstaende automatisch ein leeres Set.
self.__TIDsNFreie = defaultdict(set)
self.__Groesster = Tische.Groesster self.__Groesster = Tische.Groesster
self.__Tische = Tische self.__Tische = Tische
for i in range( self.__Groesster + 1 ):
self.__TIDsNFreie.append( set() )
for T in Tische.TischListe: for T in Tische.TischListe:
n_freie = T.freiePlaetze() n_freie = T.freiePlaetze()
self.__TIDsNFreie[n_freie].add(T.Id) self.__TIDsNFreie[n_freie].add(T.Id)
def __repr__(self): def __repr__(self):
result = "\n-- Tische mit n Freien Plaetzen:\n" result = "\n-- Tische mit n Freien Plaetzen:\n"
for i in range(self.__Groesster+1): for i in range(self.__Groesster+1):
result = result + " " +repr(i) + " :" + repr(self.__TIDsNFreie[i]) + "\n" # .get statt [i], damit das Anzeigen keine leeren Eintraege
# ins defaultdict schreibt
result = result + " " +repr(i) + " :" + repr(self.__TIDsNFreie.get(i, set())) + "\n"
return result return result
def IdsnFreie(self, n_freie): def IdsnFreie(self, n_freie):
"""gibt ein Set mit allen Tischids zurueck, wo noch X freie Plaetze sind""" """gibt ein Set mit allen Tischids zurueck, wo noch X freie Plaetze sind"""
@@ -406,11 +412,13 @@ class Strafliste:
print("-- Strafliste laden") print("-- Strafliste laden")
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read( os.path.expanduser(FileName) ) config.read( os.path.expanduser(FileName) )
self.NichtNachbar = eval(config.get( 'Globals', 'NichtNachbar' )) # ast.literal_eval statt eval: die Werte sind reine Literale
self.Allein = eval(config.get( 'Globals', 'Allein' )) # (Zahlen und Dicts wie {2:-10}), also kein Code noetig.
self.TischWert = eval( config.get( 'Globals', 'TischWertigkeiten' ) ) self.NichtNachbar = ast.literal_eval(config.get( 'Globals', 'NichtNachbar' ))
self.Allein = ast.literal_eval(config.get( 'Globals', 'Allein' ))
self.TischWert = ast.literal_eval( config.get( 'Globals', 'TischWertigkeiten' ) )
for (k,v) in config.items( 'Trennung' ): for (k,v) in config.items( 'Trennung' ):
self.GruppenTrennen[int(k)] = eval(v) self.GruppenTrennen[int(k)] = ast.literal_eval(v)
# Sitzplatzverteilung, bestehend aus den Einzelelementen # Sitzplatzverteilung, bestehend aus den Einzelelementen
@@ -618,10 +626,6 @@ class Sitzplatzverteilung:
Teile.append( Teile.append(
'<circle cx="%g" cy="%g" r="%g" fill="white" ' '<circle cx="%g" cy="%g" r="%g" fill="white" '
'stroke="black" stroke-width="1.5"/>\n' % (Mx, My, TR) ) 'stroke="black" stroke-width="1.5"/>\n' % (Mx, My, TR) )
Teile.append(
'<text x="%g" y="%g" text-anchor="middle" '
'font-size="10" fill="black">Tisch %s</text>\n'
% (Mx, My - TR - 2, T.Id) )
Plaetze = list(T) Plaetze = list(T)
n = len(Plaetze) n = len(Plaetze)
@@ -636,6 +640,19 @@ class Sitzplatzverteilung:
'stroke="black" stroke-width="1"/>\n' 'stroke="black" stroke-width="1"/>\n'
% (Px, Py, PersonenRadius, Farbe) ) % (Px, Py, PersonenRadius, Farbe) )
# Tischnummer zentriert in die Tischmitte (nach den Personen-
# kreisen gezeichnet, damit sie oben liegt; ein halbtransparenter
# weisser Kreis darunter haelt sie auch ueber Personenkreisen
# lesbar, falls die Tischmitte belegt sein sollte)
Teile.append(
'<circle cx="%g" cy="%g" r="%g" fill="white" '
'fill-opacity="0.75"/>\n' % (Mx, My, PersonenRadius) )
Teile.append(
'<text x="%g" y="%g" text-anchor="middle" '
'dominant-baseline="central" font-size="%g" '
'font-weight="bold" fill="black">%s</text>\n'
% (Mx, My, PersonenRadius * 1.1, T.Nummer) )
Teile.append( '</svg>\n' ) Teile.append( '</svg>\n' )
fout = open( OutputSVG, 'w' ) fout = open( OutputSVG, 'w' )
@@ -897,11 +914,19 @@ class Sitzplatzverteilung:
Tisch = Tische.vonId(TischId) Tisch = Tische.vonId(TischId)
self.PersonSetzen( Person, Tisch ) self.PersonSetzen( Person, Tisch )
def TopX(self, Liste, Anzahl=-1 ): def TopX(self, Liste, Anzahl=-1 ):
"""gibt eine Liste mit X Ids zurueck, die die schlechtesten Wertungen haben""" """gibt eine Liste mit X Ids zurueck, die die schlechtesten Wertungen haben
Achtung: Dies ist bewusst *nicht* heapq.nsmallest. Der laufende
Schwellwert Min sinkt monoton mit, so dass nur Ids aufgenommen
werden, die waehrend der Iteration einen neuen Tiefpunkt erreichen
oder gleichziehen. Das Ergebnis haengt an der Iterationsreihenfolge
und praegt das Mutationsverhalten des GA (mutieren() ->
SchlechteGruppenTopX). Eine Umstellung auf heapq wuerde die
Loesungsfindung veraendern und ist daher bewusst unterlassen.
"""
Min = 0 Min = 0
RListe=[] RListe=[]
Wertigkeit=[] Wertigkeit=[]
# TODO filter verwenden
for Id, Wert in Liste.items(): for Id, Wert in Liste.items():
if Wert <= Min: if Wert <= Min:
RListe.append(Id) RListe.append(Id)
@@ -911,92 +936,112 @@ class Sitzplatzverteilung:
# Sitzplatzverteilung, bestehend aus den Einzelelementen # Sitzplatzverteilung, bestehend aus den Einzelelementen
class XMLConfig: class JSONConfig:
"""aus Xml file alles einlesen""" """liest die Gaesteliste aus einer bestellung.json ein
def getText(self, nodelist): (Nachfolger des frueheren XMLConfig; JSON hat die alte XML-Form
rc = "" abgeloest, siehe doc/dateiformate.md).
for node in nodelist:
if node.nodeType == node.TEXT_NODE: Erwartete Struktur:
rc = rc + node.data {
return rc "gruppen": [
def handlePersonNode( self, person ): {
"""Personendaten erfassen""" "name": "Familie Huber", # optional, nur Anzeige
info = {} "tisch": 1, # optional -> VIP-Fixplatz
try: "anzahl": 1, # optional -> Sammelbuchung
Titel = person.getElementsByTagName("Titel")[0] "personen": [
info["Titel"] = self.getText(Titel.childNodes) {"vorname": "Anna", "nachname": "Huber", "titel": ""}
except IndexError: ]
info["Titel"] = "" }
Vorname = person.getElementsByTagName("Vorname")[0] ]
Nachname = person.getElementsByTagName("Nachname")[0] }
info["Vorname"] = self.getText(Vorname.childNodes)
info["Nachname"] = self.getText(Nachname.childNodes) "tisch" macht die ganze Gruppe zu VIPs an diesem Tisch (laeuft
return info nicht durch die GA-Platzierung). "anzahl" > 1 vervielfacht jede
def HandleBestellung(self, Bestellung): Person der Gruppe in so viele identische Buchungen (anonyme
"""alle Bloecke in der XML Bestellung parsen""" Sammelbuchung). "titel" ist optional.
PidListe = [] """
VipHash = {} def HandleBestellung(self, Daten):
"""die geladene JSON-Struktur in Personen/Gruppen/VIPs zerlegen
Rueckgabe: [ PN, GN, VIPGruppe, VipHash, GruppenNamen ]
(wie CSVConfig; das fruehere XMLConfig gab die ersten vier
Elemente zurueck - Aufrufer, die GruppenNamen nicht brauchen,
koennen es ignorieren.)
"""
if not isinstance( Daten, dict ) or 'gruppen' not in Daten:
raise ValueError("bestellung.json braucht ein Objekt mit dem "
"Schluessel 'gruppen'")
PN = Personen() PN = Personen()
GN = Gruppen() GN = Gruppen()
VIPGruppe = Gruppe( 0 ) VIPGruppe = Gruppe( 0 )
VipHash = {}
GruppenNamen = {}
print('-HandleBestellung')
AlleGruppenXML = Bestellung.getElementsByTagName("Gruppe")
PID = 0 PID = 0
GID = 0 GID = 0
for GruppeXML in AlleGruppenXML: for Index, GruppeJSON in enumerate( Daten['gruppen'] ):
if 'personen' not in GruppeJSON:
raise ValueError("Gruppe %d: 'personen' fehlt" % (Index + 1))
IsVip = 'tisch' in GruppeJSON and GruppeJSON['tisch'] is not None
if IsVip:
try:
TischNr = int( GruppeJSON['tisch'] )
except (TypeError, ValueError):
raise ValueError("Gruppe %d: 'tisch' ist keine "
"Tischnummer" % (Index + 1))
try:
AnzahlReservierungen = int( GruppeJSON.get( 'anzahl', 1 ) )
except (TypeError, ValueError):
raise ValueError("Gruppe %d: 'anzahl' ist keine Zahl"
% (Index + 1))
if AnzahlReservierungen < 1:
raise ValueError("Gruppe %d: 'anzahl' muss >= 1 sein"
% (Index + 1))
# regulaere (nicht-VIP) Gruppen bekommen eine GID
if not IsVip:
GID = GID + 1 GID = GID + 1
print('-- GID ' + repr(GID))
G = Gruppe( GID ) G = Gruppe( GID )
GruppenNamen[ GID ] = GruppeJSON.get( 'name', 'Gruppe %d' % GID )
IsVip = False for PersonJSON in GruppeJSON['personen']:
AnzahlReservierungen = 1 Vorname = str( PersonJSON.get( 'vorname', '' ) ).strip()
for Node in GruppeXML.childNodes: Nachname = str( PersonJSON.get( 'nachname', '' ) ).strip()
if Node.nodeName == 'Tisch': Titel = str( PersonJSON.get( 'titel', '' ) ).strip()
TischNr = self.getText(Node.childNodes) if Vorname == '' or Nachname == '':
print('TischNr:', TischNr) raise ValueError("Gruppe %d: jede Person braucht Vorname "
IsVip = True "und Nachname" % (Index + 1))
if Node.nodeName == 'Anzahl': for i in range( AnzahlReservierungen ):
AnzahlReservierungen = int(self.getText(Node.childNodes))
print('Anzahl der Reservierungen:', AnzahlReservierungen)
IsContingent = True
for Node in GruppeXML.childNodes:
if Node.nodeName == 'Person':
NRes = 0
for i in range(int(AnzahlReservierungen)):
NRes = NRes + 1
PID = PID + 1 PID = PID + 1
print("PID =" + repr(PID))
info = self.handlePersonNode( Node )
Vorname = info["Vorname"]
Nachname = info["Nachname"]
Titel = info["Titel"]
P = Person( PID, Vorname, Nachname, Titel ) P = Person( PID, Vorname, Nachname, Titel )
if IsVip == True: if IsVip:
PidListe.append( int(PID) ) VipHash[ PID ] = TischNr
VipHash[ int(PID) ] = int(TischNr)
VIPGruppe.Person_dazu( P ) VIPGruppe.Person_dazu( P )
else: else:
PN.PersonAdd( P ) PN.PersonAdd( P )
G.Person_dazu( P ) G.Person_dazu( P )
if (G.Anzahl() > 0) & (IsVip != True):
if not IsVip:
if G.Anzahl() > 0:
GN.Gruppe_dazu( G ) GN.Gruppe_dazu( G )
if IsVip == True: else:
# leere Gruppe wieder verwerfen
del GruppenNamen[ GID ]
GID = GID - 1 GID = GID - 1
print(PN)
print(GN) return [ PN, GN, VIPGruppe, VipHash, GruppenNamen ]
print(VIPGruppe)
print(VipHash)
print('==========')
return [ PN, GN, VIPGruppe, VipHash ]
def laden(self, Bestellung): def laden(self, Bestellung):
"""lese alle Eingangsdaten aus einem XML File ein""" """lese alle Eingangsdaten aus einer JSON-Datei ein"""
fsock = open( Bestellung, 'r') import json
xmlini = minidom.parse(fsock) # utf-8-sig schluckt ein evtl. vorhandenes BOM
return self.HandleBestellung(xmlini) fsock = open( Bestellung, 'r', encoding='utf-8-sig' )
Daten = json.load( fsock )
fsock.close()
return self.HandleBestellung( Daten )
+185 -2
View File
@@ -6,9 +6,37 @@ from random import *
from itertools import chain from itertools import chain
from copy import deepcopy from copy import deepcopy
from functools import total_ordering from functools import total_ordering
import ast
import configparser, os import configparser, os
def _StringAusdruck(text):
"""wertet einen einfachen String-Ausdruck sicher aus.
zyklus.cfg nutzt kompakte Schreibweisen wie
'e,'+'s,z,m,j,z,'*3+'s'
also String-Literale, mit '+' verkettet und mit '*' vervielfacht.
ast.literal_eval kann das nicht (nur reine Literale), eval() waere
unsicher. Dieser Mini-Auswerter erlaubt ausschliesslich str-Literale,
ganze Zahlen sowie die Operatoren + und * - kein Funktionsaufruf,
keine Namen, kein beliebiger Code.
"""
knoten = ast.parse(text, mode='eval').body
def rechne(n):
if isinstance(n, ast.Constant) and isinstance(n.value, (str, int)):
return n.value
if isinstance(n, ast.BinOp) and isinstance(n.op, (ast.Add, ast.Mult)):
links, rechts = rechne(n.left), rechne(n.right)
return links + rechts if isinstance(n.op, ast.Add) else links * rechts
raise ValueError("unerlaubter Ausdruck in zyklus.cfg: %r" % text)
ergebnis = rechne(knoten)
if not isinstance(ergebnis, str):
raise ValueError("zyklus.cfg-Ausdruck ergibt keinen String: %r" % text)
return ergebnis
__author__ = "Michael Stangl" __author__ = "Michael Stangl"
__version__ = "$Revision: $" __version__ = "$Revision: $"
__date__ = "$Date: $" __date__ = "$Date: $"
@@ -65,24 +93,174 @@ class Zyklus:
ZyklusPath= os.path.join( FileName ) ZyklusPath= os.path.join( FileName )
config.read( ZyklusPath ) config.read( ZyklusPath )
self.Name = ZyklusName self.Name = ZyklusName
Abfolge = eval( config.get( ZyklusName, 'Abfolge' )) Abfolge = _StringAusdruck( config.get( ZyklusName, 'Abfolge' ))
Anzahl = eval( config.get( ZyklusName, 'Anzahl' )) Anzahl = _StringAusdruck( config.get( ZyklusName, 'Anzahl' ))
self.Abfolge = Abfolge.split(',') self.Abfolge = Abfolge.split(',')
self.Anzahl = Anzahl.split(',') self.Anzahl = Anzahl.split(',')
pass pass
class Entwicklung:
"""Beobachtungsobjekt: zeichnet den Verlauf der Loesungsfindung auf.
Nach jeder Zyklus-Aktion wird ein Messpunkt erfasst (bester,
durchschnittlicher und schlechtester Wert im aktuellen Pool).
alsSVG() zeichnet daraus eine Verlaufsgrafik: Zeitachse (Aktions-
index) nach rechts, Wertigkeit auf der Y-Achse. Domaenen-agnostisch;
funktioniert mit jeder Loesung, die ein Attribut .value hat.
"""
# Buchstaben der Zyklus-Abfolge -> lesbare Namen fuer die Beschriftung
AktionsNamen = {
'e': 'erzeugen', 'm': 'mutieren', 's': 'selektieren',
'S': 'zufaellig', 'j': 'behalten', 'z': 'Generation' }
def __init__(self):
# je Messpunkt: (Schrittindex, Aktion, bester, schnitt, schlechtester, N)
self.Punkte = []
def erfassen(self, Aktion, Pool):
"""einen Messpunkt aus dem aktuellen Pool aufnehmen"""
if not Pool:
return
Werte = [ L.value for L in Pool ]
self.Punkte.append( (
len(self.Punkte),
Aktion,
max(Werte),
sum(Werte) / len(Werte),
min(Werte),
len(Werte) ) )
def __repr__(self):
r = "-- Entwicklung (%d Messpunkte):\n" % len(self.Punkte)
for (i, Aktion, best, schnitt, schlecht, n) in self.Punkte:
r = r + (" %3d %-12s best=%s schnitt=%.1f schlecht=%s (N=%d)\n"
% (i, self.AktionsNamen.get(Aktion, Aktion),
best, schnitt, schlecht, n))
return r
def alsSVG(self, OutputSVG, Breite=900, Hoehe=500, Rand=60):
"""zeichnet den aufgezeichneten Verlauf als SVG-Liniendiagramm"""
if not self.Punkte:
raise ValueError("keine Messpunkte aufgezeichnet")
AlleWerte = []
for (_i, _a, best, schnitt, schlecht, _n) in self.Punkte:
AlleWerte.extend( (best, schnitt, schlecht) )
WMin, WMax = min(AlleWerte), max(AlleWerte)
if WMax == WMin:
WMax = WMin + 1 # flache Linie nicht auf den Rand kleben
N = len(self.Punkte)
PlotBreite = Breite - 2 * Rand
PlotHoehe = Hoehe - 2 * Rand
def X(i):
if N == 1:
return Rand + PlotBreite / 2
return Rand + PlotBreite * i / (N - 1)
def Y(wert):
# hoher (besserer) Wert oben, niedriger unten
return Rand + PlotHoehe * (WMax - wert) / (WMax - WMin)
def Polyline(Index, Farbe, Beschriftung):
punkte = ' '.join( '%.1f,%.1f' % (X(i), Y(p[Index]))
for i, p in enumerate(self.Punkte) )
return ('<polyline points="%s" fill="none" stroke="%s" '
'stroke-width="2"/>\n' % (punkte, Farbe), Beschriftung, Farbe)
Teile = []
Teile.append( '<?xml version="1.0" encoding="UTF-8"?>\n' )
Teile.append(
'<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" '
'viewBox="0 0 %d %d" font-family="sans-serif">\n'
% (Breite, Hoehe, Breite, Hoehe) )
Teile.append( '<rect width="100%" height="100%" fill="white"/>\n' )
Teile.append(
'<text x="%d" y="30" text-anchor="middle" font-size="16" '
'fill="#333">Entwicklung der Loesungsfindung</text>\n'
% (Breite // 2) )
# Achsen
Teile.append(
'<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#333" '
'stroke-width="1.5"/>\n' % (Rand, Rand, Rand, Hoehe - Rand) )
Teile.append(
'<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#333" '
'stroke-width="1.5"/>\n'
% (Rand, Hoehe - Rand, Breite - Rand, Hoehe - Rand) )
# Y-Gitter + Beschriftung (5 Linien)
for k in range(6):
wert = WMin + (WMax - WMin) * k / 5
y = Y(wert)
Teile.append(
'<line x1="%d" y1="%.1f" x2="%d" y2="%.1f" stroke="#eee" '
'stroke-width="1"/>\n' % (Rand, y, Breite - Rand, y) )
Teile.append(
'<text x="%d" y="%.1f" text-anchor="end" font-size="11" '
'fill="#666">%.0f</text>\n' % (Rand - 8, y + 4, wert) )
Teile.append(
'<text x="18" y="%d" text-anchor="middle" font-size="12" '
'fill="#333" transform="rotate(-90 18 %d)">Wertigkeit '
'(hoeher = besser)</text>\n' % (Hoehe // 2, Hoehe // 2) )
Teile.append(
'<text x="%d" y="%d" text-anchor="middle" font-size="12" '
'fill="#333">Zyklus-Aktion (Zeit)</text>\n'
% (Breite // 2, Hoehe - 18) )
# X-Beschriftung: Aktionsbuchstaben (bei wenigen Punkten alle,
# sonst ausgeduennt, damit die Achse lesbar bleibt)
Schritt = max(1, N // 30)
for i, p in enumerate(self.Punkte):
if i % Schritt == 0 or i == N - 1:
Teile.append(
'<text x="%.1f" y="%d" text-anchor="middle" font-size="10" '
'fill="#999">%s</text>\n' % (X(i), Hoehe - Rand + 16, p[1]) )
# die drei Kurven
Kurven = [
Polyline(2, '#2f6f4f', 'beste Loesung'),
Polyline(3, '#c08a2d', 'Durchschnitt'),
Polyline(4, '#a03030', 'schlechteste Loesung') ]
for (svg, _besch, _farbe) in Kurven:
Teile.append( svg )
# Legende oben rechts
LegX = Breite - Rand - 160
LegY = Rand + 10
for k, (_svg, besch, farbe) in enumerate(Kurven):
y = LegY + k * 18
Teile.append(
'<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="%s" '
'stroke-width="2"/>\n' % (LegX, y, LegX + 24, y, farbe) )
Teile.append(
'<text x="%d" y="%d" font-size="11" fill="#333">%s</text>\n'
% (LegX + 30, y + 4, besch) )
Teile.append( '</svg>\n' )
fout = open( OutputSVG, 'w' )
fout.writelines( Teile )
fout.close()
class Farm: class Farm:
"""enthaelt und pflegt Loesungsobjekte, kreuzt, mutiert, selektiert sie""" """enthaelt und pflegt Loesungsobjekte, kreuzt, mutiert, selektiert sie"""
def __init__(self, Zyklus, Klasse, *Arguments, **keywords): def __init__(self, Zyklus, Klasse, *Arguments, **keywords):
"""initialisiere die Farm """initialisiere die Farm
mit einem definerten Zyklus fuer die Zucht und dem gewunschten zu Zuchtobjekt mit einem definerten Zyklus fuer die Zucht und dem gewunschten zu Zuchtobjekt
Beobachtung: self.Entwicklung zeichnet nach jeder Zyklus-Aktion
den Pool-Zustand auf und laesst sich per alsSVG() visualisieren.
""" """
self.Position = 0 self.Position = 0
self.Pool = [] self.Pool = []
self.PoolNeu = [] self.PoolNeu = []
self.Zyklus = Zyklus self.Zyklus = Zyklus
self.Klasse = Klasse self.Klasse = Klasse
self.Entwicklung = Entwicklung()
for Aktion, Zahl in zip(Zyklus.Abfolge,Zyklus.Anzahl): for Aktion, Zahl in zip(Zyklus.Abfolge,Zyklus.Anzahl):
#print 'Aktion:',Aktion #print 'Aktion:',Aktion
#print 'Zahl:',Zahl #print 'Zahl:',Zahl
@@ -101,6 +279,11 @@ class Farm:
self.NeueGeneration() self.NeueGeneration()
else: else:
pass pass
# nach jeder Aktion den aktuellen Pool-Zustand aufzeichnen.
# Der beobachtete Pool ist der, in dem gerade die Loesungen
# liegen: PoolNeu, sobald dort etwas gesammelt wurde, sonst Pool.
self.Entwicklung.erfassen(
Aktion, self.PoolNeu if self.PoolNeu else self.Pool )
def LoesungenErzeugen(self, N, Klasse, *Arguments, **keywords): def LoesungenErzeugen(self, N, Klasse, *Arguments, **keywords):
""" Erzeuge Loesungen""" """ Erzeuge Loesungen"""
for i in range(N): for i in range(N):
+14 -5
View File
@@ -18,16 +18,21 @@ import optparse
if __name__ == '__main__': if __name__ == '__main__':
# Kommandozeilenoption --indir: Verzeichnis mit den Eingabedateien # Kommandozeilenoption --indir: Verzeichnis mit den Eingabedateien
# (tische.ini, bestellung.xml) und Ziel fuer die Ausgabedateien # (tische.ini, bestellung.json) und Ziel fuer die Ausgabedateien
# (TischePersonen.txt, PersonenTische.csv) # (TischePersonen.txt, PersonenTische.csv)
parser = optparse.OptionParser() parser = optparse.OptionParser()
parser.add_option( "--indir", dest="indir", default=None, parser.add_option( "--indir", dest="indir", default=None,
help="Verzeichnis mit tische.ini und bestellung.xml; " help="Verzeichnis mit tische.ini und bestellung.json; "
"dort werden auch TischePersonen.txt und PersonenTische.csv abgelegt" ) "dort werden auch TischePersonen.txt und PersonenTische.csv abgelegt" )
parser.add_option( "--tosvg", dest="tosvg", action="store_true", default=False, parser.add_option( "--tosvg", dest="tosvg", action="store_true", default=False,
help="zusaetzlich eine Sitzplatzverteilung.svg im --indir Verzeichnis " help="zusaetzlich eine Sitzplatzverteilung.svg im --indir Verzeichnis "
"erzeugen (Tische als weisse Kreise, Personen als farbige " "erzeugen (Tische als weisse Kreise, Personen als farbige "
"Kreise je Gruppe)" ) "Kreise je Gruppe)" )
parser.add_option( "--show-development", dest="showdev", action="store_true",
default=False,
help="zusaetzlich eine Entwicklung.svg im --indir Verzeichnis "
"erzeugen: Verlauf der Loesungsfindung (Zeitachse nach rechts, "
"beste/durchschnittliche/schlechteste Wertigkeit auf der Y-Achse)" )
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if options.indir is None: if options.indir is None:
@@ -51,12 +56,13 @@ if __name__ == '__main__':
# lade Eingabedateien # lade Eingabedateien
Tischconfig = os.path.join( InDir, 'tische.ini' ) Tischconfig = os.path.join( InDir, 'tische.ini' )
Bestellung = os.path.join( InDir, 'bestellung.xml' ) Bestellung = os.path.join( InDir, 'bestellung.json' )
# lade Ausgabedateien # lade Ausgabedateien
AusgabeTP= os.path.join( InDir, 'TischePersonen.txt' ) AusgabeTP= os.path.join( InDir, 'TischePersonen.txt' )
AusgabePT = os.path.join( InDir, 'PersonenTische.csv' ) AusgabePT = os.path.join( InDir, 'PersonenTische.csv' )
AusgabeSVG = os.path.join( InDir, 'Sitzplatzverteilung.svg' ) AusgabeSVG = os.path.join( InDir, 'Sitzplatzverteilung.svg' )
AusgabeEntwicklung = os.path.join( InDir, 'Entwicklung.svg' )
Z = Zyklus() Z = Zyklus()
Z.laden( Zyklusconfig, Zyklusart ) Z.laden( Zyklusconfig, Zyklusart )
@@ -68,8 +74,8 @@ if __name__ == '__main__':
TE.laden( Tischconfig ) TE.laden( Tischconfig )
print(TE) print(TE)
InputFile = XMLConfig() InputFile = JSONConfig()
[ PN, GN, VIPGruppe, VIPListePT ] = InputFile.laden( Bestellung ) [ PN, GN, VIPGruppe, VIPListePT, GruppenNamen ] = InputFile.laden( Bestellung )
print(PN) print(PN)
print(GN) print(GN)
print(VIPGruppe) print(VIPGruppe)
@@ -83,6 +89,9 @@ if __name__ == '__main__':
S.speichern( AusgabeTP, AusgabePT ) S.speichern( AusgabeTP, AusgabePT )
if options.tosvg: if options.tosvg:
S.alsSVG( AusgabeSVG ) S.alsSVG( AusgabeSVG )
if options.showdev:
F1.Entwicklung.alsSVG( AusgabeEntwicklung )
print( "Entwicklungsverlauf geschrieben:", AusgabeEntwicklung )
else: else:
pass pass
+376
View File
@@ -0,0 +1,376 @@
"""
Plattformunabhaengige Prozessverwaltung fuer die Web-Testinstanz
(ersetzt die fruehere PowerShell-Loesung run_web_helper.ps1, damit
dasselbe Skript unter Windows und Linux funktioniert).
Aufruf (ueber bin/run_web.bat bzw. bin/run_web.sh oder direkt):
python libs/webserver.py start # Server im Hintergrund starten
python libs/webserver.py stop # Server beenden
python libs/webserver.py status # laeuft er? PID? Port?
python libs/webserver.py run # im Vordergrund (Strg+C beendet)
python libs/webserver.py # wie "run"
--port N weicht vom Standardport 8000 ab
Designentscheidung gegen Zombie-Prozesse: Im Hintergrundbetrieb
("start") laeuft uvicorn OHNE --reload als ein einzelner Prozess,
der per PID-Datei sauber beendet werden kann. Der Auto-Reload
(--reload) startet Arbeitsprozesse als multiprocessing-Spawn-Kinder,
die bei unsanftem Ende des Reloaders MIT geerbtem Server-Socket
weiterleben und den Port unsichtbar weiterbedienen koennen (unter
Windows tauchen sie weder als Socket-Besitzer noch mit "uvicorn" in
der Kommandozeile auf). Deshalb gibt es --reload nur im
Vordergrundmodus ("run"), wo Strg+C alles gemeinsam beendet.
"""
__author__ = "Michael Stangl"
__copyright__ = "Copyright (c) 2026 Michael Stangl"
__license__ = "Python"
import argparse
import json
import os
import signal
import socket
import subprocess
import sys
import time
import urllib.request
BASISDIR = os.path.dirname( os.path.dirname( os.path.abspath( __file__ ) ) )
WEBDIR = os.path.join( BASISDIR, 'web' )
LOGDIR = os.path.join( BASISDIR, 'log' )
PIDDATEI = os.path.join( LOGDIR, 'run_web.pid' )
LOGDATEI = os.path.join( LOGDIR, 'run_web.log' )
HOST = '127.0.0.1'
STANDARDPORT = 8000
APPTITEL = 'Sitz & Platz' # steht im OpenAPI-Titel von web/main.py
# ---------------------------------------------------------------- Sonden
def PortOffen(Port):
"""True, wenn auf dem Port etwas Verbindungen annimmt"""
with socket.socket( socket.AF_INET, socket.SOCK_STREAM ) as s:
s.settimeout( 1.0 )
return s.connect_ex( (HOST, Port) ) == 0
def IstUnsereApp(Port):
"""True/False, ob auf dem Port unsere FastAPI-App antwortet;
None, wenn keine (verwertbare) Antwort kommt"""
try:
with urllib.request.urlopen(
'http://%s:%d/openapi.json' % (HOST, Port), timeout=3 ) as antwort:
daten = json.load( antwort )
return APPTITEL in daten.get( 'info', {} ).get( 'title', '' )
except Exception:
return None
def PidLebt(Pid):
"""True, wenn der Prozess existiert (portabel)"""
if Pid is None or Pid <= 0:
return False
if sys.platform == 'win32':
# os.kill(pid, 0) ist unter Windows unbrauchbar (wirft WinError 87
# statt zu pruefen). Stattdessen im Toolhelp-Snapshot nachsehen.
return Pid in _ProzessTabelle()
try:
os.kill( Pid, 0 )
return True
except PermissionError:
return True # existiert, gehoert aber jemand anderem
except OSError:
return False
# ------------------------------------------------------------- PID-Datei
def PidLesen():
try:
with open( PIDDATEI ) as f:
return int( f.read().strip() )
except (OSError, ValueError):
return None
def PidSchreiben(Pid):
os.makedirs( LOGDIR, exist_ok=True )
with open( PIDDATEI, 'w' ) as f:
f.write( '%d\n' % Pid )
def PidLoeschen():
try:
os.remove( PIDDATEI )
except OSError:
pass
def ListenerPid(Port):
"""PID des Prozesses, der auf dem Port lauscht (Fallback, wenn die
PID-Datei fehlt); None wenn nicht feststellbar"""
try:
if sys.platform == 'win32':
ausgabe = subprocess.run(
['netstat', '-ano', '-p', 'tcp'],
capture_output=True, text=True, timeout=10 ).stdout
for zeile in ausgabe.splitlines():
teile = zeile.split()
# Zustandsspalte NICHT auf "LISTENING" pruefen - netstat
# ist lokalisiert (deutsch: "ABHOEREN"). Ein Listener ist
# an der Gegenstelle 0.0.0.0:0 erkennbar; PID = letzte Spalte.
if ( len(teile) >= 5 and teile[0] == 'TCP'
and teile[1].endswith( ':%d' % Port )
and teile[2] in ('0.0.0.0:0', '[::]:0') ):
return int( teile[-1] )
else:
ausgabe = subprocess.run(
['ss', '-tlnp'],
capture_output=True, text=True, timeout=10 ).stdout
for zeile in ausgabe.splitlines():
if ':%d ' % Port in zeile and 'pid=' in zeile:
return int( zeile.split('pid=')[1].split(',')[0] )
except (OSError, ValueError, subprocess.SubprocessError):
pass
return None
# ------------------------------------------------- Prozessbaum-Hilfen
def _ProzessTabelle():
"""{ Pid: (ElternPid, Prozessname) } aller laufenden Prozesse -
Windows ueber einen Toolhelp-Snapshot (ctypes, Standardbibliothek),
Linux ueber /proc"""
tabelle = {}
if sys.platform == 'win32':
import ctypes
import ctypes.wintypes as wt
class PROCESSENTRY32(ctypes.Structure):
_fields_ = [
('dwSize', wt.DWORD), ('cntUsage', wt.DWORD),
('th32ProcessID', wt.DWORD),
('th32DefaultHeapID', ctypes.POINTER( ctypes.c_ulong )),
('th32ModuleID', wt.DWORD), ('cntThreads', wt.DWORD),
('th32ParentProcessID', wt.DWORD),
('pcPriClassBase', ctypes.c_long),
('dwFlags', wt.DWORD), ('szExeFile', ctypes.c_char * 260) ]
TH32CS_SNAPPROCESS = 0x2
k32 = ctypes.windll.kernel32
schnappschuss = k32.CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 )
if schnappschuss == -1:
return tabelle
eintrag = PROCESSENTRY32()
eintrag.dwSize = ctypes.sizeof( PROCESSENTRY32 )
if k32.Process32First( schnappschuss, ctypes.byref( eintrag ) ):
while True:
tabelle[ int(eintrag.th32ProcessID) ] = (
int(eintrag.th32ParentProcessID),
eintrag.szExeFile.decode( errors='replace' ).lower() )
if not k32.Process32Next( schnappschuss,
ctypes.byref( eintrag ) ):
break
k32.CloseHandle( schnappschuss )
else:
for name in os.listdir( '/proc' ):
if not name.isdigit():
continue
try:
with open( '/proc/%s/stat' % name ) as f:
stat = f.read()
# Format: pid (comm) zustand ppid ... - comm kann
# Leerzeichen enthalten, daher an der Klammer trennen
kommando = stat[ stat.index('(') + 1 : stat.rindex(')') ]
felder = stat[ stat.rindex(')') + 2 : ].split()
tabelle[ int(name) ] = ( int(felder[1]), kommando.lower() )
except (OSError, ValueError, IndexError):
continue
return tabelle
def ProzessWurzel(Pid):
"""laeuft vom Prozess die Elternkette hoch, solange die Eltern auch
Python-Prozesse sind, und liefert die Wurzel des Python-Baums.
Noetig fuer fremde/alte --reload-Server: wird nur der Arbeits-
prozess beendet, startet der Reloader ihn sofort neu."""
tabelle = _ProzessTabelle()
aktuell = Pid
for _ in range( 10 ): # Schutz gegen Zyklen
eintrag = tabelle.get( aktuell )
if eintrag is None:
break
eltern, _name = eintrag
elternEintrag = tabelle.get( eltern )
if elternEintrag is None or not elternEintrag[1].startswith( 'python' ):
break
aktuell = eltern
return aktuell
# ------------------------------------------------------------- Aktionen
def UvicornKommando(Port, Reload):
Kommando = [ sys.executable, '-m', 'uvicorn', 'main:app',
'--app-dir', WEBDIR, '--port', str(Port) ]
if Reload:
Kommando.append( '--reload' )
return Kommando
def Starten(Port):
if PortOffen( Port ):
if IstUnsereApp( Port ):
print( 'Server laeuft bereits: http://localhost:%d' % Port )
else:
print( 'FEHLER: Port %d ist von einem fremden Programm belegt.' % Port )
return 1
os.makedirs( LOGDIR, exist_ok=True )
logdatei = open( LOGDATEI, 'w' )
# vom Terminal abkoppeln, damit der Server das Schliessen der
# Konsole ueberlebt; eigene Prozessgruppe fuer sauberes Beenden
if sys.platform == 'win32':
flags = ( subprocess.DETACHED_PROCESS
| subprocess.CREATE_NEW_PROCESS_GROUP )
prozess = subprocess.Popen(
UvicornKommando( Port, Reload=False ),
stdout=logdatei, stderr=subprocess.STDOUT,
cwd=BASISDIR, creationflags=flags )
else:
prozess = subprocess.Popen(
UvicornKommando( Port, Reload=False ),
stdout=logdatei, stderr=subprocess.STDOUT,
cwd=BASISDIR, start_new_session=True )
PidSchreiben( prozess.pid )
# warten bis der Port antwortet (oder der Prozess frueh stirbt)
for _ in range( 30 ):
if prozess.poll() is not None:
print( 'FEHLER: Server sofort wieder beendet - siehe %s' % LOGDATEI )
PidLoeschen()
return 1
if PortOffen( Port ):
print( 'Server laeuft: http://localhost:%d (PID %d, Log: %s)'
% (Port, prozess.pid, os.path.relpath( LOGDATEI, BASISDIR )) )
print( 'Hinweis: Hintergrundbetrieb ohne Auto-Reload - fuer '
'Entwicklung mit Reload "run" im Vordergrund nutzen.' )
return 0
time.sleep( 0.5 )
print( 'FEHLER: Server antwortet nicht auf Port %d - siehe %s'
% (Port, LOGDATEI) )
return 1
def BaumBeenden(Pid):
"""beendet einen Prozess samt Kindprozessen (portabel)"""
if sys.platform == 'win32':
subprocess.run( ['taskkill', '/PID', str(Pid), '/T', '/F'],
capture_output=True )
else:
try:
os.killpg( os.getpgid( Pid ), signal.SIGTERM )
except (ProcessLookupError, PermissionError, OSError):
try:
os.kill( Pid, signal.SIGTERM )
except OSError:
pass
# kurze Gnadenfrist, dann hart nachsetzen
for _ in range( 10 ):
if not PidLebt( Pid ):
return
time.sleep( 0.3 )
try:
os.killpg( os.getpgid( Pid ), signal.SIGKILL )
except (ProcessLookupError, PermissionError, OSError):
pass
def Stoppen(Port):
gefunden = False
Pid = PidLesen()
if Pid is not None and PidLebt( Pid ):
BaumBeenden( Pid )
gefunden = True
# Fallback: PID-Datei fehlt/veraltet, aber es lauscht noch jemand -
# dann die Wurzel des Python-Prozessbaums beenden (sonst startet ein
# --reload-Elternprozess den abgeschossenen Worker sofort neu)
if PortOffen( Port ):
LPid = ListenerPid( Port )
if LPid is not None and PidLebt( LPid ):
BaumBeenden( ProzessWurzel( LPid ) )
gefunden = True
# nachpruefen, ob der Port wirklich frei geworden ist
for _ in range( 10 ):
if not PortOffen( Port ):
break
time.sleep( 0.3 )
PidLoeschen()
if PortOffen( Port ):
print( 'WARNUNG: Auf Port %d antwortet immer noch etwas. Moeglicher '
'verwaister Prozess mit geerbtem Socket - Kandidaten mit '
'"netstat -ano" (Windows) bzw. "ss -tlnp" (Linux) suchen.'
% Port )
return 1
print( 'Beendet.' if gefunden else 'Es lief nichts.' )
return 0
def Status(Port):
Pid = PidLesen()
if PortOffen( Port ):
Zusatz = ''
if Pid is not None and PidLebt( Pid ):
# uvicorn haelt den Port oft in einem Kindprozess des in der
# PID-Datei vermerkten Hauptprozesses - das ist derselbe Server
Zusatz = ' (PID %d)' % Pid
elif ( LPid := ListenerPid( Port ) ) is not None:
Zusatz = ' (Listener-PID %d, keine PID-Datei)' % LPid
if IstUnsereApp( Port ):
print( 'LAEUFT: http://localhost:%d%s' % (Port, Zusatz) )
return 0
print( 'BELEGT: Port %d antwortet, aber nicht mit unserer App%s'
% (Port, Zusatz) )
return 1
if Pid is not None and PidLebt( Pid ):
print( 'UNKLAR: Prozess %d aus der PID-Datei lebt, aber Port %d '
'antwortet nicht (Startphase oder haengt?)' % (Pid, Port) )
return 1
print( 'GESTOPPT: kein Server auf Port %d' % Port )
return 1
def Vordergrund(Port):
print( 'Starte Web-Testinstanz im Vordergrund mit Auto-Reload '
'(Strg+C zum Beenden) ...' )
try:
return subprocess.call( UvicornKommando( Port, Reload=True ),
cwd=BASISDIR )
except KeyboardInterrupt:
return 0
# ------------------------------------------------------------------ Main
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='verwaltet die lokale Web-Testinstanz (FastAPI/uvicorn)' )
parser.add_argument( 'aktion', nargs='?', default='run',
choices=['start', 'stop', 'status', 'run'],
help='start=Hintergrund, stop=beenden, status=anzeigen, '
'run=Vordergrund mit Auto-Reload (Standard)' )
parser.add_argument( '--port', type=int, default=STANDARDPORT,
help='Port der Testinstanz (Standard: %d)' % STANDARDPORT )
argumente = parser.parse_args()
if argumente.aktion == 'start':
sys.exit( Starten( argumente.port ) )
elif argumente.aktion == 'stop':
sys.exit( Stoppen( argumente.port ) )
elif argumente.aktion == 'status':
sys.exit( Status( argumente.port ) )
else:
sys.exit( Vordergrund( argumente.port ) )
+5 -6
View File
@@ -1,8 +1,7 @@
# Python-Abhaengigkeiten fuer platz # Python-Abhaengigkeiten fuer platz
# #
# Aktuell werden nur Module aus der Standardbibliothek verwendet # Der Kern (libs/) verwendet nur die Standardbibliothek.
# (configparser, xml.dom.minidom, random, ...). # Die folgenden Pakete braucht nur die Web-API (web/main.py, Roadmap M1):
# Keine externen Pakete erforderlich. fastapi>=0.110
# uvicorn[standard]>=0.29
# Beispiel: python-multipart>=0.0.9
# requests==2.31.0
+106 -1
View File
@@ -18,7 +18,8 @@ import unittest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'libs')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'libs'))
from Strukturdaten import Gruppe, Gruppen, Person, Sitzplatzverteilung, Strafliste, Tisch, Tische from Strukturdaten import Gruppe, Gruppen, JSONConfig, Person, Sitzplatzverteilung, Strafliste, Tisch, Tische
from ga import Entwicklung, _StringAusdruck
IMAGES_DIR = os.path.join(os.path.dirname(__file__), '..', 'doc', 'images') IMAGES_DIR = os.path.join(os.path.dirname(__file__), '..', 'doc', 'images')
@@ -169,5 +170,109 @@ class TestSechsTischeKreisDreiergruppen(unittest.TestCase):
"Tische %s der Gruppe %s sind keine Nachbarn" % (SV.Gid_Tid[G.Id], G.Id) ) "Tische %s der Gruppe %s sind keine Nachbarn" % (SV.Gid_Tid[G.Id], G.Id) )
class TestJSONConfig(unittest.TestCase):
"""Parsen der bestellung.json: Gruppen, VIP-Bindung, Anzahl-Expansion."""
def test_gruppen_vips_und_sammelbuchung(self):
Daten = {
"gruppen": [
{
"name": "Brautpaar",
"tisch": 1,
"personen": [
{"vorname": "Julia", "nachname": "Brandt"},
{"vorname": "Daniel", "nachname": "Sommer", "titel": "Dr."},
],
},
{
"name": "Familie Huber",
"personen": [
{"vorname": "Anna", "nachname": "Huber"},
{"vorname": "Berta", "nachname": "Huber"},
],
},
{
"name": "Musikverein",
"anzahl": 5,
"personen": [ {"vorname": "Gast", "nachname": "Musikverein"} ],
},
]
}
PN, GN, VIPGruppe, VipHash, GruppenNamen = JSONConfig().HandleBestellung(Daten)
# VIP-Gruppe: beide ans Tisch 1 gebunden, nicht in den GA-Gruppen
self.assertEqual( VIPGruppe.Anzahl(), 2 )
self.assertEqual( set(VipHash.values()), {1} )
self.assertEqual( len(VipHash), 2 )
# regulaere Gruppen: Familie Huber (2) + Musikverein (5x expandiert) = 7
self.assertEqual( GN.NLeute, 7 )
self.assertEqual( len(GN.GruppenListe), 2 )
# Sammelbuchung: die eine Musikverein-Person wurde 5x vervielfacht
Groessen = sorted( G.Anzahl() for G in GN.GruppenListe )
self.assertEqual( Groessen, [2, 5] )
# Anzeigenamen kommen durch
self.assertIn( "Familie Huber", GruppenNamen.values() )
self.assertIn( "Musikverein", GruppenNamen.values() )
def test_fehlende_pflichtfelder_werfen_fehler(self):
with self.assertRaises(ValueError):
JSONConfig().HandleBestellung( {"keine_gruppen": []} )
with self.assertRaises(ValueError):
JSONConfig().HandleBestellung(
{"gruppen": [ {"personen": [ {"vorname": "Nur"} ]} ]} )
class TestStringAusdruck(unittest.TestCase):
"""Der sichere Zyklus-Ausdruck-Auswerter (Ersatz fuer eval)."""
def test_verkettung_und_vervielfachung(self):
self.assertEqual(
_StringAusdruck("'e,'+'s,z,m,j,z,'*3+'s'"),
'e,s,z,m,j,z,s,z,m,j,z,s,z,m,j,z,s' )
self.assertEqual( _StringAusdruck("'e,s'"), 'e,s' )
def test_lehnt_code_ab(self):
# kein Funktionsaufruf, keine Namen, kein Import
for boese in ("__import__('os')", "open('x')", "1+1", "a+'b'"):
with self.assertRaises(ValueError):
_StringAusdruck(boese)
class TestEntwicklung(unittest.TestCase):
"""Das Beobachtungsobjekt zeichnet den Verlauf auf und rendert SVG."""
class _L:
def __init__(self, value):
self.value = value
def test_erfassen_und_svg(self):
E = Entwicklung()
E.erfassen( 'e', [self._L(-200), self._L(-150), self._L(-90)] )
E.erfassen( 's', [self._L(-90), self._L(-70)] )
E.erfassen( 'm', [self._L(-70), self._L(-40)] )
self.assertEqual( len(E.Punkte), 3 )
# je Punkt: (index, aktion, best, schnitt, schlecht, N)
_i, aktion, best, schnitt, schlecht, n = E.Punkte[0]
self.assertEqual( aktion, 'e' )
self.assertEqual( best, -90 )
self.assertEqual( schlecht, -200 )
self.assertEqual( n, 3 )
Pfad = os.path.join( IMAGES_DIR, 'test_entwicklung.svg' )
E.alsSVG( Pfad )
with open(Pfad) as f:
svg = f.read()
self.assertIn( '<svg', svg )
self.assertEqual( svg.count('<polyline'), 3 ) # best/schnitt/schlecht
def test_leere_entwicklung_wirft_beim_svg(self):
with self.assertRaises(ValueError):
Entwicklung().alsSVG( os.path.join(IMAGES_DIR, 'leer.svg') )
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+470
View File
@@ -0,0 +1,470 @@
"""
REST-API um den bestehenden Sitzplatz-Kern (Meilenstein 1 der Roadmap).
Session-basiert, nichts wird dauerhaft gespeichert: jede Sitzung lebt
im Speicher des Servers und verfaellt nach 24 Stunden (TTL) — bewusst
ohne Accounts/DB, siehe Roadmap M1 ("schont Datenschutz-Aufwand").
Endpunkte (siehe auch /docs — interaktive Swagger-UI von FastAPI):
POST /sitzung — legt eine Arbeits-Session an
GET /sitzung/{id} — Status der Session
POST /sitzung/{id}/tische — Tisch-Layout als JSON (Ersatz tische.ini)
POST /sitzung/{id}/personen — Gaesteliste als CSV-Upload (Ersatz bestellung.json)
POST /sitzung/{id}/berechnen — startet die GA-Optimierung
GET /sitzung/{id}/ergebnis — Ergebnis als JSON (Tische -> Personen)
GET /sitzung/{id}/ergebnis.svg — Ergebnis als SVG (alsSVG aus dem Kern)
Start (lokale Testinstanz, siehe bin/run_web.bat / bin/run_web.sh):
python -m uvicorn main:app --reload --app-dir web
"""
import ast
import math
import os
import sys
import tempfile
import threading
import time
import uuid
# Projektwurzel und Pfade robust aus der eigenen Lage ableiten, damit der
# Server auch ohne vorheriges setenv laeuft (PLATZ_* hat Vorrang, falls gesetzt)
BASISDIR = os.path.dirname( os.path.dirname( os.path.abspath( __file__ ) ) )
LIBSDIR = os.environ.get( 'PLATZ_LIBS', os.path.join( BASISDIR, 'libs' ) )
CFGDIR = os.environ.get( 'PLATZ_CFG', os.path.join( BASISDIR, 'cfg' ) )
if LIBSDIR not in sys.path:
sys.path.insert( 0, LIBSDIR )
from fastapi import FastAPI, File, HTTPException, UploadFile
from fastapi.responses import Response
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field
from ga import Farm, Zyklus
from CSVConfig import CSVConfig
from Strukturdaten import Sitzplatzverteilung, Strafliste, Tisch, Tische
app = FastAPI(
title='Sitz & Platz — API',
description='Sitzplatz-Optimierung per genetischem Algorithmus '
'(Meilenstein 1: Session-basiert, ohne Accounts)',
version='0.1.0' )
# ---------------------------------------------------------------- GA-Konfig
# Zyklus und Strafliste einmal beim Start laden (wie in libs/platz.py)
import configparser
_PlatzCfg = configparser.ConfigParser()
_PlatzCfg.read( os.path.join( CFGDIR, 'platz.cfg' ) )
ZYKLUSDATEI = os.path.expandvars( os.path.normpath(
_PlatzCfg.get( 'Config', 'Zyklusdatei',
fallback=os.path.join( CFGDIR, 'zyklus.cfg' ) ) ) )
ZYKLUSART = _PlatzCfg.get( 'Config', 'Zyklusart', fallback='Easy' )
STRAFENDATEI = os.path.expandvars( os.path.normpath(
_PlatzCfg.get( 'Config', 'Strafendatei',
fallback=os.path.join( CFGDIR, 'strafen.cfg' ) ) ) )
if not os.path.isfile( ZYKLUSDATEI ):
ZYKLUSDATEI = os.path.join( CFGDIR, 'zyklus.cfg' )
if not os.path.isfile( STRAFENDATEI ):
STRAFENDATEI = os.path.join( CFGDIR, 'strafen.cfg' )
GAZyklus = Zyklus()
GAZyklus.laden( ZYKLUSDATEI, ZYKLUSART )
Strafen = Strafliste()
Strafen.laden( STRAFENDATEI )
# ---------------------------------------------------------------- Sessions
SESSION_TTL_SEKUNDEN = 24 * 3600
Sessions = {}
SessionsLock = threading.Lock()
def _SessionsAufraeumen():
"""Sessions loeschen, deren TTL abgelaufen ist (Datenschutz per Design)"""
Jetzt = time.time()
with SessionsLock:
Abgelaufen = [ Sid for Sid, S in Sessions.items()
if Jetzt - S['erzeugt'] > SESSION_TTL_SEKUNDEN ]
for Sid in Abgelaufen:
del Sessions[Sid]
def _GibSession(SitzungsId):
_SessionsAufraeumen()
with SessionsLock:
S = Sessions.get( SitzungsId )
if S is None:
raise HTTPException( status_code=404,
detail='Sitzung nicht gefunden oder abgelaufen (TTL 24h)' )
return S
# ------------------------------------------------------------ API-Modelle
class TischEingabe(BaseModel):
"""ein Tisch aus dem Frontend (Ersatz fuer eine tische.ini-Section)"""
id: int | None = Field( default=None, description='Tisch-Id; leer = fortlaufend' )
nummer: int | None = Field( default=None, description='Anzeigenummer; leer = wie Id' )
hof: int = Field( default=1, description='Hof/Raum-Nummer' )
plaetze: int = Field( gt=0, le=100, description='Anzahl Sitzplaetze' )
x: float = Field( description='X-Koordinate im Raster' )
y: float = Field( description='Y-Koordinate im Raster' )
nachbarn: list[int] | None = Field( default=None,
description='explizite Nachbar-Tisch-Ids; leer = aus Naehe ableiten' )
class TischLayout(BaseModel):
tische: list[TischEingabe] = Field( min_length=1 )
nachbarRadius: float = Field( default=1.5, gt=0,
description='max. Rasterabstand, bis zu dem zwei Tische als '
'Nachbarn gelten (nur fuer Tische ohne explizite Liste)' )
# --------------------------------------------------------------- Hilfen
def _NachbarnAbleiten(TischListe, Radius):
"""Nachbarliste aus raeumlicher Naehe der Koordinaten ableiten
(Roadmap M1: Ersatz fuer manuelle Nachbarliste-Pflege).
Explizit angegebene Nachbarn bleiben erhalten und werden
beidseitig ergaenzt."""
Nachbarn = { T.id: set( T.nachbarn or [] ) for T in TischListe }
for T1 in TischListe:
for T2 in TischListe:
if T1.id == T2.id:
continue
if T1.nachbarn is None or T2.nachbarn is None:
Abstand = math.dist( (T1.x, T1.y), (T2.x, T2.y) )
if Abstand <= Radius:
Nachbarn[T1.id].add( T2.id )
Nachbarn[T2.id].add( T1.id )
# expliziten Angaben beidseitig machen
for Tid, Menge in Nachbarn.items():
for NachbarId in Menge:
if NachbarId in Nachbarn:
Nachbarn[NachbarId].add( Tid )
return { Tid: sorted(Menge) for Tid, Menge in Nachbarn.items() }
def _TischeBauen(S):
"""baut aus dem gespeicherten Layout frische Tisch-Objekte
(pro Berechnung neu, damit keine Reste vorheriger Laeufe kleben)"""
TE = Tische( [] )
for T in S['tische']:
TE.Tisch_dazu( Tisch(
T['id'],
Plaetze=T['plaetze'],
Koordinaten=( T['x'], T['y'] ),
Nachbarliste=T['nachbarn'],
Hof=T['hof'],
Nummer=T['nummer'] ) )
return TE
def _ErgebnisJSON(S):
"""baut die JSON-Darstellung des besten Ergebnisses einer Session"""
SV = S['ergebnis']
GruppenNamen = S.get( 'gruppenNamen', {} )
def GruppeVonPerson(P):
try:
G = SV.Gruppen.GruppeVonPid( P.Id )
return GruppenNamen.get( G.Id, 'Gruppe %s' % G.Id )
except KeyError:
return 'VIP'
TischListe = []
for T in SV.Tische:
Personen = [ {
'titel': P._Titel,
'vorname': P._Vorname,
'nachname': P._Name,
'gruppe': GruppeVonPerson( P ),
} for P in T ]
Personen.sort( key=lambda p: (p['gruppe'], p['nachname'], p['vorname']) )
TischListe.append( {
'id': T.Id,
'nummer': T.Nummer,
'hof': T.Hof,
'plaetze': T.Plaetze,
'besetzt': T.besetztePlaetze(),
'frei': T.freiePlaetze(),
'wert': SV.TischWertGet( T ),
'personen': Personen,
} )
TischListe.sort( key=lambda t: t['id'] )
GruppenListe = []
for G in SV.Gruppen:
GruppenListe.append( {
'id': G.Id,
'name': GruppenNamen.get( G.Id, 'Gruppe %s' % G.Id ),
'anzahl': G.Anzahl(),
'teilungen': SV.GruppenTeilungGet( G ),
'wert': SV.GruppenWertGet( G ),
} )
GruppenListe.sort( key=lambda g: g['id'] )
return {
'wertigkeit': SV.value,
'variante': S['varianten'],
'tische': TischListe,
'gruppen': GruppenListe,
}
# --------------------------------------------------------------- Endpunkte
@app.post( '/sitzung', status_code=201 )
def SitzungAnlegen():
"""legt eine neue Arbeits-Session an (TTL 24h, nur im Speicher)"""
_SessionsAufraeumen()
SitzungsId = uuid.uuid4().hex
with SessionsLock:
Sessions[SitzungsId] = {
'erzeugt': time.time(),
'tische': None,
'personen': None,
'ergebnis': None,
'varianten': 0,
}
return { 'id': SitzungsId, 'ttlStunden': SESSION_TTL_SEKUNDEN // 3600 }
@app.get( '/sitzung/{SitzungsId}' )
def SitzungStatus(SitzungsId: str):
"""Status: was ist schon da, was fehlt noch"""
S = _GibSession( SitzungsId )
return {
'id': SitzungsId,
'tische': len( S['tische'] ) if S['tische'] else 0,
'personen': S['personen']['nLeute'] if S['personen'] else 0,
'vips': S['personen']['nVips'] if S['personen'] else 0,
'berechnet': S['ergebnis'] is not None,
'varianten': S['varianten'],
}
@app.post( '/sitzung/{SitzungsId}/tische' )
def TischeSetzen(SitzungsId: str, Layout: TischLayout):
"""Tisch-Layout uebernehmen (JSON-Ersatz fuer tische.ini);
Nachbarschaft wird aus raeumlicher Naehe abgeleitet"""
S = _GibSession( SitzungsId )
# Ids vergeben/pruefen
NaechsteId = 1
Vergeben = set()
for T in Layout.tische:
if T.id is None:
while NaechsteId in Vergeben:
NaechsteId = NaechsteId + 1
T.id = NaechsteId
if T.id in Vergeben:
raise HTTPException( status_code=400,
detail='Tisch-Id %d ist doppelt' % T.id )
Vergeben.add( T.id )
Nachbarn = _NachbarnAbleiten( Layout.tische, Layout.nachbarRadius )
S['tische'] = [ {
'id': T.id,
'nummer': T.nummer if T.nummer is not None else T.id,
'hof': T.hof,
'plaetze': T.plaetze,
'x': T.x,
'y': T.y,
'nachbarn': Nachbarn[T.id],
} for T in Layout.tische ]
S['ergebnis'] = None
S['varianten'] = 0
return {
'tische': S['tische'],
'plaetzeGesamt': sum( T['plaetze'] for T in S['tische'] ),
}
@app.post( '/sitzung/{SitzungsId}/tische-datei' )
def TischDateiHochladen(SitzungsId: str, datei: UploadFile = File(...)):
"""bestehende tische.ini hochladen (gleiches Format wie fuer die CLI,
z.B. work/hochzeit/tische.ini); die Nachbarliste aus der Datei
wird unveraendert uebernommen, nichts wird neu abgeleitet"""
S = _GibSession( SitzungsId )
Rohdaten = datei.file.read()
try:
Text = Rohdaten.decode( 'utf-8-sig' )
except UnicodeDecodeError:
Text = Rohdaten.decode( 'cp1252' )
config = configparser.ConfigParser()
try:
config.read_string( Text )
except configparser.Error as Fehler:
raise HTTPException( status_code=400,
detail='tische.ini nicht lesbar: %s' % Fehler )
if not config.sections():
raise HTTPException( status_code=400,
detail='tische.ini enthaelt keine Tisch-Sektionen ([1], [2], ...)' )
# gleiche Feldsemantik wie Tische.laden() in Strukturdaten.py,
# aber mit ast.literal_eval statt eval (Upload = fremde Eingabe)
TischListe = []
for Sektion in config.sections():
try:
Tid = int( Sektion )
Plaetze = int( ast.literal_eval( config.get( Sektion, 'Plaetze' ) ) )
Koords = ast.literal_eval( config.get( Sektion, 'Koordinaten' ) )
Nachbarn = list( ast.literal_eval(
config.get( Sektion, 'Nachbarliste' ) ) )
Nummer = int( ast.literal_eval(
config.get( Sektion, 'Nummer', fallback=Sektion ) ) )
Hof = int( ast.literal_eval(
config.get( Sektion, 'Hof', fallback='1' ) ) )
except (ValueError, SyntaxError, configparser.Error) as Fehler:
raise HTTPException( status_code=400,
detail='tische.ini, Sektion [%s]: %s' % (Sektion, Fehler) )
if Plaetze < 1:
raise HTTPException( status_code=400,
detail='tische.ini, Sektion [%s]: Plaetze muss >= 1 sein'
% Sektion )
TischListe.append( {
'id': Tid,
'nummer': Nummer,
'hof': Hof,
'plaetze': Plaetze,
'x': float( Koords[0] ),
'y': float( Koords[1] ),
'nachbarn': sorted( int(N) for N in Nachbarn ),
} )
Ids = [ T['id'] for T in TischListe ]
if len( Ids ) != len( set( Ids ) ):
raise HTTPException( status_code=400,
detail='tische.ini enthaelt doppelte Tisch-Ids' )
S['tische'] = TischListe
S['ergebnis'] = None
S['varianten'] = 0
return {
'tische': S['tische'],
'plaetzeGesamt': sum( T['plaetze'] for T in S['tische'] ),
}
@app.post( '/sitzung/{SitzungsId}/personen' )
def PersonenHochladen(SitzungsId: str, datei: UploadFile = File(...)):
"""Gaesteliste als CSV hochladen (Ersatz fuer bestellung.json);
Format siehe libs/CSVConfig.py"""
S = _GibSession( SitzungsId )
Rohdaten = datei.file.read()
try:
Text = Rohdaten.decode( 'utf-8-sig' )
except UnicodeDecodeError:
Text = Rohdaten.decode( 'cp1252' ) # Excel/Windows-Fallback
try:
[ PN, GN, VIPGruppe, VipHash, GruppenNamen ] = \
CSVConfig().HandleGaesteliste( Text )
except ValueError as Fehler:
raise HTTPException( status_code=400, detail=str(Fehler) )
S['personen'] = {
'PN': PN,
'GN': GN,
'VIPGruppe': VIPGruppe,
'VipHash': VipHash,
'nLeute': GN.NLeute,
'nVips': VIPGruppe.Anzahl(),
}
S['gruppenNamen'] = GruppenNamen
S['ergebnis'] = None
S['varianten'] = 0
return {
'personen': GN.NLeute,
'vips': VIPGruppe.Anzahl(),
'gruppen': [ {
'id': G.Id,
'name': GruppenNamen.get( G.Id, 'Gruppe %s' % G.Id ),
'anzahl': G.Anzahl(),
} for G in GN ],
}
@app.post( '/sitzung/{SitzungsId}/berechnen' )
def Berechnen(SitzungsId: str):
"""startet die GA-Optimierung; jeder Aufruf erzeugt eine neue Variante"""
S = _GibSession( SitzungsId )
if S['tische'] is None:
raise HTTPException( status_code=400,
detail='Erst das Tisch-Layout setzen (POST .../tische)' )
if S['personen'] is None:
raise HTTPException( status_code=400,
detail='Erst die Gaesteliste hochladen (POST .../personen)' )
TE = _TischeBauen( S )
P = S['personen']
# Vorab-Pruefungen mit klaren Fehlermeldungen (der Kern wirft sonst
# nur IndexError/AttributeError tief aus der Platzierung heraus)
GesamtLeute = P['nLeute'] + P['nVips']
if GesamtLeute > TE.NStuehle:
raise HTTPException( status_code=400,
detail='Zu wenig Stuehle: %d Personen, aber nur %d Plaetze'
% (GesamtLeute, TE.NStuehle) )
VIPsProTisch = {}
for Pid, Tid in P['VipHash'].items():
VIPsProTisch[Tid] = VIPsProTisch.get( Tid, 0 ) + 1
for Tid, AnzahlVIPs in VIPsProTisch.items():
T = TE.vonId( Tid )
if T is None:
raise HTTPException( status_code=400,
detail='VIP-Tisch %d existiert nicht im Layout' % Tid )
if AnzahlVIPs > T.Plaetze:
raise HTTPException( status_code=400,
detail='Tisch %d hat %d Plaetze, aber %d VIPs sind '
'dort fest gesetzt' % (Tid, T.Plaetze, AnzahlVIPs) )
try:
GAFarm = Farm( GAZyklus, Sitzplatzverteilung,
Tische=TE, Gruppen=P['GN'], Strafen=Strafen,
VIPListe=P['VipHash'], VIPs=P['VIPGruppe'] )
except IndexError as Fehler:
raise HTTPException( status_code=400, detail=str(Fehler) )
S['ergebnis'] = GAFarm.Bester()
S['varianten'] = S['varianten'] + 1
return _ErgebnisJSON( S )
@app.get( '/sitzung/{SitzungsId}/ergebnis' )
def Ergebnis(SitzungsId: str):
"""das beste bisher berechnete Ergebnis als JSON"""
S = _GibSession( SitzungsId )
if S['ergebnis'] is None:
raise HTTPException( status_code=404,
detail='Noch nichts berechnet (POST .../berechnen)' )
return _ErgebnisJSON( S )
@app.get( '/sitzung/{SitzungsId}/ergebnis.svg' )
def ErgebnisSVG(SitzungsId: str):
"""das beste bisher berechnete Ergebnis als SVG (alsSVG aus dem Kern)"""
S = _GibSession( SitzungsId )
if S['ergebnis'] is None:
raise HTTPException( status_code=404,
detail='Noch nichts berechnet (POST .../berechnen)' )
# alsSVG schreibt in eine Datei -> Tempdatei schreiben, lesen, loeschen
Handle, TempPfad = tempfile.mkstemp( suffix='.svg' )
os.close( Handle )
try:
S['ergebnis'].alsSVG( TempPfad )
fsock = open( TempPfad, 'r' )
SVG = fsock.read()
fsock.close()
finally:
os.remove( TempPfad )
return Response( content=SVG, media_type='image/svg+xml' )
# statisches Test-Frontend unter / (muss nach den API-Routen gemountet sein)
app.mount( '/', StaticFiles(
directory=os.path.join( os.path.dirname( os.path.abspath( __file__ ) ),
'static' ),
html=True ), name='static' )
+329
View File
@@ -0,0 +1,329 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sitz &amp; Platz — Testoberfläche (M1)</title>
<style>
:root {
--akzent: #2f6f4f;
--rand: #d9d4c8;
--hintergrund: #faf8f3;
}
* { box-sizing: border-box; }
body {
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
margin: 0; background: var(--hintergrund); color: #2a2a24;
line-height: 1.5;
}
header {
background: var(--akzent); color: white;
padding: 1rem 1.5rem;
}
header h1 { margin: 0; font-size: 1.3rem; }
header p { margin: .2rem 0 0; opacity: .85; font-size: .85rem; }
main { max-width: 60rem; margin: 0 auto; padding: 1rem 1.5rem 4rem; }
section {
background: white; border: 1px solid var(--rand);
border-radius: 8px; padding: 1rem 1.25rem; margin-top: 1.25rem;
}
h2 { font-size: 1.05rem; margin: 0 0 .75rem; color: var(--akzent); }
table { border-collapse: collapse; width: 100%; font-size: .9rem; }
th, td { border: 1px solid var(--rand); padding: .3rem .5rem; text-align: left; }
th { background: #f1eee6; }
input[type=number] { width: 4.5rem; }
textarea {
width: 100%; min-height: 11rem; font-family: ui-monospace, monospace;
font-size: .85rem; border: 1px solid var(--rand); border-radius: 4px;
padding: .5rem;
}
button {
background: var(--akzent); color: white; border: 0;
border-radius: 4px; padding: .45rem .9rem; font-size: .9rem;
cursor: pointer; margin: .25rem .35rem .25rem 0;
}
button.sekundaer { background: #8b8577; }
button:disabled { background: #b9b4a6; cursor: not-allowed; }
.status { font-size: .85rem; margin-top: .5rem; min-height: 1.2rem; }
.status.ok { color: var(--akzent); }
.status.fehler { color: #a03030; white-space: pre-wrap; }
#svgBild { max-width: 100%; border: 1px solid var(--rand); border-radius: 4px;
margin-top: .75rem; background: white; }
.kennzahl { display: inline-block; background: #f1eee6; border-radius: 4px;
padding: .3rem .7rem; margin: .15rem .3rem .15rem 0; font-size: .9rem; }
.kennzahl b { color: var(--akzent); }
.hinweis { font-size: .8rem; color: #6b6557; }
</style>
</head>
<body>
<header>
<h1>Sitz &amp; Platz <small style="font-weight:normal">— Testoberfläche (Meilenstein 1)</small></h1>
<p>Session-basiert, nichts wird dauerhaft gespeichert (TTL 24 h). Sitzung: <code id="sitzungsId">wird angelegt…</code></p>
</header>
<main>
<section>
<h2>1. Tische definieren</h2>
<p class="hinweis">Koordinaten sind Rasterpositionen (wie in tische.ini). Tische, die
näher als der Nachbar-Radius (1,5 Rastereinheiten) beieinander liegen, gelten
automatisch als Nachbarn — Gruppen, die geteilt werden müssen, kommen bevorzugt
an Nachbartische.</p>
<table id="tischTabelle">
<thead>
<tr><th>Tisch-Nr.</th><th>Plätze</th><th>X</th><th>Y</th><th></th></tr>
</thead>
<tbody></tbody>
</table>
<button type="button" onclick="tischZeile()">+ Tisch hinzufügen</button>
<button type="button" class="sekundaer" onclick="presetReihe()">Preset: 4er-Reihe</button>
<button type="button" class="sekundaer" onclick="presetRaster()">Preset: 3×3-Raster</button>
<br>
<label class="hinweis">Oder eine bestehende <code>tische.ini</code> laden
(z.&nbsp;B. aus <code>work/hochzeit</code>):</label>
<input type="file" id="tischDatei" accept=".ini,text/plain">
<button type="button" class="sekundaer" onclick="tischDateiLaden()">tische.ini laden</button>
<br>
<button type="button" onclick="tischeSpeichern()">Tische speichern</button>
<div class="status" id="tischStatus"></div>
</section>
<section>
<h2>2. Gästeliste (CSV)</h2>
<p class="hinweis">Spalten: <code>Vorname;Nachname;Titel;Gruppe;Tisch;Anzahl</code>
<code>Gruppe</code> hält Personen zusammen (leer = Einzelgast),
<code>Tisch</code> setzt VIPs fest (z.&nbsp;B. Brautpaar an Tisch 1),
<code>Anzahl</code> expandiert eine Zeile in mehrere Buchungen
(anonyme Sammelbuchung). Datei hochladen <em>oder</em> direkt hier bearbeiten:</p>
<textarea id="csvText"></textarea>
<br>
<input type="file" id="csvDatei" accept=".csv,text/csv">
<br>
<button type="button" onclick="gaesteHochladen()">Gästeliste übernehmen</button>
<div class="status" id="gaesteStatus"></div>
<div id="gruppenUebersicht"></div>
</section>
<section>
<h2>3. Sitzordnung berechnen</h2>
<p class="hinweis">Jeder Klick startet den genetischen Algorithmus neu und liefert
eine neue Variante — die Wertigkeit (0 = perfekt, negativer = schlechter)
macht Varianten vergleichbar.</p>
<button type="button" id="berechnenKnopf" onclick="berechnen()" disabled>Sitzordnung berechnen</button>
<div class="status" id="rechnenStatus"></div>
<div id="kennzahlen"></div>
<div id="ergebnisTabellen"></div>
<img id="svgBild" alt="" style="display:none">
</section>
</main>
<script>
'use strict';
let sitzung = null;
async function api(pfad, optionen) {
const antwort = await fetch(pfad, optionen);
const daten = await antwort.json().catch(() => null);
if (!antwort.ok) {
const detail = daten && daten.detail
? (typeof daten.detail === 'string' ? daten.detail : JSON.stringify(daten.detail, null, 2))
: antwort.statusText;
throw new Error(detail);
}
return daten;
}
function status(id, text, klasse) {
const el = document.getElementById(id);
el.textContent = text;
el.className = 'status ' + (klasse || '');
}
/* ------------------------------------------------ 1. Tische */
function tischZeile(plaetze, x, y, tid) {
const tbody = document.querySelector('#tischTabelle tbody');
const nr = tid ?? tbody.children.length + 1;
const tr = document.createElement('tr');
if (tid !== undefined) tr.dataset.tid = tid; // echte Id aus tische.ini behalten
tr.innerHTML =
'<td class="tischNr">' + nr + '</td>' +
'<td><input type="number" min="1" max="100" value="' + (plaetze ?? 8) + '" class="plaetze"></td>' +
'<td><input type="number" step="1" value="' + (x ?? nr) + '" class="x"></td>' +
'<td><input type="number" step="1" value="' + (y ?? 1) + '" class="y"></td>' +
'<td><button type="button" class="sekundaer" onclick="this.closest(\'tr\').remove(); nummerieren()">✕</button></td>';
tbody.appendChild(tr);
}
function nummerieren() {
document.querySelectorAll('#tischTabelle tbody tr').forEach((tr, i) => {
if (tr.dataset.tid === undefined)
tr.querySelector('.tischNr').textContent = i + 1;
});
}
function tischeLeeren() {
document.querySelector('#tischTabelle tbody').innerHTML = '';
}
function presetReihe() {
tischeLeeren();
for (let i = 1; i <= 4; i++) tischZeile(5, i, 1);
}
function presetRaster() {
tischeLeeren();
for (let y = 1; y <= 3; y++)
for (let x = 1; x <= 3; x++)
tischZeile(8, x, y);
}
async function tischeSpeichern() {
const zeilen = [...document.querySelectorAll('#tischTabelle tbody tr')];
if (zeilen.length === 0) { status('tischStatus', 'Mindestens ein Tisch nötig.', 'fehler'); return; }
// Zeilen aus einer geladenen tische.ini behalten ihre echte Id
// (wichtig fuer VIP-Bindungen wie "Tisch 16"); neue Zeilen bekommen
// die naechste freie Id
const vergeben = new Set(zeilen.map(tr => parseInt(tr.dataset.tid, 10)).filter(n => !isNaN(n)));
let naechste = 1;
const freieId = () => { while (vergeben.has(naechste)) naechste++; vergeben.add(naechste); return naechste; };
const tische = zeilen.map(tr => ({
id: tr.dataset.tid !== undefined ? parseInt(tr.dataset.tid, 10) : freieId(),
plaetze: parseInt(tr.querySelector('.plaetze').value, 10),
x: parseFloat(tr.querySelector('.x').value),
y: parseFloat(tr.querySelector('.y').value),
}));
try {
const r = await api('/sitzung/' + sitzung + '/tische', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tische }),
});
const nachbarn = r.tische.map(t => 'Tisch ' + t.id + ' ↔ [' + t.nachbarn.join(', ') + ']').join(' ');
status('tischStatus', '✓ ' + r.tische.length + ' Tische, ' + r.plaetzeGesamt +
' Plätze gesamt. Abgeleitete Nachbarn: ' + nachbarn, 'ok');
berechnenFreigeben();
} catch (e) { status('tischStatus', 'Fehler: ' + e.message, 'fehler'); }
}
async function tischDateiLaden() {
const eingabe = document.getElementById('tischDatei');
if (eingabe.files.length === 0) {
status('tischStatus', 'Bitte zuerst eine tische.ini auswählen.', 'fehler');
return;
}
const form = new FormData();
form.append('datei', eingabe.files[0]);
try {
const r = await api('/sitzung/' + sitzung + '/tische-datei', { method: 'POST', body: form });
tischeLeeren();
for (const t of r.tische) tischZeile(t.plaetze, t.x, t.y, t.id);
status('tischStatus', '✓ ' + r.tische.length + ' Tische aus Datei übernommen (' +
r.plaetzeGesamt + ' Plätze) — Layout ist bereits gespeichert, ' +
'„Tische speichern“ ist nur nach Änderungen nötig.', 'ok');
berechnenFreigeben();
} catch (e) { status('tischStatus', 'Fehler: ' + e.message, 'fehler'); }
}
/* ------------------------------------------------ 2. Gäste */
const beispielCSV = `Vorname;Nachname;Titel;Gruppe;Tisch;Anzahl
Britta;Beispiel;;Brautpaar;1;
Bernd;Beispiel;Dr.;Brautpaar;1;
Anna;Huber;;Familie Huber;;
Berta;Huber;;Familie Huber;;
Carl;Huber;;Familie Huber;;
Doris;Huber;;Familie Huber;;
Emil;Meier;;Familie Meier;;
Frieda;Meier;;Familie Meier;;
Gustav;Meier;;Familie Meier;;
Hanna;Schmidt;;Studienfreunde;;
Igor;Petrov;;Studienfreunde;;
Julia;Weber;;Studienfreunde;;
Gast;Kollegium;;Kollegen;;4
Karl;Einzelgast;;;;
`;
async function gaesteHochladen() {
const dateiInput = document.getElementById('csvDatei');
let blob;
if (dateiInput.files.length > 0) {
blob = dateiInput.files[0];
} else {
blob = new Blob([document.getElementById('csvText').value], { type: 'text/csv' });
}
const form = new FormData();
form.append('datei', blob, 'gaeste.csv');
try {
const r = await api('/sitzung/' + sitzung + '/personen', { method: 'POST', body: form });
status('gaesteStatus', '✓ ' + r.personen + ' Personen in ' + r.gruppen.length +
' Gruppen, dazu ' + r.vips + ' VIPs mit festem Tisch.', 'ok');
const html = '<table><thead><tr><th>Gruppe</th><th>Personen</th></tr></thead><tbody>' +
r.gruppen.map(g => '<tr><td>' + g.name + '</td><td>' + g.anzahl + '</td></tr>').join('') +
'</tbody></table>';
document.getElementById('gruppenUebersicht').innerHTML = html;
berechnenFreigeben();
} catch (e) { status('gaesteStatus', 'Fehler: ' + e.message, 'fehler'); }
}
/* ------------------------------------------------ 3. Berechnen */
async function berechnenFreigeben() {
try {
const s = await api('/sitzung/' + sitzung);
document.getElementById('berechnenKnopf').disabled = !(s.tische > 0 && s.personen > 0);
} catch (e) { /* Session ggf. abgelaufen — beim Berechnen gemeldet */ }
}
async function berechnen() {
const knopf = document.getElementById('berechnenKnopf');
knopf.disabled = true;
status('rechnenStatus', 'Der genetische Algorithmus läuft…');
try {
const r = await api('/sitzung/' + sitzung + '/berechnen', { method: 'POST' });
status('rechnenStatus', '✓ Variante ' + r.variante + ' berechnet.', 'ok');
knopf.textContent = 'Neue Variante berechnen';
zeigeErgebnis(r);
const svg = await fetch('/sitzung/' + sitzung + '/ergebnis.svg');
const svgText = await svg.text();
const bild = document.getElementById('svgBild');
bild.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgText);
bild.style.display = 'block';
} catch (e) {
status('rechnenStatus', 'Fehler: ' + e.message, 'fehler');
} finally {
knopf.disabled = false;
}
}
function zeigeErgebnis(r) {
const getrennte = r.gruppen.filter(g => g.teilungen > 0).length;
document.getElementById('kennzahlen').innerHTML =
'<span class="kennzahl">Wertigkeit: <b>' + r.wertigkeit + '</b></span>' +
'<span class="kennzahl">Variante: <b>' + r.variante + '</b></span>' +
'<span class="kennzahl">getrennte Gruppen: <b>' + getrennte + '</b></span>';
let html = '<table><thead><tr><th>Tisch</th><th>Belegung</th><th>Gäste (Gruppe)</th></tr></thead><tbody>';
for (const t of r.tische) {
const gaeste = t.personen.map(p =>
((p.titel ? p.titel + ' ' : '') + p.vorname + ' ' + p.nachname +
' <span class="hinweis">(' + p.gruppe + ')</span>')).join(', ');
html += '<tr><td>Tisch ' + t.nummer + '</td><td>' + t.besetzt + '/' + t.plaetze +
'</td><td>' + gaeste + '</td></tr>';
}
html += '</tbody></table>';
html += '<h2 style="margin-top:1rem">Gruppen</h2>' +
'<table><thead><tr><th>Gruppe</th><th>Personen</th><th>Teilungen</th><th>Strafpunkte</th></tr></thead><tbody>' +
r.gruppen.map(g => '<tr><td>' + g.name + '</td><td>' + g.anzahl + '</td><td>' +
g.teilungen + '</td><td>' + g.wert + '</td></tr>').join('') +
'</tbody></table>';
document.getElementById('ergebnisTabellen').innerHTML = html;
}
/* ------------------------------------------------ Start */
window.addEventListener('DOMContentLoaded', async () => {
document.getElementById('csvText').value = beispielCSV;
presetReihe();
try {
const r = await api('/sitzung', { method: 'POST' });
sitzung = r.id;
document.getElementById('sitzungsId').textContent = sitzung;
} catch (e) {
document.getElementById('sitzungsId').textContent = 'Fehler: ' + e.message;
}
});
</script>
</body>
</html>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="500" viewBox="0 0 900 500" font-family="sans-serif">
<rect width="100%" height="100%" fill="white"/>
<text x="450" y="30" text-anchor="middle" font-size="16" fill="#333">Entwicklung der Loesungsfindung</text>
<line x1="60" y1="60" x2="60" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440" x2="840" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440.0" x2="840" y2="440.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="444.0" text-anchor="end" font-size="11" fill="#666">-83</text>
<line x1="60" y1="364.0" x2="840" y2="364.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="368.0" text-anchor="end" font-size="11" fill="#666">-75</text>
<line x1="60" y1="288.0" x2="840" y2="288.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="292.0" text-anchor="end" font-size="11" fill="#666">-68</text>
<line x1="60" y1="212.0" x2="840" y2="212.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="216.0" text-anchor="end" font-size="11" fill="#666">-60</text>
<line x1="60" y1="136.0" x2="840" y2="136.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="140.0" text-anchor="end" font-size="11" fill="#666">-53</text>
<line x1="60" y1="60.0" x2="840" y2="60.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="64.0" text-anchor="end" font-size="11" fill="#666">-45</text>
<text x="18" y="250" text-anchor="middle" font-size="12" fill="#333" transform="rotate(-90 18 250)">Wertigkeit (hoeher = besser)</text>
<text x="450" y="482" text-anchor="middle" font-size="12" fill="#333">Zyklus-Aktion (Zeit)</text>
<text x="60.0" y="456" text-anchor="middle" font-size="10" fill="#999">e</text>
<text x="108.8" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="157.5" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="206.2" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="255.0" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="303.8" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="352.5" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="401.2" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="450.0" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="498.8" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="547.5" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="596.2" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="645.0" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="693.8" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="742.5" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="791.2" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="840.0" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<polyline points="60.0,80.0 108.8,80.0 157.5,80.0 206.2,60.0 255.0,60.0 303.8,60.0 352.5,60.0 401.2,60.0 450.0,60.0 498.8,60.0 547.5,60.0 596.2,60.0 645.0,60.0 693.8,60.0 742.5,60.0 791.2,60.0 840.0,60.0" fill="none" stroke="#2f6f4f" stroke-width="2"/>
<polyline points="60.0,272.0 108.8,132.0 157.5,132.0 206.2,108.0 255.0,116.0 303.8,116.0 352.5,72.0 401.2,72.0 450.0,72.0 498.8,72.0 547.5,72.0 596.2,60.0 645.0,60.0 693.8,60.0 742.5,60.0 791.2,60.0 840.0,60.0" fill="none" stroke="#c08a2d" stroke-width="2"/>
<polyline points="60.0,440.0 108.8,200.0 157.5,200.0 206.2,160.0 255.0,200.0 303.8,200.0 352.5,80.0 401.2,80.0 450.0,80.0 498.8,80.0 547.5,80.0 596.2,60.0 645.0,60.0 693.8,60.0 742.5,60.0 791.2,60.0 840.0,60.0" fill="none" stroke="#a03030" stroke-width="2"/>
<line x1="680" y1="70" x2="704" y2="70" stroke="#2f6f4f" stroke-width="2"/>
<text x="710" y="74" font-size="11" fill="#333">beste Loesung</text>
<line x1="680" y1="88" x2="704" y2="88" stroke="#c08a2d" stroke-width="2"/>
<text x="710" y="92" font-size="11" fill="#333">Durchschnitt</text>
<line x1="680" y1="106" x2="704" y2="106" stroke="#a03030" stroke-width="2"/>
<text x="710" y="110" font-size="11" fill="#333">schlechteste Loesung</text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+120
View File
@@ -0,0 +1,120 @@
Aksoy, Rainer, = Hof 1, Tisch 14, Nummer 4
Aksoy, Renate, = Hof 1, Tisch 14, Nummer 2
Aydin, Horst, = Hof 1, Tisch 2, Nummer 8
Aydin, Ursula, = Hof 1, Tisch 2, Nummer 3
Barth, Hannelore, = Hof 1, Tisch 9, Nummer 8
Berger, Sergej, = Hof 1, Tisch 8, Nummer 5
Brandt, Andreas, = Hof 1, Tisch 2, Nummer 2
Brandt, Anja, = Hof 1, Tisch 15, Nummer 8
Brandt, Anton, = Hof 1, Tisch 16, Nummer 2
Brandt, Ben, = Hof 1, Tisch 17, Nummer 5
Brandt, Daniel, = Hof 1, Tisch 1, Nummer 4
Brandt, Felix, = Hof 1, Tisch 16, Nummer 5
Brandt, Frieda, = Hof 1, Tisch 16, Nummer 3
Brandt, Greta, = Hof 1, Tisch 16, Nummer 9
Brandt, Helga, = Hof 1, Tisch 1, Nummer 3
Brandt, Julia, = Hof 1, Tisch 1, Nummer 9
Brandt, Kathrin, = Hof 1, Tisch 1, Nummer 2
Brandt, Lena, = Hof 1, Tisch 2, Nummer 6
Brandt, Milan, = Hof 1, Tisch 15, Nummer 4
Brandt, Paul, = Hof 1, Tisch 17, Nummer 4
Brandt, Sofia, = Hof 1, Tisch 15, Nummer 9
Brandt, Theo, = Hof 1, Tisch 2, Nummer 5
Brandt, Werner, = Hof 1, Tisch 1, Nummer 12
Busch, Stefanie, = Hof 1, Tisch 8, Nummer 8
Demir, Elif, = Hof 1, Tisch 10, Nummer 8
Demir, Hannelore, = Hof 1, Tisch 13, Nummer 5
Demir, Ingrid, = Hof 1, Tisch 13, Nummer 2
Demir, Monika, = Hof 1, Tisch 13, Nummer 3
Dietrich, Jennifer, = Hof 1, Tisch 13, Nummer 7
Dietrich, Nicole, = Hof 1, Tisch 12, Nummer 6
Erdem, Erika, = Hof 1, Tisch 3, Nummer 4
Erdem, Gerhard, = Hof 1, Tisch 9, Nummer 6
Erdem, Rainer, = Hof 1, Tisch 9, Nummer 7
Esposito, Christian, = Hof 1, Tisch 12, Nummer 7
Esposito, Gisela, = Hof 1, Tisch 9, Nummer 2
Esposito, Julia, = Hof 1, Tisch 8, Nummer 9
Fischer, Heinz, = Hof 1, Tisch 3, Nummer 2
Fischer, Helga, = Hof 1, Tisch 3, Nummer 5
Fischer, Nicole, = Hof 1, Tisch 12, Nummer 8
Fischer, Sven, = Hof 1, Tisch 8, Nummer 3
Frank, Erika, = Hof 1, Tisch 9, Nummer 3
Frank, Nadine, = Hof 1, Tisch 11, Nummer 7
Frank, Timo, = Hof 1, Tisch 10, Nummer 4
Frank, Tobias, = Hof 1, Tisch 1, Nummer 5
Franke, Brigitte, = Hof 1, Tisch 9, Nummer 9
Hansen, Olga, = Hof 1, Tisch 11, Nummer 3
Hansen, Wolfgang, = Hof 1, Tisch 9, Nummer 5
Hoffmann, Lena, = Hof 1, Tisch 12, Nummer 4
Horn, Daniel, = Hof 1, Tisch 11, Nummer 5
Horn, Svenja, = Hof 1, Tisch 13, Nummer 4
Klein, Stefan, = Hof 1, Tisch 10, Nummer 2
Koch, Andreas, = Hof 1, Tisch 8, Nummer 7
Koch, Giovanna, = Hof 1, Tisch 4, Nummer 6
Kovac, Giovanna, = Hof 1, Tisch 8, Nummer 4
Kraus, Dennis, = Hof 1, Tisch 11, Nummer 6
Kraus, Matteo, = Hof 1, Tisch 16, Nummer 8
Kraus, Melanie, = Hof 1, Tisch 11, Nummer 8
Kuhn, Leni, = Hof 1, Tisch 16, Nummer 10
Kuhn, Sarah, = Hof 1, Tisch 14, Nummer 3
Kuhn, Stefan, = Hof 1, Tisch 13, Nummer 8
Kuhn, Timo, = Hof 1, Tisch 14, Nummer 5
Lange, Milan, = Hof 1, Tisch 11, Nummer 2
Meyer, Anton, = Hof 1, Tisch 17, Nummer 6
Meyer, Christian, = Hof 1, Tisch 13, Nummer 6
Meyer, Ida, = Hof 1, Tisch 17, Nummer 3
Meyer, Marco, = Hof 1, Tisch 12, Nummer 9
Meyer, Mila, = Hof 1, Tisch 16, Nummer 11
Meyer, Sandra, = Hof 1, Tisch 12, Nummer 3
Meyer, Wolfgang, = Hof 1, Tisch 9, Nummer 4
Musikverein, Gast, = Hof 1, Tisch 4, Nummer 2
Musikverein, Gast, = Hof 1, Tisch 4, Nummer 3
Musikverein, Gast, = Hof 1, Tisch 4, Nummer 4
Musikverein, Gast, = Hof 1, Tisch 4, Nummer 7
Musikverein, Gast, = Hof 1, Tisch 4, Nummer 8
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 2
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 3
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 4
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 5
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 6
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 7
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 8
Musikverein, Gast, = Hof 1, Tisch 5, Nummer 9
Peters, Melanie, = Hof 1, Tisch 1, Nummer 6
Peters, Yusuf, = Hof 1, Tisch 1, Nummer 7
Polat, Matthias, = Hof 1, Tisch 12, Nummer 2
Roth, Stefanie, = Hof 1, Tisch 13, Nummer 9
Schmidt, Dennis, = Hof 1, Tisch 4, Nummer 5
Schmitt, Markus, = Hof 1, Tisch 11, Nummer 9
Schwarz, Elias, = Hof 1, Tisch 16, Nummer 14
Schwarz, Guenter, = Hof 1, Tisch 14, Nummer 7
Schwarz, Liam, = Hof 1, Tisch 16, Nummer 7
Schwarz, Sarah, = Hof 1, Tisch 2, Nummer 4
Schwarz, Yusuf, = Hof 1, Tisch 2, Nummer 7
Simon, Jennifer, = Hof 1, Tisch 10, Nummer 3
Simon, Patrick, = Hof 1, Tisch 8, Nummer 6
Simon, Stefanie, = Hof 1, Tisch 10, Nummer 7
Sommer, Daniel, = Hof 1, Tisch 1, Nummer 13
Sommer, Dennis, = Hof 1, Tisch 15, Nummer 5
Sommer, Dennis, = Hof 1, Tisch 15, Nummer 7
Sommer, Dieter, = Hof 1, Tisch 1, Nummer 10
Sommer, Emil, = Hof 1, Tisch 16, Nummer 12
Sommer, Ida, = Hof 1, Tisch 16, Nummer 6
Sommer, Jennifer, = Hof 1, Tisch 15, Nummer 2
Sommer, Lina, = Hof 1, Tisch 15, Nummer 3
Sommer, Max, = Hof 1, Tisch 16, Nummer 13
Sommer, Nicole, = Hof 1, Tisch 15, Nummer 6
Sommer, Renate, = Hof 1, Tisch 1, Nummer 8
Sommer, Theo, = Hof 1, Tisch 16, Nummer 4
Stein, Andreas, = Hof 1, Tisch 10, Nummer 6
Stein, Werner, = Hof 1, Tisch 14, Nummer 6
Tran, Heinz, = Hof 1, Tisch 3, Nummer 3
Vogel, Anton, = Hof 1, Tisch 17, Nummer 2
Vogel, Max, = Hof 1, Tisch 17, Nummer 7
Vogel, Sandra, = Hof 1, Tisch 14, Nummer 9
Vogel, Svenja, = Hof 1, Tisch 8, Nummer 2
Vogel, Yusuf, = Hof 1, Tisch 14, Nummer 8
Wagner, Daniel, = Hof 1, Tisch 10, Nummer 5
Wagner, Timo, = Hof 1, Tisch 12, Nummer 5
Winkler, Sarah, = Hof 1, Tisch 1, Nummer 11
Yilmaz, Olga, = Hof 1, Tisch 11, Nummer 4
1 Aksoy Rainer = Hof 1 Tisch 14 Nummer 4
2 Aksoy Renate = Hof 1 Tisch 14 Nummer 2
3 Aydin Horst = Hof 1 Tisch 2 Nummer 8
4 Aydin Ursula = Hof 1 Tisch 2 Nummer 3
5 Barth Hannelore = Hof 1 Tisch 9 Nummer 8
6 Berger Sergej = Hof 1 Tisch 8 Nummer 5
7 Brandt Andreas = Hof 1 Tisch 2 Nummer 2
8 Brandt Anja = Hof 1 Tisch 15 Nummer 8
9 Brandt Anton = Hof 1 Tisch 16 Nummer 2
10 Brandt Ben = Hof 1 Tisch 17 Nummer 5
11 Brandt Daniel = Hof 1 Tisch 1 Nummer 4
12 Brandt Felix = Hof 1 Tisch 16 Nummer 5
13 Brandt Frieda = Hof 1 Tisch 16 Nummer 3
14 Brandt Greta = Hof 1 Tisch 16 Nummer 9
15 Brandt Helga = Hof 1 Tisch 1 Nummer 3
16 Brandt Julia = Hof 1 Tisch 1 Nummer 9
17 Brandt Kathrin = Hof 1 Tisch 1 Nummer 2
18 Brandt Lena = Hof 1 Tisch 2 Nummer 6
19 Brandt Milan = Hof 1 Tisch 15 Nummer 4
20 Brandt Paul = Hof 1 Tisch 17 Nummer 4
21 Brandt Sofia = Hof 1 Tisch 15 Nummer 9
22 Brandt Theo = Hof 1 Tisch 2 Nummer 5
23 Brandt Werner = Hof 1 Tisch 1 Nummer 12
24 Busch Stefanie = Hof 1 Tisch 8 Nummer 8
25 Demir Elif = Hof 1 Tisch 10 Nummer 8
26 Demir Hannelore = Hof 1 Tisch 13 Nummer 5
27 Demir Ingrid = Hof 1 Tisch 13 Nummer 2
28 Demir Monika = Hof 1 Tisch 13 Nummer 3
29 Dietrich Jennifer = Hof 1 Tisch 13 Nummer 7
30 Dietrich Nicole = Hof 1 Tisch 12 Nummer 6
31 Erdem Erika = Hof 1 Tisch 3 Nummer 4
32 Erdem Gerhard = Hof 1 Tisch 9 Nummer 6
33 Erdem Rainer = Hof 1 Tisch 9 Nummer 7
34 Esposito Christian = Hof 1 Tisch 12 Nummer 7
35 Esposito Gisela = Hof 1 Tisch 9 Nummer 2
36 Esposito Julia = Hof 1 Tisch 8 Nummer 9
37 Fischer Heinz = Hof 1 Tisch 3 Nummer 2
38 Fischer Helga = Hof 1 Tisch 3 Nummer 5
39 Fischer Nicole = Hof 1 Tisch 12 Nummer 8
40 Fischer Sven = Hof 1 Tisch 8 Nummer 3
41 Frank Erika = Hof 1 Tisch 9 Nummer 3
42 Frank Nadine = Hof 1 Tisch 11 Nummer 7
43 Frank Timo = Hof 1 Tisch 10 Nummer 4
44 Frank Tobias = Hof 1 Tisch 1 Nummer 5
45 Franke Brigitte = Hof 1 Tisch 9 Nummer 9
46 Hansen Olga = Hof 1 Tisch 11 Nummer 3
47 Hansen Wolfgang = Hof 1 Tisch 9 Nummer 5
48 Hoffmann Lena = Hof 1 Tisch 12 Nummer 4
49 Horn Daniel = Hof 1 Tisch 11 Nummer 5
50 Horn Svenja = Hof 1 Tisch 13 Nummer 4
51 Klein Stefan = Hof 1 Tisch 10 Nummer 2
52 Koch Andreas = Hof 1 Tisch 8 Nummer 7
53 Koch Giovanna = Hof 1 Tisch 4 Nummer 6
54 Kovac Giovanna = Hof 1 Tisch 8 Nummer 4
55 Kraus Dennis = Hof 1 Tisch 11 Nummer 6
56 Kraus Matteo = Hof 1 Tisch 16 Nummer 8
57 Kraus Melanie = Hof 1 Tisch 11 Nummer 8
58 Kuhn Leni = Hof 1 Tisch 16 Nummer 10
59 Kuhn Sarah = Hof 1 Tisch 14 Nummer 3
60 Kuhn Stefan = Hof 1 Tisch 13 Nummer 8
61 Kuhn Timo = Hof 1 Tisch 14 Nummer 5
62 Lange Milan = Hof 1 Tisch 11 Nummer 2
63 Meyer Anton = Hof 1 Tisch 17 Nummer 6
64 Meyer Christian = Hof 1 Tisch 13 Nummer 6
65 Meyer Ida = Hof 1 Tisch 17 Nummer 3
66 Meyer Marco = Hof 1 Tisch 12 Nummer 9
67 Meyer Mila = Hof 1 Tisch 16 Nummer 11
68 Meyer Sandra = Hof 1 Tisch 12 Nummer 3
69 Meyer Wolfgang = Hof 1 Tisch 9 Nummer 4
70 Musikverein Gast = Hof 1 Tisch 4 Nummer 2
71 Musikverein Gast = Hof 1 Tisch 4 Nummer 3
72 Musikverein Gast = Hof 1 Tisch 4 Nummer 4
73 Musikverein Gast = Hof 1 Tisch 4 Nummer 7
74 Musikverein Gast = Hof 1 Tisch 4 Nummer 8
75 Musikverein Gast = Hof 1 Tisch 5 Nummer 2
76 Musikverein Gast = Hof 1 Tisch 5 Nummer 3
77 Musikverein Gast = Hof 1 Tisch 5 Nummer 4
78 Musikverein Gast = Hof 1 Tisch 5 Nummer 5
79 Musikverein Gast = Hof 1 Tisch 5 Nummer 6
80 Musikverein Gast = Hof 1 Tisch 5 Nummer 7
81 Musikverein Gast = Hof 1 Tisch 5 Nummer 8
82 Musikverein Gast = Hof 1 Tisch 5 Nummer 9
83 Peters Melanie = Hof 1 Tisch 1 Nummer 6
84 Peters Yusuf = Hof 1 Tisch 1 Nummer 7
85 Polat Matthias = Hof 1 Tisch 12 Nummer 2
86 Roth Stefanie = Hof 1 Tisch 13 Nummer 9
87 Schmidt Dennis = Hof 1 Tisch 4 Nummer 5
88 Schmitt Markus = Hof 1 Tisch 11 Nummer 9
89 Schwarz Elias = Hof 1 Tisch 16 Nummer 14
90 Schwarz Guenter = Hof 1 Tisch 14 Nummer 7
91 Schwarz Liam = Hof 1 Tisch 16 Nummer 7
92 Schwarz Sarah = Hof 1 Tisch 2 Nummer 4
93 Schwarz Yusuf = Hof 1 Tisch 2 Nummer 7
94 Simon Jennifer = Hof 1 Tisch 10 Nummer 3
95 Simon Patrick = Hof 1 Tisch 8 Nummer 6
96 Simon Stefanie = Hof 1 Tisch 10 Nummer 7
97 Sommer Daniel = Hof 1 Tisch 1 Nummer 13
98 Sommer Dennis = Hof 1 Tisch 15 Nummer 5
99 Sommer Dennis = Hof 1 Tisch 15 Nummer 7
100 Sommer Dieter = Hof 1 Tisch 1 Nummer 10
101 Sommer Emil = Hof 1 Tisch 16 Nummer 12
102 Sommer Ida = Hof 1 Tisch 16 Nummer 6
103 Sommer Jennifer = Hof 1 Tisch 15 Nummer 2
104 Sommer Lina = Hof 1 Tisch 15 Nummer 3
105 Sommer Max = Hof 1 Tisch 16 Nummer 13
106 Sommer Nicole = Hof 1 Tisch 15 Nummer 6
107 Sommer Renate = Hof 1 Tisch 1 Nummer 8
108 Sommer Theo = Hof 1 Tisch 16 Nummer 4
109 Stein Andreas = Hof 1 Tisch 10 Nummer 6
110 Stein Werner = Hof 1 Tisch 14 Nummer 6
111 Tran Heinz = Hof 1 Tisch 3 Nummer 3
112 Vogel Anton = Hof 1 Tisch 17 Nummer 2
113 Vogel Max = Hof 1 Tisch 17 Nummer 7
114 Vogel Sandra = Hof 1 Tisch 14 Nummer 9
115 Vogel Svenja = Hof 1 Tisch 8 Nummer 2
116 Vogel Yusuf = Hof 1 Tisch 14 Nummer 8
117 Wagner Daniel = Hof 1 Tisch 10 Nummer 5
118 Wagner Timo = Hof 1 Tisch 12 Nummer 5
119 Winkler Sarah = Hof 1 Tisch 1 Nummer 11
120 Yilmaz Olga = Hof 1 Tisch 11 Nummer 4
+175
View File
@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="995.887" height="667.258" viewBox="0 0 995.887 667.258">
<rect width="100%" height="100%" fill="white"/>
<circle cx="744.415" cy="251.472" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="744.415" cy="251.472" r="12" fill="white" fill-opacity="0.75"/>
<text x="744.415" y="251.472" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">6</text>
<circle cx="87.1573" cy="415.786" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="87.1573" cy="415.786" r="12" fill="white" fill-opacity="0.75"/>
<text x="87.1573" y="415.786" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">7</text>
<circle cx="251.472" cy="251.472" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="251.472" cy="216.979" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="251.472" cy="285.965" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="251.472" cy="251.472" r="12" fill="white" fill-opacity="0.75"/>
<text x="251.472" y="251.472" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">3</text>
<circle cx="908.73" cy="415.786" r="48.4" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="908.73" cy="389.386" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="931.593" cy="402.586" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="931.593" cy="428.986" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="908.73" cy="442.186" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="885.867" cy="428.986" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="885.867" cy="402.586" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="908.73" cy="415.786" r="12" fill="white" fill-opacity="0.75"/>
<text x="908.73" y="415.786" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">17</text>
<circle cx="415.786" cy="251.472" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="415.786" cy="216.979" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="442.754" cy="229.966" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="449.415" cy="259.147" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="430.752" cy="282.549" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="400.82" cy="282.549" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="382.158" cy="259.147" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="388.818" cy="229.966" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="251.472" r="12" fill="white" fill-opacity="0.75"/>
<text x="415.786" y="251.472" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">4</text>
<circle cx="87.1573" cy="251.472" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="87.1573" cy="216.979" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="111.548" cy="227.081" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="121.651" cy="251.472" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="111.548" cy="275.862" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="87.1573" cy="285.965" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="62.7669" cy="275.862" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="52.664" cy="251.472" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="62.7669" cy="227.081" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="87.1573" cy="251.472" r="12" fill="white" fill-opacity="0.75"/>
<text x="87.1573" y="251.472" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">2</text>
<circle cx="580.101" cy="251.472" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="580.101" cy="216.979" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="604.491" cy="227.081" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="614.594" cy="251.472" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="604.491" cy="275.862" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="580.101" cy="285.965" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="555.711" cy="275.862" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="545.608" cy="251.472" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="555.711" cy="227.081" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="580.101" cy="251.472" r="12" fill="white" fill-opacity="0.75"/>
<text x="580.101" y="251.472" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">5</text>
<circle cx="251.472" cy="415.786" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="251.472" cy="381.293" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="275.862" cy="391.396" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="285.965" cy="415.786" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="275.862" cy="440.177" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="251.472" cy="450.28" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="227.081" cy="440.177" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="216.979" cy="415.786" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="227.081" cy="391.396" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="251.472" cy="415.786" r="12" fill="white" fill-opacity="0.75"/>
<text x="251.472" y="415.786" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">8</text>
<circle cx="415.786" cy="415.786" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="415.786" cy="381.293" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="440.177" cy="391.396" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="450.28" cy="415.786" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="440.177" cy="440.177" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="450.28" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="391.396" cy="440.177" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="381.293" cy="415.786" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="391.396" cy="391.396" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="415.786" r="12" fill="white" fill-opacity="0.75"/>
<text x="415.786" y="415.786" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">9</text>
<circle cx="580.101" cy="415.786" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="580.101" cy="381.293" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="604.491" cy="391.396" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="614.594" cy="415.786" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="604.491" cy="440.177" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="580.101" cy="450.28" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="555.711" cy="440.177" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="545.608" cy="415.786" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="555.711" cy="391.396" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="580.101" cy="415.786" r="12" fill="white" fill-opacity="0.75"/>
<text x="580.101" y="415.786" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">10</text>
<circle cx="744.415" cy="415.786" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="744.415" cy="381.293" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="768.806" cy="391.396" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="778.909" cy="415.786" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="768.806" cy="440.177" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="744.415" cy="450.28" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="720.025" cy="440.177" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="709.922" cy="415.786" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="720.025" cy="391.396" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="744.415" cy="415.786" r="12" fill="white" fill-opacity="0.75"/>
<text x="744.415" y="415.786" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">11</text>
<circle cx="87.1573" cy="580.101" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="87.1573" cy="545.608" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="111.548" cy="555.711" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="121.651" cy="580.101" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="111.548" cy="604.491" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="87.1573" cy="614.594" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="62.7669" cy="604.491" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="52.664" cy="580.101" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="62.7669" cy="555.711" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="87.1573" cy="580.101" r="12" fill="white" fill-opacity="0.75"/>
<text x="87.1573" y="580.101" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">12</text>
<circle cx="251.472" cy="580.101" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="251.472" cy="545.608" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="275.862" cy="555.711" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="285.965" cy="580.101" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="275.862" cy="604.491" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="251.472" cy="614.594" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="227.081" cy="604.491" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="216.979" cy="580.101" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="227.081" cy="555.711" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="251.472" cy="580.101" r="12" fill="white" fill-opacity="0.75"/>
<text x="251.472" y="580.101" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">13</text>
<circle cx="415.786" cy="580.101" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="415.786" cy="545.608" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="440.177" cy="555.711" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="450.28" cy="580.101" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="440.177" cy="604.491" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="614.594" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="391.396" cy="604.491" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="381.293" cy="580.101" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="391.396" cy="555.711" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="580.101" r="12" fill="white" fill-opacity="0.75"/>
<text x="415.786" y="580.101" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">14</text>
<circle cx="580.101" cy="580.101" r="56.4933" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="580.101" cy="545.608" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="604.491" cy="555.711" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="614.594" cy="580.101" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="604.491" cy="604.491" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="580.101" cy="614.594" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="555.711" cy="604.491" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="545.608" cy="580.101" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="555.711" cy="555.711" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="580.101" cy="580.101" r="12" fill="white" fill-opacity="0.75"/>
<text x="580.101" y="580.101" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">15</text>
<circle cx="415.786" cy="87.1573" r="73.0009" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="415.786" cy="36.1564" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="441.287" cy="42.9892" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="459.954" cy="61.6568" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="466.787" cy="87.1573" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="459.954" cy="112.658" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="441.287" cy="131.325" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="138.158" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="390.286" cy="131.325" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="371.618" cy="112.658" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="364.785" cy="87.1573" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="371.618" cy="61.6568" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="390.286" cy="42.9892" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="415.786" cy="87.1573" r="12" fill="white" fill-opacity="0.75"/>
<text x="415.786" y="87.1573" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">1</text>
<circle cx="908.73" cy="251.472" r="77.1573" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="908.73" cy="196.315" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="934.363" cy="202.632" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="954.124" cy="220.139" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="963.485" cy="244.823" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="960.303" cy="271.031" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="945.306" cy="292.758" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="921.93" cy="305.026" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="895.53" cy="305.026" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="872.154" cy="292.758" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="857.157" cy="271.031" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="853.975" cy="244.823" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="863.336" cy="220.139" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="883.097" cy="202.632" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="908.73" cy="251.472" r="12" fill="white" fill-opacity="0.75"/>
<text x="908.73" y="251.472" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">16</text>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

+154
View File
@@ -0,0 +1,154 @@
[1]
Kathrin Brandt = Hof 1, Tisch 1, Nummer 1
Helga Brandt = Hof 1, Tisch 1, Nummer 2
Daniel Brandt = Hof 1, Tisch 1, Nummer 3
Tobias Frank = Hof 1, Tisch 1, Nummer 4
Melanie Peters = Hof 1, Tisch 1, Nummer 5
Yusuf Peters = Hof 1, Tisch 1, Nummer 6
Renate Sommer = Hof 1, Tisch 1, Nummer 7
Julia Brandt = Hof 1, Tisch 1, Nummer 8
Dieter Sommer = Hof 1, Tisch 1, Nummer 9
Sarah Winkler = Hof 1, Tisch 1, Nummer 10
Werner Brandt = Hof 1, Tisch 1, Nummer 11
Daniel Sommer = Hof 1, Tisch 1, Nummer 12
[2]
Andreas Brandt = Hof 1, Tisch 2, Nummer 1
Ursula Aydin = Hof 1, Tisch 2, Nummer 2
Sarah Schwarz = Hof 1, Tisch 2, Nummer 3
Theo Brandt = Hof 1, Tisch 2, Nummer 4
Lena Brandt = Hof 1, Tisch 2, Nummer 5
Yusuf Schwarz = Hof 1, Tisch 2, Nummer 6
Horst Aydin = Hof 1, Tisch 2, Nummer 7
[3]
Heinz Fischer = Hof 1, Tisch 3, Nummer 1
Heinz Tran = Hof 1, Tisch 3, Nummer 2
Erika Erdem = Hof 1, Tisch 3, Nummer 3
Helga Fischer = Hof 1, Tisch 3, Nummer 4
[4]
Gast Musikverein = Hof 1, Tisch 4, Nummer 1
Gast Musikverein = Hof 1, Tisch 4, Nummer 2
Gast Musikverein = Hof 1, Tisch 4, Nummer 3
Dennis Schmidt = Hof 1, Tisch 4, Nummer 4
Giovanna Koch = Hof 1, Tisch 4, Nummer 5
Gast Musikverein = Hof 1, Tisch 4, Nummer 6
Gast Musikverein = Hof 1, Tisch 4, Nummer 7
[5]
Gast Musikverein = Hof 1, Tisch 5, Nummer 1
Gast Musikverein = Hof 1, Tisch 5, Nummer 2
Gast Musikverein = Hof 1, Tisch 5, Nummer 3
Gast Musikverein = Hof 1, Tisch 5, Nummer 4
Gast Musikverein = Hof 1, Tisch 5, Nummer 5
Gast Musikverein = Hof 1, Tisch 5, Nummer 6
Gast Musikverein = Hof 1, Tisch 5, Nummer 7
Gast Musikverein = Hof 1, Tisch 5, Nummer 8
[6]
[7]
[8]
Svenja Vogel = Hof 1, Tisch 8, Nummer 1
Sven Fischer = Hof 1, Tisch 8, Nummer 2
Giovanna Kovac = Hof 1, Tisch 8, Nummer 3
Sergej Berger = Hof 1, Tisch 8, Nummer 4
Patrick Simon = Hof 1, Tisch 8, Nummer 5
Andreas Koch = Hof 1, Tisch 8, Nummer 6
Stefanie Busch = Hof 1, Tisch 8, Nummer 7
Julia Esposito = Hof 1, Tisch 8, Nummer 8
[9]
Gisela Esposito = Hof 1, Tisch 9, Nummer 1
Erika Frank = Hof 1, Tisch 9, Nummer 2
Wolfgang Meyer = Hof 1, Tisch 9, Nummer 3
Wolfgang Hansen = Hof 1, Tisch 9, Nummer 4
Gerhard Erdem = Hof 1, Tisch 9, Nummer 5
Rainer Erdem = Hof 1, Tisch 9, Nummer 6
Hannelore Barth = Hof 1, Tisch 9, Nummer 7
Brigitte Franke = Hof 1, Tisch 9, Nummer 8
[10]
Stefan Klein = Hof 1, Tisch 10, Nummer 1
Jennifer Simon = Hof 1, Tisch 10, Nummer 2
Timo Frank = Hof 1, Tisch 10, Nummer 3
Daniel Wagner = Hof 1, Tisch 10, Nummer 4
Andreas Stein = Hof 1, Tisch 10, Nummer 5
Stefanie Simon = Hof 1, Tisch 10, Nummer 6
Elif Demir = Hof 1, Tisch 10, Nummer 7
[11]
Milan Lange = Hof 1, Tisch 11, Nummer 1
Olga Hansen = Hof 1, Tisch 11, Nummer 2
Olga Yilmaz = Hof 1, Tisch 11, Nummer 3
Daniel Horn = Hof 1, Tisch 11, Nummer 4
Dennis Kraus = Hof 1, Tisch 11, Nummer 5
Nadine Frank = Hof 1, Tisch 11, Nummer 6
Melanie Kraus = Hof 1, Tisch 11, Nummer 7
Markus Schmitt = Hof 1, Tisch 11, Nummer 8
[12]
Matthias Polat = Hof 1, Tisch 12, Nummer 1
Sandra Meyer = Hof 1, Tisch 12, Nummer 2
Lena Hoffmann = Hof 1, Tisch 12, Nummer 3
Timo Wagner = Hof 1, Tisch 12, Nummer 4
Nicole Dietrich = Hof 1, Tisch 12, Nummer 5
Christian Esposito = Hof 1, Tisch 12, Nummer 6
Nicole Fischer = Hof 1, Tisch 12, Nummer 7
Marco Meyer = Hof 1, Tisch 12, Nummer 8
[13]
Ingrid Demir = Hof 1, Tisch 13, Nummer 1
Monika Demir = Hof 1, Tisch 13, Nummer 2
Svenja Horn = Hof 1, Tisch 13, Nummer 3
Hannelore Demir = Hof 1, Tisch 13, Nummer 4
Christian Meyer = Hof 1, Tisch 13, Nummer 5
Jennifer Dietrich = Hof 1, Tisch 13, Nummer 6
Stefan Kuhn = Hof 1, Tisch 13, Nummer 7
Stefanie Roth = Hof 1, Tisch 13, Nummer 8
[14]
Renate Aksoy = Hof 1, Tisch 14, Nummer 1
Sarah Kuhn = Hof 1, Tisch 14, Nummer 2
Rainer Aksoy = Hof 1, Tisch 14, Nummer 3
Timo Kuhn = Hof 1, Tisch 14, Nummer 4
Werner Stein = Hof 1, Tisch 14, Nummer 5
Guenter Schwarz = Hof 1, Tisch 14, Nummer 6
Yusuf Vogel = Hof 1, Tisch 14, Nummer 7
Sandra Vogel = Hof 1, Tisch 14, Nummer 8
[15]
Jennifer Sommer = Hof 1, Tisch 15, Nummer 1
Lina Sommer = Hof 1, Tisch 15, Nummer 2
Milan Brandt = Hof 1, Tisch 15, Nummer 3
Dennis Sommer = Hof 1, Tisch 15, Nummer 4
Nicole Sommer = Hof 1, Tisch 15, Nummer 5
Dennis Sommer = Hof 1, Tisch 15, Nummer 6
Anja Brandt = Hof 1, Tisch 15, Nummer 7
Sofia Brandt = Hof 1, Tisch 15, Nummer 8
[16]
Anton Brandt = Hof 1, Tisch 16, Nummer 1
Frieda Brandt = Hof 1, Tisch 16, Nummer 2
Theo Sommer = Hof 1, Tisch 16, Nummer 3
Felix Brandt = Hof 1, Tisch 16, Nummer 4
Ida Sommer = Hof 1, Tisch 16, Nummer 5
Liam Schwarz = Hof 1, Tisch 16, Nummer 6
Matteo Kraus = Hof 1, Tisch 16, Nummer 7
Greta Brandt = Hof 1, Tisch 16, Nummer 8
Leni Kuhn = Hof 1, Tisch 16, Nummer 9
Mila Meyer = Hof 1, Tisch 16, Nummer 10
Emil Sommer = Hof 1, Tisch 16, Nummer 11
Max Sommer = Hof 1, Tisch 16, Nummer 12
Elias Schwarz = Hof 1, Tisch 16, Nummer 13
[17]
Anton Vogel = Hof 1, Tisch 17, Nummer 1
Ida Meyer = Hof 1, Tisch 17, Nummer 2
Paul Brandt = Hof 1, Tisch 17, Nummer 3
Ben Brandt = Hof 1, Tisch 17, Nummer 4
Anton Meyer = Hof 1, Tisch 17, Nummer 5
Max Vogel = Hof 1, Tisch 17, Nummer 6
+575
View File
@@ -0,0 +1,575 @@
{
"gruppen": [
{
"name": "Ehrentafel: Brautpaar, Brauteltern, Braeutigameltern, Trauzeugen (fest an Tisch 1)",
"tisch": 1,
"personen": [
{
"vorname": "Julia",
"nachname": "Brandt"
},
{
"vorname": "Daniel",
"nachname": "Sommer"
},
{
"vorname": "Werner",
"nachname": "Brandt"
},
{
"vorname": "Helga",
"nachname": "Brandt"
},
{
"vorname": "Dieter",
"nachname": "Sommer"
},
{
"vorname": "Renate",
"nachname": "Sommer"
},
{
"vorname": "Sarah",
"nachname": "Winkler"
},
{
"vorname": "Tobias",
"nachname": "Frank"
}
]
},
{
"name": "Familie Peters (Braut-Seite, Geschwister Melanie)",
"personen": [
{
"vorname": "Yusuf",
"nachname": "Peters"
},
{
"vorname": "Melanie",
"nachname": "Peters"
}
]
},
{
"name": "Familie Brandt (Braut-Seite, Geschwister Daniel): Felix (12 J.) am Kindertisch; Ben (13 J.) am Teenager-Tisch",
"personen": [
{
"vorname": "Daniel",
"nachname": "Brandt"
},
{
"vorname": "Kathrin",
"nachname": "Brandt"
}
]
},
{
"name": "Familie Kraus (Braut-Seite, Geschwister Melanie): Matteo (9 J.) am Kindertisch",
"personen": [
{
"vorname": "Dennis",
"nachname": "Kraus"
},
{
"vorname": "Melanie",
"nachname": "Kraus"
}
]
},
{
"name": "Familie Brandt (Braut-Seite, Geschwister Andreas): Theo (3 J.) sitzt bei den Eltern; Anton (10 J.) am Kindertisch; Frieda (12 J.) am Kindertisch; Paul (19 J.) am Teenager-Tisch",
"personen": [
{
"vorname": "Andreas",
"nachname": "Brandt"
},
{
"vorname": "Lena",
"nachname": "Brandt"
},
{
"vorname": "Theo",
"nachname": "Brandt"
}
]
},
{
"name": "Familie Meyer (Braut-Seite, Geschwister Sandra): Mila (10 J.) am Kindertisch; Anton (16 J.) am Teenager-Tisch; Ida (16 J.) am Teenager-Tisch",
"personen": [
{
"vorname": "Marco",
"nachname": "Meyer"
},
{
"vorname": "Sandra",
"nachname": "Meyer"
}
]
},
{
"name": "Familie Brandt (Braut-Seite, Geschwister Milan): Sofia (2 J.) sitzt bei den Eltern; Greta (7 J.) am Kindertisch",
"personen": [
{
"vorname": "Milan",
"nachname": "Brandt"
},
{
"vorname": "Anja",
"nachname": "Brandt"
},
{
"vorname": "Sofia",
"nachname": "Brandt"
}
]
},
{
"name": "Familie Vogel (Braut-Seite, Geschwister Sandra): Anton (14 J.) am Teenager-Tisch; Max (15 J.) am Teenager-Tisch",
"personen": [
{
"vorname": "Yusuf",
"nachname": "Vogel"
},
{
"vorname": "Sandra",
"nachname": "Vogel"
}
]
},
{
"name": "Familie Sommer (Braeutigam-Seite, Geschwister Dennis): Lina (3 J.) sitzt bei den Eltern; Theo (8 J.) am Kindertisch; Max (12 J.) am Kindertisch",
"personen": [
{
"vorname": "Dennis",
"nachname": "Sommer"
},
{
"vorname": "Nicole",
"nachname": "Sommer"
},
{
"vorname": "Lina",
"nachname": "Sommer"
}
]
},
{
"name": "Familie Kuhn (Braeutigam-Seite, Geschwister Sarah): Leni (10 J.) am Kindertisch",
"personen": [
{
"vorname": "Timo",
"nachname": "Kuhn"
},
{
"vorname": "Sarah",
"nachname": "Kuhn"
}
]
},
{
"name": "Familie Sommer (Braeutigam-Seite, Geschwister Dennis): Emil (8 J.) am Kindertisch; Ida (8 J.) am Kindertisch",
"personen": [
{
"vorname": "Dennis",
"nachname": "Sommer"
},
{
"vorname": "Jennifer",
"nachname": "Sommer"
}
]
},
{
"name": "Familie Schwarz (Braeutigam-Seite, Geschwister Sarah): Liam (8 J.) am Kindertisch; Elias (8 J.) am Kindertisch",
"personen": [
{
"vorname": "Yusuf",
"nachname": "Schwarz"
},
{
"vorname": "Sarah",
"nachname": "Schwarz"
}
]
},
{
"name": "Onkel und Tante (Braut-Seite)",
"personen": [
{
"vorname": "Horst",
"nachname": "Aydin"
},
{
"vorname": "Ursula",
"nachname": "Aydin"
}
]
},
{
"name": "Onkel und Tante mit Cousine (Braut-Seite)",
"personen": [
{
"vorname": "Ingrid",
"nachname": "Demir"
},
{
"vorname": "Monika",
"nachname": "Demir"
},
{
"vorname": "Hannelore",
"nachname": "Demir"
}
]
},
{
"name": "Onkel und Tante (Braeutigam-Seite)",
"personen": [
{
"vorname": "Heinz",
"nachname": "Fischer"
},
{
"vorname": "Helga",
"nachname": "Fischer"
}
]
},
{
"name": "Grosstante mit Begleitung",
"personen": [
{
"vorname": "Heinz",
"nachname": "Tran"
},
{
"vorname": "Erika",
"nachname": "Erdem"
}
]
},
{
"name": "Studienfreunde der Braut",
"personen": [
{
"vorname": "Nicole",
"nachname": "Fischer"
},
{
"vorname": "Matthias",
"nachname": "Polat"
},
{
"vorname": "Christian",
"nachname": "Esposito"
},
{
"vorname": "Lena",
"nachname": "Hoffmann"
},
{
"vorname": "Timo",
"nachname": "Wagner"
},
{
"vorname": "Nicole",
"nachname": "Dietrich"
}
]
},
{
"name": "Kegelclub des Brautvaters",
"personen": [
{
"vorname": "Wolfgang",
"nachname": "Hansen"
},
{
"vorname": "Hannelore",
"nachname": "Barth"
},
{
"vorname": "Erika",
"nachname": "Frank"
},
{
"vorname": "Gerhard",
"nachname": "Erdem"
},
{
"vorname": "Rainer",
"nachname": "Erdem"
},
{
"vorname": "Gisela",
"nachname": "Esposito"
},
{
"vorname": "Brigitte",
"nachname": "Franke"
},
{
"vorname": "Wolfgang",
"nachname": "Meyer"
}
]
},
{
"name": "Arbeitskolleginnen der Braut",
"personen": [
{
"vorname": "Elif",
"nachname": "Demir"
},
{
"vorname": "Jennifer",
"nachname": "Simon"
},
{
"vorname": "Andreas",
"nachname": "Stein"
},
{
"vorname": "Stefan",
"nachname": "Klein"
},
{
"vorname": "Timo",
"nachname": "Frank"
},
{
"vorname": "Daniel",
"nachname": "Wagner"
},
{
"vorname": "Stefanie",
"nachname": "Simon"
}
]
},
{
"name": "Arbeitskollegen des Braeutigams",
"personen": [
{
"vorname": "Nadine",
"nachname": "Frank"
},
{
"vorname": "Markus",
"nachname": "Schmitt"
},
{
"vorname": "Olga",
"nachname": "Yilmaz"
},
{
"vorname": "Olga",
"nachname": "Hansen"
},
{
"vorname": "Daniel",
"nachname": "Horn"
},
{
"vorname": "Milan",
"nachname": "Lange"
}
]
},
{
"name": "Nachbarn der Brauteltern",
"personen": [
{
"vorname": "Guenter",
"nachname": "Schwarz"
},
{
"vorname": "Rainer",
"nachname": "Aksoy"
},
{
"vorname": "Renate",
"nachname": "Aksoy"
},
{
"vorname": "Werner",
"nachname": "Stein"
}
]
},
{
"name": "Fussballverein des Braeutigams",
"personen": [
{
"vorname": "Sergej",
"nachname": "Berger"
},
{
"vorname": "Giovanna",
"nachname": "Kovac"
},
{
"vorname": "Dennis",
"nachname": "Schmidt"
},
{
"vorname": "Patrick",
"nachname": "Simon"
},
{
"vorname": "Stefanie",
"nachname": "Busch"
},
{
"vorname": "Andreas",
"nachname": "Koch"
},
{
"vorname": "Svenja",
"nachname": "Vogel"
},
{
"vorname": "Giovanna",
"nachname": "Koch"
},
{
"vorname": "Julia",
"nachname": "Esposito"
}
]
},
{
"name": "Freundinnen aus der Schulzeit",
"personen": [
{
"vorname": "Jennifer",
"nachname": "Dietrich"
},
{
"vorname": "Christian",
"nachname": "Meyer"
},
{
"vorname": "Svenja",
"nachname": "Horn"
},
{
"vorname": "Stefanie",
"nachname": "Roth"
},
{
"vorname": "Stefan",
"nachname": "Kuhn"
}
]
},
{
"name": "Kindertisch (5-12 Jahre, fest an Tisch 16)",
"tisch": 16,
"personen": [
{
"vorname": "Felix",
"nachname": "Brandt"
},
{
"vorname": "Matteo",
"nachname": "Kraus"
},
{
"vorname": "Anton",
"nachname": "Brandt"
},
{
"vorname": "Frieda",
"nachname": "Brandt"
},
{
"vorname": "Mila",
"nachname": "Meyer"
},
{
"vorname": "Greta",
"nachname": "Brandt"
},
{
"vorname": "Theo",
"nachname": "Sommer"
},
{
"vorname": "Max",
"nachname": "Sommer"
},
{
"vorname": "Leni",
"nachname": "Kuhn"
},
{
"vorname": "Emil",
"nachname": "Sommer"
},
{
"vorname": "Ida",
"nachname": "Sommer"
},
{
"vorname": "Liam",
"nachname": "Schwarz"
},
{
"vorname": "Elias",
"nachname": "Schwarz"
}
]
},
{
"name": "Teenager-Tisch (13-19 Jahre, fest an Tisch 17)",
"tisch": 17,
"personen": [
{
"vorname": "Ben",
"nachname": "Brandt"
},
{
"vorname": "Paul",
"nachname": "Brandt"
},
{
"vorname": "Anton",
"nachname": "Meyer"
},
{
"vorname": "Ida",
"nachname": "Meyer"
},
{
"vorname": "Anton",
"nachname": "Vogel"
},
{
"vorname": "Max",
"nachname": "Vogel"
}
]
},
{
"name": "Musikverein (anonyme Sammelbuchung, 13 Karten)",
"anzahl": 13,
"personen": [
{
"vorname": "Gast",
"nachname": "Musikverein"
}
]
},
{
"name": "alleinreisender Gast",
"personen": [
{
"vorname": "Sven",
"nachname": "Fischer"
}
]
}
]
}
+118
View File
@@ -0,0 +1,118 @@
[1]
Nummer=1
Hof=1
Plaetze=12
Nachbarliste=[3, 4, 5]
Koordinaten=(2,0)
[2]
Nummer=2
Hof=1
Plaetze=8
Nachbarliste=[3, 7, 8]
Koordinaten=(0,1)
[3]
Nummer=3
Hof=1
Plaetze=8
Nachbarliste=[1, 2, 4, 7, 8, 9]
Koordinaten=(1,1)
[4]
Nummer=4
Hof=1
Plaetze=8
Nachbarliste=[1, 3, 5, 8, 9, 10]
Koordinaten=(2,1)
[5]
Nummer=5
Hof=1
Plaetze=8
Nachbarliste=[1, 4, 6, 9, 10, 11]
Koordinaten=(3,1)
[6]
Nummer=6
Hof=1
Plaetze=8
Nachbarliste=[5, 10, 11, 16, 17]
Koordinaten=(4,1)
[7]
Nummer=7
Hof=1
Plaetze=8
Nachbarliste=[2, 3, 8, 12, 13]
Koordinaten=(0,2)
[8]
Nummer=8
Hof=1
Plaetze=8
Nachbarliste=[2, 3, 4, 7, 9, 12, 13, 14]
Koordinaten=(1,2)
[9]
Nummer=9
Hof=1
Plaetze=8
Nachbarliste=[3, 4, 5, 8, 10, 13, 14, 15]
Koordinaten=(2,2)
[10]
Nummer=10
Hof=1
Plaetze=8
Nachbarliste=[4, 5, 6, 9, 11, 14, 15]
Koordinaten=(3,2)
[11]
Nummer=11
Hof=1
Plaetze=8
Nachbarliste=[5, 6, 10, 15, 16, 17]
Koordinaten=(4,2)
[12]
Nummer=12
Hof=1
Plaetze=8
Nachbarliste=[7, 8, 13]
Koordinaten=(0,3)
[13]
Nummer=13
Hof=1
Plaetze=8
Nachbarliste=[7, 8, 9, 12, 14]
Koordinaten=(1,3)
[14]
Nummer=14
Hof=1
Plaetze=8
Nachbarliste=[8, 9, 10, 13, 15]
Koordinaten=(2,3)
[15]
Nummer=15
Hof=1
Plaetze=8
Nachbarliste=[9, 10, 11, 14]
Koordinaten=(3,3)
[16]
Nummer=16
Hof=1
Plaetze=13
Nachbarliste=[6, 11, 17]
Koordinaten=(5,1)
[17]
Nummer=17
Hof=1
Plaetze=6
Nachbarliste=[6, 11, 16]
Koordinaten=(5,2)
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="500" viewBox="0 0 900 500" font-family="sans-serif">
<rect width="100%" height="100%" fill="white"/>
<text x="450" y="30" text-anchor="middle" font-size="16" fill="#333">Entwicklung der Loesungsfindung</text>
<line x1="60" y1="60" x2="60" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440" x2="840" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440.0" x2="840" y2="440.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="444.0" text-anchor="end" font-size="11" fill="#666">-620</text>
<line x1="60" y1="364.0" x2="840" y2="364.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="368.0" text-anchor="end" font-size="11" fill="#666">-559</text>
<line x1="60" y1="288.0" x2="840" y2="288.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="292.0" text-anchor="end" font-size="11" fill="#666">-498</text>
<line x1="60" y1="212.0" x2="840" y2="212.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="216.0" text-anchor="end" font-size="11" fill="#666">-436</text>
<line x1="60" y1="136.0" x2="840" y2="136.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="140.0" text-anchor="end" font-size="11" fill="#666">-375</text>
<line x1="60" y1="60.0" x2="840" y2="60.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="64.0" text-anchor="end" font-size="11" fill="#666">-314</text>
<text x="18" y="250" text-anchor="middle" font-size="12" fill="#333" transform="rotate(-90 18 250)">Wertigkeit (hoeher = besser)</text>
<text x="450" y="482" text-anchor="middle" font-size="12" fill="#333">Zyklus-Aktion (Zeit)</text>
<text x="60.0" y="456" text-anchor="middle" font-size="10" fill="#999">e</text>
<text x="108.8" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="157.5" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="206.2" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="255.0" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="303.8" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="352.5" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="401.2" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="450.0" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="498.8" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="547.5" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="596.2" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="645.0" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="693.8" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="742.5" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="791.2" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="840.0" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<polyline points="60.0,288.5 108.8,288.5 157.5,288.5 206.2,199.1 255.0,199.1 303.8,199.1 352.5,199.1 401.2,199.1 450.0,127.1 498.8,127.1 547.5,127.1 596.2,127.1 645.0,127.1 693.8,60.0 742.5,60.0 791.2,60.0 840.0,60.0" fill="none" stroke="#2f6f4f" stroke-width="2"/>
<polyline points="60.0,359.5 108.8,318.8 157.5,318.8 206.2,245.3 255.0,269.8 303.8,269.8 352.5,225.4 401.2,225.4 450.0,175.5 498.8,192.1 547.5,192.1 596.2,148.4 645.0,148.4 693.8,117.9 742.5,128.1 791.2,128.1 840.0,60.0" fill="none" stroke="#c08a2d" stroke-width="2"/>
<polyline points="60.0,440.0 108.8,353.1 157.5,353.1 206.2,288.5 255.0,353.1 303.8,353.1 352.5,243.8 401.2,243.8 450.0,231.4 498.8,243.8 547.5,243.8 596.2,169.3 645.0,169.3 693.8,231.4 742.5,231.4 791.2,231.4 840.0,60.0" fill="none" stroke="#a03030" stroke-width="2"/>
<line x1="680" y1="70" x2="704" y2="70" stroke="#2f6f4f" stroke-width="2"/>
<text x="710" y="74" font-size="11" fill="#333">beste Loesung</text>
<line x1="680" y1="88" x2="704" y2="88" stroke="#c08a2d" stroke-width="2"/>
<text x="710" y="92" font-size="11" fill="#333">Durchschnitt</text>
<line x1="680" y1="106" x2="704" y2="106" stroke="#a03030" stroke-width="2"/>
<text x="710" y="110" font-size="11" fill="#333">schlechteste Loesung</text>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

+300
View File
@@ -0,0 +1,300 @@
Aksoy, Luca, = Hof 1, Tisch 32, Nummer 5
Aksoy, Marie, = Hof 1, Tisch 33, Nummer 4
Aksoy, Marija, = Hof 1, Tisch 31, Nummer 2
Aksoy, Michael, Dr. = Hof 2, Tisch 64, Nummer 5
Aksoy, Nadine, = Hof 1, Tisch 32, Nummer 4
Aksoy, Nicole, = Hof 1, Tisch 24, Nummer 5
Aksoy, Sergej, = Hof 1, Tisch 32, Nummer 2
Aksoy, Sofia, = Hof 1, Tisch 32, Nummer 3
Aksoy, Thomas, = Hof 1, Tisch 33, Nummer 5
Albrecht, Andreas, = Hof 1, Tisch 20, Nummer 4
Albrecht, Emre, Prof. Dr. = Hof 2, Tisch 57, Nummer 2
Albrecht, Sarah, = Hof 1, Tisch 20, Nummer 2
Albrecht, Yusuf, Prof. Dr. = Hof 1, Tisch 28, Nummer 3
Arnold, Christian, = Hof 2, Tisch 47, Nummer 2
Arnold, Jennifer, = Hof 2, Tisch 47, Nummer 4
Arnold, Jennifer, Dr. = Hof 2, Tisch 57, Nummer 3
Arnold, Leni, = Hof 1, Tisch 20, Nummer 3
Arnold, Mila, = Hof 2, Tisch 47, Nummer 3
Arnold, Nadine, = Hof 1, Tisch 30, Nummer 2
Arnold, Patrick, = Hof 1, Tisch 20, Nummer 5
Barth, Lina, = Hof 2, Tisch 83, Nummer 2
Barth, Markus, = Hof 2, Tisch 68, Nummer 4
Barth, Nadine, = Hof 2, Tisch 83, Nummer 3
Barth, Patrick, = Hof 2, Tisch 83, Nummer 5
Barth, Timo, = Hof 2, Tisch 83, Nummer 4
Bauer, Dragana, = Hof 1, Tisch 44, Nummer 3
Bauer, Yusuf, = Hof 1, Tisch 3, Nummer 5
Becker, Jan, = Hof 2, Tisch 61, Nummer 2
Becker, Marija, = Hof 2, Tisch 55, Nummer 2
Becker, Nicole, = Hof 2, Tisch 61, Nummer 4
Becker, Sandra, = Hof 2, Tisch 61, Nummer 3
Berger, Andreas, = Hof 1, Tisch 18, Nummer 2
Berger, Anton, = Hof 1, Tisch 18, Nummer 4
Berger, Jennifer, = Hof 1, Tisch 18, Nummer 3
Berger, Layla, = Hof 1, Tisch 17, Nummer 2
Berger, Nadine, Dr. = Hof 2, Tisch 81, Nummer 2
Berger, Stefan, = Hof 1, Tisch 17, Nummer 4
Berger, Svenja, = Hof 1, Tisch 17, Nummer 3
Bianchi, Jan, Dr. = Hof 1, Tisch 4, Nummer 2
Bianchi, Mia, = Hof 2, Tisch 50, Nummer 4
Bianchi, Sandra, = Hof 2, Tisch 50, Nummer 3
Bianchi, Thomas, = Hof 2, Tisch 50, Nummer 2
Bianchi, Thomas, = Hof 2, Tisch 58, Nummer 5
Braun, Christian, = Hof 1, Tisch 41, Nummer 3
Braun, Dragana, = Hof 1, Tisch 40, Nummer 4
Braun, Jan, = Hof 1, Tisch 40, Nummer 3
Braun, Julia, = Hof 1, Tisch 41, Nummer 4
Braun, Milan, = Hof 1, Tisch 41, Nummer 2
Braun, Nadine, = Hof 1, Tisch 40, Nummer 2
Busch, Emre, = Hof 1, Tisch 22, Nummer 4
Busch, Marie, = Hof 1, Tisch 22, Nummer 5
Busch, Michael, = Hof 1, Tisch 22, Nummer 2
Busch, Olga, = Hof 1, Tisch 22, Nummer 3
Busch, Patrick, = Hof 2, Tisch 54, Nummer 3
Celik, Elif, = Hof 1, Tisch 33, Nummer 2
Celik, Patrick, = Hof 1, Tisch 33, Nummer 3
Dallmann, Charlotte, = Hof 1, Tisch 7, Nummer 2
Dallmann, Heinrich, = Hof 1, Tisch 7, Nummer 4
Dallmann, Margarete, = Hof 1, Tisch 7, Nummer 5
Dallmann, Maximilian, = Hof 1, Tisch 7, Nummer 3
Demir, Sandra, Dr. = Hof 1, Tisch 45, Nummer 5
Dietrich, Dragana, Prof. Dr. = Hof 2, Tisch 54, Nummer 4
Dietrich, Jennifer, = Hof 2, Tisch 85, Nummer 4
Dietrich, Marco, = Hof 2, Tisch 85, Nummer 3
Dietrich, Sofia, = Hof 2, Tisch 85, Nummer 2
Engel, Anja, = Hof 2, Tisch 58, Nummer 3
Engel, Anton, = Hof 2, Tisch 48, Nummer 3
Engel, Julia, = Hof 2, Tisch 48, Nummer 4
Engel, Nicole, = Hof 2, Tisch 48, Nummer 2
Engel, Yusuf, = Hof 2, Tisch 58, Nummer 2
Esposito, Jan, Dr. = Hof 1, Tisch 16, Nummer 2
Esposito, Yusuf, Prof. Dr. = Hof 2, Tisch 55, Nummer 4
Frank, Dennis, = Hof 1, Tisch 26, Nummer 3
Frank, Finn, = Hof 1, Tisch 25, Nummer 3
Frank, Lena, = Hof 1, Tisch 25, Nummer 2
Frank, Marie, = Hof 1, Tisch 26, Nummer 2
Frank, Nadine, = Hof 1, Tisch 26, Nummer 4
Franke, Amira, = Hof 2, Tisch 79, Nummer 2
Franke, Julia, = Hof 2, Tisch 70, Nummer 3
Franke, Marco, = Hof 2, Tisch 79, Nummer 3
Franke, Michael, = Hof 2, Tisch 79, Nummer 4
Franke, Olga, = Hof 2, Tisch 70, Nummer 4
Freifrau von Waldeck, Antonia, = Hof 1, Tisch 6, Nummer 3
Freiherr von Waldeck, Konstantin, Dr. = Hof 1, Tisch 6, Nummer 4
Grothe, Alexander, Dr. h.c. = Hof 1, Tisch 5, Nummer 3
Grothe, Beate, = Hof 1, Tisch 5, Nummer 5
Haas, Jennifer, Dr. = Hof 2, Tisch 68, Nummer 5
Haas, Patrick, = Hof 1, Tisch 44, Nummer 4
Hansen, Giovanna, = Hof 1, Tisch 1, Nummer 3
Hansen, Markus, = Hof 1, Tisch 1, Nummer 4
Hansen, Nicole, Dr. = Hof 1, Tisch 45, Nummer 2
Hansen, Olga, = Hof 1, Tisch 1, Nummer 2
Hansen, Thomas, = Hof 1, Tisch 16, Nummer 3
Hartmann, Deniz, = Hof 1, Tisch 13, Nummer 2
Hartmann, Marija, = Hof 1, Tisch 23, Nummer 4
Hartmann, Nadine, = Hof 1, Tisch 13, Nummer 3
Hartmann, Olga, = Hof 1, Tisch 23, Nummer 2
Hartmann, Thomas, = Hof 1, Tisch 23, Nummer 3
Hoffmann, Emre, = Hof 2, Tisch 54, Nummer 2
Hoffmann, Patrick, Prof. Dr. = Hof 1, Tisch 30, Nummer 4
Hoffmann, Thomas, Dr. = Hof 1, Tisch 21, Nummer 4
Horn, Kathrin, = Hof 1, Tisch 39, Nummer 2
Horn, Layla, = Hof 1, Tisch 39, Nummer 4
Horn, Leon, = Hof 1, Tisch 39, Nummer 5
Horn, Michael, = Hof 1, Tisch 39, Nummer 3
Kaya, Christina, = Hof 2, Tisch 56, Nummer 2
Kaya, Luca, = Hof 2, Tisch 56, Nummer 4
Kaya, Nicole, = Hof 2, Tisch 56, Nummer 3
Kaya, Patrick, Dr. = Hof 1, Tisch 3, Nummer 4
Kaya, Timo, Prof. Dr. = Hof 1, Tisch 45, Nummer 4
Kessler, Ferdinand, Dr. = Hof 1, Tisch 5, Nummer 2
Kessler, Viktoria, = Hof 1, Tisch 5, Nummer 4
Klein, Emre, = Hof 1, Tisch 3, Nummer 3
Klein, Lina, = Hof 2, Tisch 67, Nummer 4
Klein, Markus, = Hof 1, Tisch 24, Nummer 2
Klein, Milan, = Hof 2, Tisch 67, Nummer 3
Klein, Olga, Dr. = Hof 2, Tisch 57, Nummer 4
Klein, Stefanie, = Hof 2, Tisch 67, Nummer 2
Koch, Emil, = Hof 1, Tisch 34, Nummer 4
Koch, Jan, = Hof 1, Tisch 34, Nummer 2
Koch, Kathrin, Dr. = Hof 2, Tisch 71, Nummer 2
Koch, Marie, = Hof 1, Tisch 34, Nummer 5
Koch, Michael, = Hof 1, Tisch 25, Nummer 5
Koch, Olga, = Hof 1, Tisch 34, Nummer 3
Koch, Sarah, = Hof 1, Tisch 25, Nummer 4
Kovac, Anja, = Hof 1, Tisch 27, Nummer 2
Kovac, Henry, = Hof 1, Tisch 35, Nummer 2
Kovac, Lena, = Hof 1, Tisch 27, Nummer 4
Kovac, Luca, = Hof 2, Tisch 75, Nummer 3
Kovac, Marco, = Hof 1, Tisch 27, Nummer 3
Kovac, Marija, = Hof 1, Tisch 35, Nummer 4
Kovac, Matthias, = Hof 2, Tisch 55, Nummer 3
Kovac, Tobias, = Hof 1, Tisch 35, Nummer 3
Kraus, Markus, = Hof 1, Tisch 10, Nummer 2
Kraus, Patrick, Dr. = Hof 2, Tisch 65, Nummer 5
Krause, Andreas, = Hof 1, Tisch 38, Nummer 2
Krause, Arda, = Hof 1, Tisch 38, Nummer 4
Krause, Christina, Dr. = Hof 1, Tisch 4, Nummer 4
Krause, Daniel, = Hof 1, Tisch 24, Nummer 4
Krause, Dragana, Dr. = Hof 2, Tisch 75, Nummer 4
Krause, Emma, = Hof 1, Tisch 38, Nummer 3
Krause, Julia, = Hof 1, Tisch 38, Nummer 5
Krause, Matthias, = Hof 2, Tisch 64, Nummer 4
Krause, Thomas, = Hof 1, Tisch 44, Nummer 5
Kuhn, Julia, Dr. = Hof 2, Tisch 71, Nummer 4
Lange, Dragana, = Hof 1, Tisch 36, Nummer 3
Lange, Jan, = Hof 2, Tisch 52, Nummer 3
Lange, Michael, = Hof 1, Tisch 36, Nummer 4
Lange, Sergej, = Hof 2, Tisch 54, Nummer 5
Lange, Sofia, = Hof 2, Tisch 52, Nummer 5
Lange, Svenja, = Hof 1, Tisch 36, Nummer 2
Lehmann, Giovanna, = Hof 1, Tisch 8, Nummer 5
Lehmann, Layla, = Hof 1, Tisch 8, Nummer 3
Lehmann, Markus, = Hof 1, Tisch 9, Nummer 2
Lehmann, Milan, = Hof 1, Tisch 8, Nummer 2
Lehmann, Nicole, = Hof 1, Tisch 8, Nummer 4
Lehmann, Patrick, = Hof 2, Tisch 81, Nummer 3
Lehmann, Thomas, = Hof 1, Tisch 9, Nummer 3
Ludwig, Aylin, Dr. = Hof 2, Tisch 64, Nummer 3
Ludwig, Elias, = Hof 2, Tisch 82, Nummer 2
Ludwig, Elif, = Hof 2, Tisch 82, Nummer 3
Ludwig, Emre, = Hof 2, Tisch 82, Nummer 4
Ludwig, Emre, Prof. Dr. = Hof 1, Tisch 24, Nummer 3
Ludwig, Giovanna, = Hof 2, Tisch 82, Nummer 5
Maier, Arda, = Hof 2, Tisch 78, Nummer 3
Maier, Christian, = Hof 2, Tisch 78, Nummer 5
Maier, Olga, = Hof 2, Tisch 78, Nummer 4
Maier, Sarah, = Hof 2, Tisch 78, Nummer 2
Neumann, Jennifer, = Hof 1, Tisch 42, Nummer 3
Neumann, Lina, = Hof 1, Tisch 42, Nummer 5
Neumann, Matteo, = Hof 1, Tisch 42, Nummer 2
Neumann, Thomas, = Hof 1, Tisch 42, Nummer 4
Nguyen, Anja, = Hof 2, Tisch 62, Nummer 3
Nguyen, Daniel, = Hof 2, Tisch 62, Nummer 4
Nguyen, Julia, = Hof 1, Tisch 15, Nummer 3
Nguyen, Sofia, = Hof 2, Tisch 62, Nummer 2
Nowak, Aylin, = Hof 2, Tisch 52, Nummer 4
Nowak, Felix, = Hof 2, Tisch 53, Nummer 5
Nowak, Kathrin, = Hof 2, Tisch 52, Nummer 2
Nowak, Lina, = Hof 2, Tisch 53, Nummer 2
Nowak, Markus, = Hof 2, Tisch 53, Nummer 4
Nowak, Noah, = Hof 2, Tisch 53, Nummer 3
Ott, Daniel, = Hof 1, Tisch 13, Nummer 4
Ott, Hannah, = Hof 1, Tisch 12, Nummer 2
Ott, Luca, = Hof 2, Tisch 75, Nummer 5
Ott, Matthias, = Hof 1, Tisch 12, Nummer 3
Ott, Melanie, = Hof 1, Tisch 13, Nummer 5
Ott, Olga, = Hof 1, Tisch 12, Nummer 4
Ott, Sergej, Prof. Dr. = Hof 1, Tisch 44, Nummer 2
Ott, Yusuf, Prof. Dr. = Hof 2, Tisch 81, Nummer 4
Peters, Christian, = Hof 1, Tisch 10, Nummer 4
Peters, Nicole, = Hof 1, Tisch 45, Nummer 3
Petrov, Giovanna, = Hof 1, Tisch 10, Nummer 5
Petrov, Kathrin, = Hof 2, Tisch 46, Nummer 3
Petrov, Kathrin, Prof. Dr. = Hof 1, Tisch 11, Nummer 5
Petrov, Matthias, = Hof 2, Tisch 59, Nummer 3
Petrov, Milan, = Hof 2, Tisch 46, Nummer 5
Petrov, Sergej, = Hof 1, Tisch 31, Nummer 4
Pohl, Dragana, = Hof 2, Tisch 77, Nummer 3
Pohl, Emre, = Hof 2, Tisch 77, Nummer 2
Pohl, Mia, = Hof 2, Tisch 77, Nummer 5
Pohl, Theo, = Hof 2, Tisch 77, Nummer 4
Polat, Kathrin, = Hof 1, Tisch 31, Nummer 5
Polat, Katrin, = Hof 2, Tisch 69, Nummer 2
Polat, Marija, = Hof 1, Tisch 30, Nummer 5
Polat, Markus, = Hof 2, Tisch 69, Nummer 3
Polat, Sarah, = Hof 2, Tisch 70, Nummer 2
Polat, Stefan, = Hof 2, Tisch 70, Nummer 5
Polat, Stefanie, = Hof 2, Tisch 69, Nummer 4
Popescu, Matthias, = Hof 1, Tisch 28, Nummer 5
Richter, Anja, = Hof 1, Tisch 2, Nummer 2
Richter, Anja, Prof. Dr. = Hof 2, Tisch 74, Nummer 3
Richter, Anton, = Hof 1, Tisch 2, Nummer 5
Richter, Greta, = Hof 1, Tisch 2, Nummer 4
Richter, Julia, Prof. Dr. = Hof 2, Tisch 59, Nummer 4
Richter, Marco, = Hof 1, Tisch 30, Nummer 3
Richter, Matthias, = Hof 1, Tisch 2, Nummer 3
Richter, Stefan, = Hof 2, Tisch 66, Nummer 2
Rossi, Emilia, = Hof 2, Tisch 88, Nummer 2
Rossi, Luca, = Hof 2, Tisch 88, Nummer 4
Rossi, Melanie, = Hof 2, Tisch 88, Nummer 3
Roth, Christian, = Hof 2, Tisch 73, Nummer 3
Roth, Julia, Prof. Dr. = Hof 1, Tisch 15, Nummer 4
Roth, Olga, = Hof 2, Tisch 58, Nummer 4
Roth, Sarah, = Hof 2, Tisch 73, Nummer 2
Roth, Sofia, = Hof 2, Tisch 73, Nummer 4
Sahin, Kathrin, = Hof 2, Tisch 80, Nummer 3
Sahin, Markus, = Hof 2, Tisch 80, Nummer 4
Sahin, Nicole, = Hof 2, Tisch 80, Nummer 2
Sahin, Patrick, = Hof 2, Tisch 80, Nummer 5
Sahin, Thomas, = Hof 2, Tisch 74, Nummer 2
Schmidt, Giovanna, = Hof 2, Tisch 60, Nummer 3
Schmidt, Marie, = Hof 2, Tisch 60, Nummer 2
Schmidt, Matthias, = Hof 2, Tisch 60, Nummer 4
Schmidt, Nicole, = Hof 1, Tisch 16, Nummer 4
Schmitt, Dennis, Prof. Dr. = Hof 2, Tisch 66, Nummer 3
Schmitt, Markus, = Hof 1, Tisch 11, Nummer 4
Schmitt, Markus, = Hof 2, Tisch 87, Nummer 4
Schmitt, Sarah, = Hof 2, Tisch 87, Nummer 3
Schmitt, Sofia, = Hof 2, Tisch 87, Nummer 2
Schmitt, Stefan, Prof. Dr. = Hof 2, Tisch 72, Nummer 4
Schwarz, Giovanna, = Hof 2, Tisch 65, Nummer 3
Seidel, Anja, Dr. = Hof 1, Tisch 21, Nummer 2
Seidel, Markus, Dr. = Hof 1, Tisch 10, Nummer 3
Seidel, Yusuf, Prof. Dr. = Hof 1, Tisch 3, Nummer 2
Seifert, Elif, = Hof 2, Tisch 68, Nummer 2
Seifert, Leni, = Hof 2, Tisch 76, Nummer 2
Seifert, Marco, Prof. Dr. = Hof 2, Tisch 65, Nummer 4
Seifert, Nicole, = Hof 2, Tisch 76, Nummer 4
Seifert, Stefanie, Dr. = Hof 1, Tisch 15, Nummer 2
Seifert, Timo, = Hof 2, Tisch 76, Nummer 3
Seifert, Yusuf, = Hof 2, Tisch 68, Nummer 3
Simon, Andreas, = Hof 2, Tisch 84, Nummer 2
Simon, Emma, = Hof 2, Tisch 84, Nummer 4
Simon, Lena, = Hof 2, Tisch 84, Nummer 5
Simon, Markus, = Hof 2, Tisch 84, Nummer 3
Simon, Svenja, = Hof 1, Tisch 28, Nummer 2
Stein, Ben, = Hof 2, Tisch 63, Nummer 2
Stein, Emilia, = Hof 2, Tisch 63, Nummer 3
Stein, Giovanna, = Hof 2, Tisch 63, Nummer 4
Stein, Markus, Prof. Dr. = Hof 1, Tisch 28, Nummer 4
Stein, Nadine, Dr. = Hof 1, Tisch 11, Nummer 2
Stein, Thomas, = Hof 2, Tisch 63, Nummer 5
Steinbach, Elisabeth, = Hof 1, Tisch 6, Nummer 5
Steinbach, Georg, = Hof 1, Tisch 6, Nummer 2
Thiel, Christina, = Hof 2, Tisch 86, Nummer 3
Thiel, Jan, Dr. = Hof 2, Tisch 74, Nummer 4
Thiel, Julia, = Hof 2, Tisch 86, Nummer 4
Thiel, Yusuf, = Hof 2, Tisch 86, Nummer 2
Tran, Marija, = Hof 2, Tisch 72, Nummer 2
Tran, Olga, = Hof 1, Tisch 21, Nummer 5
Tran, Thomas, Prof. Dr. = Hof 1, Tisch 31, Nummer 3
Wagner, Henry, = Hof 1, Tisch 29, Nummer 5
Wagner, Marco, = Hof 1, Tisch 29, Nummer 3
Wagner, Nicole, = Hof 1, Tisch 29, Nummer 2
Wagner, Sarah, = Hof 1, Tisch 29, Nummer 4
Weber, Andreas, = Hof 2, Tisch 59, Nummer 2
Weber, Julia, = Hof 1, Tisch 11, Nummer 3
Weber, Katrin, = Hof 2, Tisch 71, Nummer 3
Weber, Yusuf, = Hof 2, Tisch 65, Nummer 2
Werner, Andreas, = Hof 1, Tisch 19, Nummer 3
Werner, Dennis, Prof. Dr. = Hof 1, Tisch 21, Nummer 3
Werner, Katrin, = Hof 2, Tisch 64, Nummer 2
Werner, Luca, = Hof 2, Tisch 72, Nummer 3
Werner, Marco, = Hof 1, Tisch 19, Nummer 4
Werner, Nadine, = Hof 1, Tisch 19, Nummer 2
Werner, Sandra, = Hof 1, Tisch 19, Nummer 5
Winkler, Lena, = Hof 2, Tisch 46, Nummer 4
Winkler, Marco, = Hof 1, Tisch 4, Nummer 3
Winkler, Patrick, = Hof 2, Tisch 46, Nummer 2
Winkler, Thomas, = Hof 1, Tisch 15, Nummer 5
Wolf, Ben, = Hof 1, Tisch 43, Nummer 5
Wolf, Emma, = Hof 1, Tisch 43, Nummer 4
Wolf, Julia, = Hof 1, Tisch 43, Nummer 2
Wolf, Thomas, = Hof 1, Tisch 43, Nummer 3
Zimmermann, Jan, = Hof 1, Tisch 37, Nummer 5
Zimmermann, Markus, = Hof 1, Tisch 37, Nummer 3
Zimmermann, Mia, = Hof 1, Tisch 37, Nummer 2
Zimmermann, Patrick, = Hof 2, Tisch 75, Nummer 2
Zimmermann, Svenja, = Hof 1, Tisch 37, Nummer 4
1 Aksoy Luca = Hof 1 Tisch 32 Nummer 5
2 Aksoy Marie = Hof 1 Tisch 33 Nummer 4
3 Aksoy Marija = Hof 1 Tisch 31 Nummer 2
4 Aksoy Michael Dr. = Hof 2 Tisch 64 Nummer 5
5 Aksoy Nadine = Hof 1 Tisch 32 Nummer 4
6 Aksoy Nicole = Hof 1 Tisch 24 Nummer 5
7 Aksoy Sergej = Hof 1 Tisch 32 Nummer 2
8 Aksoy Sofia = Hof 1 Tisch 32 Nummer 3
9 Aksoy Thomas = Hof 1 Tisch 33 Nummer 5
10 Albrecht Andreas = Hof 1 Tisch 20 Nummer 4
11 Albrecht Emre Prof. Dr. = Hof 2 Tisch 57 Nummer 2
12 Albrecht Sarah = Hof 1 Tisch 20 Nummer 2
13 Albrecht Yusuf Prof. Dr. = Hof 1 Tisch 28 Nummer 3
14 Arnold Christian = Hof 2 Tisch 47 Nummer 2
15 Arnold Jennifer = Hof 2 Tisch 47 Nummer 4
16 Arnold Jennifer Dr. = Hof 2 Tisch 57 Nummer 3
17 Arnold Leni = Hof 1 Tisch 20 Nummer 3
18 Arnold Mila = Hof 2 Tisch 47 Nummer 3
19 Arnold Nadine = Hof 1 Tisch 30 Nummer 2
20 Arnold Patrick = Hof 1 Tisch 20 Nummer 5
21 Barth Lina = Hof 2 Tisch 83 Nummer 2
22 Barth Markus = Hof 2 Tisch 68 Nummer 4
23 Barth Nadine = Hof 2 Tisch 83 Nummer 3
24 Barth Patrick = Hof 2 Tisch 83 Nummer 5
25 Barth Timo = Hof 2 Tisch 83 Nummer 4
26 Bauer Dragana = Hof 1 Tisch 44 Nummer 3
27 Bauer Yusuf = Hof 1 Tisch 3 Nummer 5
28 Becker Jan = Hof 2 Tisch 61 Nummer 2
29 Becker Marija = Hof 2 Tisch 55 Nummer 2
30 Becker Nicole = Hof 2 Tisch 61 Nummer 4
31 Becker Sandra = Hof 2 Tisch 61 Nummer 3
32 Berger Andreas = Hof 1 Tisch 18 Nummer 2
33 Berger Anton = Hof 1 Tisch 18 Nummer 4
34 Berger Jennifer = Hof 1 Tisch 18 Nummer 3
35 Berger Layla = Hof 1 Tisch 17 Nummer 2
36 Berger Nadine Dr. = Hof 2 Tisch 81 Nummer 2
37 Berger Stefan = Hof 1 Tisch 17 Nummer 4
38 Berger Svenja = Hof 1 Tisch 17 Nummer 3
39 Bianchi Jan Dr. = Hof 1 Tisch 4 Nummer 2
40 Bianchi Mia = Hof 2 Tisch 50 Nummer 4
41 Bianchi Sandra = Hof 2 Tisch 50 Nummer 3
42 Bianchi Thomas = Hof 2 Tisch 50 Nummer 2
43 Bianchi Thomas = Hof 2 Tisch 58 Nummer 5
44 Braun Christian = Hof 1 Tisch 41 Nummer 3
45 Braun Dragana = Hof 1 Tisch 40 Nummer 4
46 Braun Jan = Hof 1 Tisch 40 Nummer 3
47 Braun Julia = Hof 1 Tisch 41 Nummer 4
48 Braun Milan = Hof 1 Tisch 41 Nummer 2
49 Braun Nadine = Hof 1 Tisch 40 Nummer 2
50 Busch Emre = Hof 1 Tisch 22 Nummer 4
51 Busch Marie = Hof 1 Tisch 22 Nummer 5
52 Busch Michael = Hof 1 Tisch 22 Nummer 2
53 Busch Olga = Hof 1 Tisch 22 Nummer 3
54 Busch Patrick = Hof 2 Tisch 54 Nummer 3
55 Celik Elif = Hof 1 Tisch 33 Nummer 2
56 Celik Patrick = Hof 1 Tisch 33 Nummer 3
57 Dallmann Charlotte = Hof 1 Tisch 7 Nummer 2
58 Dallmann Heinrich = Hof 1 Tisch 7 Nummer 4
59 Dallmann Margarete = Hof 1 Tisch 7 Nummer 5
60 Dallmann Maximilian = Hof 1 Tisch 7 Nummer 3
61 Demir Sandra Dr. = Hof 1 Tisch 45 Nummer 5
62 Dietrich Dragana Prof. Dr. = Hof 2 Tisch 54 Nummer 4
63 Dietrich Jennifer = Hof 2 Tisch 85 Nummer 4
64 Dietrich Marco = Hof 2 Tisch 85 Nummer 3
65 Dietrich Sofia = Hof 2 Tisch 85 Nummer 2
66 Engel Anja = Hof 2 Tisch 58 Nummer 3
67 Engel Anton = Hof 2 Tisch 48 Nummer 3
68 Engel Julia = Hof 2 Tisch 48 Nummer 4
69 Engel Nicole = Hof 2 Tisch 48 Nummer 2
70 Engel Yusuf = Hof 2 Tisch 58 Nummer 2
71 Esposito Jan Dr. = Hof 1 Tisch 16 Nummer 2
72 Esposito Yusuf Prof. Dr. = Hof 2 Tisch 55 Nummer 4
73 Frank Dennis = Hof 1 Tisch 26 Nummer 3
74 Frank Finn = Hof 1 Tisch 25 Nummer 3
75 Frank Lena = Hof 1 Tisch 25 Nummer 2
76 Frank Marie = Hof 1 Tisch 26 Nummer 2
77 Frank Nadine = Hof 1 Tisch 26 Nummer 4
78 Franke Amira = Hof 2 Tisch 79 Nummer 2
79 Franke Julia = Hof 2 Tisch 70 Nummer 3
80 Franke Marco = Hof 2 Tisch 79 Nummer 3
81 Franke Michael = Hof 2 Tisch 79 Nummer 4
82 Franke Olga = Hof 2 Tisch 70 Nummer 4
83 Freifrau von Waldeck Antonia = Hof 1 Tisch 6 Nummer 3
84 Freiherr von Waldeck Konstantin Dr. = Hof 1 Tisch 6 Nummer 4
85 Grothe Alexander Dr. h.c. = Hof 1 Tisch 5 Nummer 3
86 Grothe Beate = Hof 1 Tisch 5 Nummer 5
87 Haas Jennifer Dr. = Hof 2 Tisch 68 Nummer 5
88 Haas Patrick = Hof 1 Tisch 44 Nummer 4
89 Hansen Giovanna = Hof 1 Tisch 1 Nummer 3
90 Hansen Markus = Hof 1 Tisch 1 Nummer 4
91 Hansen Nicole Dr. = Hof 1 Tisch 45 Nummer 2
92 Hansen Olga = Hof 1 Tisch 1 Nummer 2
93 Hansen Thomas = Hof 1 Tisch 16 Nummer 3
94 Hartmann Deniz = Hof 1 Tisch 13 Nummer 2
95 Hartmann Marija = Hof 1 Tisch 23 Nummer 4
96 Hartmann Nadine = Hof 1 Tisch 13 Nummer 3
97 Hartmann Olga = Hof 1 Tisch 23 Nummer 2
98 Hartmann Thomas = Hof 1 Tisch 23 Nummer 3
99 Hoffmann Emre = Hof 2 Tisch 54 Nummer 2
100 Hoffmann Patrick Prof. Dr. = Hof 1 Tisch 30 Nummer 4
101 Hoffmann Thomas Dr. = Hof 1 Tisch 21 Nummer 4
102 Horn Kathrin = Hof 1 Tisch 39 Nummer 2
103 Horn Layla = Hof 1 Tisch 39 Nummer 4
104 Horn Leon = Hof 1 Tisch 39 Nummer 5
105 Horn Michael = Hof 1 Tisch 39 Nummer 3
106 Kaya Christina = Hof 2 Tisch 56 Nummer 2
107 Kaya Luca = Hof 2 Tisch 56 Nummer 4
108 Kaya Nicole = Hof 2 Tisch 56 Nummer 3
109 Kaya Patrick Dr. = Hof 1 Tisch 3 Nummer 4
110 Kaya Timo Prof. Dr. = Hof 1 Tisch 45 Nummer 4
111 Kessler Ferdinand Dr. = Hof 1 Tisch 5 Nummer 2
112 Kessler Viktoria = Hof 1 Tisch 5 Nummer 4
113 Klein Emre = Hof 1 Tisch 3 Nummer 3
114 Klein Lina = Hof 2 Tisch 67 Nummer 4
115 Klein Markus = Hof 1 Tisch 24 Nummer 2
116 Klein Milan = Hof 2 Tisch 67 Nummer 3
117 Klein Olga Dr. = Hof 2 Tisch 57 Nummer 4
118 Klein Stefanie = Hof 2 Tisch 67 Nummer 2
119 Koch Emil = Hof 1 Tisch 34 Nummer 4
120 Koch Jan = Hof 1 Tisch 34 Nummer 2
121 Koch Kathrin Dr. = Hof 2 Tisch 71 Nummer 2
122 Koch Marie = Hof 1 Tisch 34 Nummer 5
123 Koch Michael = Hof 1 Tisch 25 Nummer 5
124 Koch Olga = Hof 1 Tisch 34 Nummer 3
125 Koch Sarah = Hof 1 Tisch 25 Nummer 4
126 Kovac Anja = Hof 1 Tisch 27 Nummer 2
127 Kovac Henry = Hof 1 Tisch 35 Nummer 2
128 Kovac Lena = Hof 1 Tisch 27 Nummer 4
129 Kovac Luca = Hof 2 Tisch 75 Nummer 3
130 Kovac Marco = Hof 1 Tisch 27 Nummer 3
131 Kovac Marija = Hof 1 Tisch 35 Nummer 4
132 Kovac Matthias = Hof 2 Tisch 55 Nummer 3
133 Kovac Tobias = Hof 1 Tisch 35 Nummer 3
134 Kraus Markus = Hof 1 Tisch 10 Nummer 2
135 Kraus Patrick Dr. = Hof 2 Tisch 65 Nummer 5
136 Krause Andreas = Hof 1 Tisch 38 Nummer 2
137 Krause Arda = Hof 1 Tisch 38 Nummer 4
138 Krause Christina Dr. = Hof 1 Tisch 4 Nummer 4
139 Krause Daniel = Hof 1 Tisch 24 Nummer 4
140 Krause Dragana Dr. = Hof 2 Tisch 75 Nummer 4
141 Krause Emma = Hof 1 Tisch 38 Nummer 3
142 Krause Julia = Hof 1 Tisch 38 Nummer 5
143 Krause Matthias = Hof 2 Tisch 64 Nummer 4
144 Krause Thomas = Hof 1 Tisch 44 Nummer 5
145 Kuhn Julia Dr. = Hof 2 Tisch 71 Nummer 4
146 Lange Dragana = Hof 1 Tisch 36 Nummer 3
147 Lange Jan = Hof 2 Tisch 52 Nummer 3
148 Lange Michael = Hof 1 Tisch 36 Nummer 4
149 Lange Sergej = Hof 2 Tisch 54 Nummer 5
150 Lange Sofia = Hof 2 Tisch 52 Nummer 5
151 Lange Svenja = Hof 1 Tisch 36 Nummer 2
152 Lehmann Giovanna = Hof 1 Tisch 8 Nummer 5
153 Lehmann Layla = Hof 1 Tisch 8 Nummer 3
154 Lehmann Markus = Hof 1 Tisch 9 Nummer 2
155 Lehmann Milan = Hof 1 Tisch 8 Nummer 2
156 Lehmann Nicole = Hof 1 Tisch 8 Nummer 4
157 Lehmann Patrick = Hof 2 Tisch 81 Nummer 3
158 Lehmann Thomas = Hof 1 Tisch 9 Nummer 3
159 Ludwig Aylin Dr. = Hof 2 Tisch 64 Nummer 3
160 Ludwig Elias = Hof 2 Tisch 82 Nummer 2
161 Ludwig Elif = Hof 2 Tisch 82 Nummer 3
162 Ludwig Emre = Hof 2 Tisch 82 Nummer 4
163 Ludwig Emre Prof. Dr. = Hof 1 Tisch 24 Nummer 3
164 Ludwig Giovanna = Hof 2 Tisch 82 Nummer 5
165 Maier Arda = Hof 2 Tisch 78 Nummer 3
166 Maier Christian = Hof 2 Tisch 78 Nummer 5
167 Maier Olga = Hof 2 Tisch 78 Nummer 4
168 Maier Sarah = Hof 2 Tisch 78 Nummer 2
169 Neumann Jennifer = Hof 1 Tisch 42 Nummer 3
170 Neumann Lina = Hof 1 Tisch 42 Nummer 5
171 Neumann Matteo = Hof 1 Tisch 42 Nummer 2
172 Neumann Thomas = Hof 1 Tisch 42 Nummer 4
173 Nguyen Anja = Hof 2 Tisch 62 Nummer 3
174 Nguyen Daniel = Hof 2 Tisch 62 Nummer 4
175 Nguyen Julia = Hof 1 Tisch 15 Nummer 3
176 Nguyen Sofia = Hof 2 Tisch 62 Nummer 2
177 Nowak Aylin = Hof 2 Tisch 52 Nummer 4
178 Nowak Felix = Hof 2 Tisch 53 Nummer 5
179 Nowak Kathrin = Hof 2 Tisch 52 Nummer 2
180 Nowak Lina = Hof 2 Tisch 53 Nummer 2
181 Nowak Markus = Hof 2 Tisch 53 Nummer 4
182 Nowak Noah = Hof 2 Tisch 53 Nummer 3
183 Ott Daniel = Hof 1 Tisch 13 Nummer 4
184 Ott Hannah = Hof 1 Tisch 12 Nummer 2
185 Ott Luca = Hof 2 Tisch 75 Nummer 5
186 Ott Matthias = Hof 1 Tisch 12 Nummer 3
187 Ott Melanie = Hof 1 Tisch 13 Nummer 5
188 Ott Olga = Hof 1 Tisch 12 Nummer 4
189 Ott Sergej Prof. Dr. = Hof 1 Tisch 44 Nummer 2
190 Ott Yusuf Prof. Dr. = Hof 2 Tisch 81 Nummer 4
191 Peters Christian = Hof 1 Tisch 10 Nummer 4
192 Peters Nicole = Hof 1 Tisch 45 Nummer 3
193 Petrov Giovanna = Hof 1 Tisch 10 Nummer 5
194 Petrov Kathrin = Hof 2 Tisch 46 Nummer 3
195 Petrov Kathrin Prof. Dr. = Hof 1 Tisch 11 Nummer 5
196 Petrov Matthias = Hof 2 Tisch 59 Nummer 3
197 Petrov Milan = Hof 2 Tisch 46 Nummer 5
198 Petrov Sergej = Hof 1 Tisch 31 Nummer 4
199 Pohl Dragana = Hof 2 Tisch 77 Nummer 3
200 Pohl Emre = Hof 2 Tisch 77 Nummer 2
201 Pohl Mia = Hof 2 Tisch 77 Nummer 5
202 Pohl Theo = Hof 2 Tisch 77 Nummer 4
203 Polat Kathrin = Hof 1 Tisch 31 Nummer 5
204 Polat Katrin = Hof 2 Tisch 69 Nummer 2
205 Polat Marija = Hof 1 Tisch 30 Nummer 5
206 Polat Markus = Hof 2 Tisch 69 Nummer 3
207 Polat Sarah = Hof 2 Tisch 70 Nummer 2
208 Polat Stefan = Hof 2 Tisch 70 Nummer 5
209 Polat Stefanie = Hof 2 Tisch 69 Nummer 4
210 Popescu Matthias = Hof 1 Tisch 28 Nummer 5
211 Richter Anja = Hof 1 Tisch 2 Nummer 2
212 Richter Anja Prof. Dr. = Hof 2 Tisch 74 Nummer 3
213 Richter Anton = Hof 1 Tisch 2 Nummer 5
214 Richter Greta = Hof 1 Tisch 2 Nummer 4
215 Richter Julia Prof. Dr. = Hof 2 Tisch 59 Nummer 4
216 Richter Marco = Hof 1 Tisch 30 Nummer 3
217 Richter Matthias = Hof 1 Tisch 2 Nummer 3
218 Richter Stefan = Hof 2 Tisch 66 Nummer 2
219 Rossi Emilia = Hof 2 Tisch 88 Nummer 2
220 Rossi Luca = Hof 2 Tisch 88 Nummer 4
221 Rossi Melanie = Hof 2 Tisch 88 Nummer 3
222 Roth Christian = Hof 2 Tisch 73 Nummer 3
223 Roth Julia Prof. Dr. = Hof 1 Tisch 15 Nummer 4
224 Roth Olga = Hof 2 Tisch 58 Nummer 4
225 Roth Sarah = Hof 2 Tisch 73 Nummer 2
226 Roth Sofia = Hof 2 Tisch 73 Nummer 4
227 Sahin Kathrin = Hof 2 Tisch 80 Nummer 3
228 Sahin Markus = Hof 2 Tisch 80 Nummer 4
229 Sahin Nicole = Hof 2 Tisch 80 Nummer 2
230 Sahin Patrick = Hof 2 Tisch 80 Nummer 5
231 Sahin Thomas = Hof 2 Tisch 74 Nummer 2
232 Schmidt Giovanna = Hof 2 Tisch 60 Nummer 3
233 Schmidt Marie = Hof 2 Tisch 60 Nummer 2
234 Schmidt Matthias = Hof 2 Tisch 60 Nummer 4
235 Schmidt Nicole = Hof 1 Tisch 16 Nummer 4
236 Schmitt Dennis Prof. Dr. = Hof 2 Tisch 66 Nummer 3
237 Schmitt Markus = Hof 1 Tisch 11 Nummer 4
238 Schmitt Markus = Hof 2 Tisch 87 Nummer 4
239 Schmitt Sarah = Hof 2 Tisch 87 Nummer 3
240 Schmitt Sofia = Hof 2 Tisch 87 Nummer 2
241 Schmitt Stefan Prof. Dr. = Hof 2 Tisch 72 Nummer 4
242 Schwarz Giovanna = Hof 2 Tisch 65 Nummer 3
243 Seidel Anja Dr. = Hof 1 Tisch 21 Nummer 2
244 Seidel Markus Dr. = Hof 1 Tisch 10 Nummer 3
245 Seidel Yusuf Prof. Dr. = Hof 1 Tisch 3 Nummer 2
246 Seifert Elif = Hof 2 Tisch 68 Nummer 2
247 Seifert Leni = Hof 2 Tisch 76 Nummer 2
248 Seifert Marco Prof. Dr. = Hof 2 Tisch 65 Nummer 4
249 Seifert Nicole = Hof 2 Tisch 76 Nummer 4
250 Seifert Stefanie Dr. = Hof 1 Tisch 15 Nummer 2
251 Seifert Timo = Hof 2 Tisch 76 Nummer 3
252 Seifert Yusuf = Hof 2 Tisch 68 Nummer 3
253 Simon Andreas = Hof 2 Tisch 84 Nummer 2
254 Simon Emma = Hof 2 Tisch 84 Nummer 4
255 Simon Lena = Hof 2 Tisch 84 Nummer 5
256 Simon Markus = Hof 2 Tisch 84 Nummer 3
257 Simon Svenja = Hof 1 Tisch 28 Nummer 2
258 Stein Ben = Hof 2 Tisch 63 Nummer 2
259 Stein Emilia = Hof 2 Tisch 63 Nummer 3
260 Stein Giovanna = Hof 2 Tisch 63 Nummer 4
261 Stein Markus Prof. Dr. = Hof 1 Tisch 28 Nummer 4
262 Stein Nadine Dr. = Hof 1 Tisch 11 Nummer 2
263 Stein Thomas = Hof 2 Tisch 63 Nummer 5
264 Steinbach Elisabeth = Hof 1 Tisch 6 Nummer 5
265 Steinbach Georg = Hof 1 Tisch 6 Nummer 2
266 Thiel Christina = Hof 2 Tisch 86 Nummer 3
267 Thiel Jan Dr. = Hof 2 Tisch 74 Nummer 4
268 Thiel Julia = Hof 2 Tisch 86 Nummer 4
269 Thiel Yusuf = Hof 2 Tisch 86 Nummer 2
270 Tran Marija = Hof 2 Tisch 72 Nummer 2
271 Tran Olga = Hof 1 Tisch 21 Nummer 5
272 Tran Thomas Prof. Dr. = Hof 1 Tisch 31 Nummer 3
273 Wagner Henry = Hof 1 Tisch 29 Nummer 5
274 Wagner Marco = Hof 1 Tisch 29 Nummer 3
275 Wagner Nicole = Hof 1 Tisch 29 Nummer 2
276 Wagner Sarah = Hof 1 Tisch 29 Nummer 4
277 Weber Andreas = Hof 2 Tisch 59 Nummer 2
278 Weber Julia = Hof 1 Tisch 11 Nummer 3
279 Weber Katrin = Hof 2 Tisch 71 Nummer 3
280 Weber Yusuf = Hof 2 Tisch 65 Nummer 2
281 Werner Andreas = Hof 1 Tisch 19 Nummer 3
282 Werner Dennis Prof. Dr. = Hof 1 Tisch 21 Nummer 3
283 Werner Katrin = Hof 2 Tisch 64 Nummer 2
284 Werner Luca = Hof 2 Tisch 72 Nummer 3
285 Werner Marco = Hof 1 Tisch 19 Nummer 4
286 Werner Nadine = Hof 1 Tisch 19 Nummer 2
287 Werner Sandra = Hof 1 Tisch 19 Nummer 5
288 Winkler Lena = Hof 2 Tisch 46 Nummer 4
289 Winkler Marco = Hof 1 Tisch 4 Nummer 3
290 Winkler Patrick = Hof 2 Tisch 46 Nummer 2
291 Winkler Thomas = Hof 1 Tisch 15 Nummer 5
292 Wolf Ben = Hof 1 Tisch 43 Nummer 5
293 Wolf Emma = Hof 1 Tisch 43 Nummer 4
294 Wolf Julia = Hof 1 Tisch 43 Nummer 2
295 Wolf Thomas = Hof 1 Tisch 43 Nummer 3
296 Zimmermann Jan = Hof 1 Tisch 37 Nummer 5
297 Zimmermann Markus = Hof 1 Tisch 37 Nummer 3
298 Zimmermann Mia = Hof 1 Tisch 37 Nummer 2
299 Zimmermann Patrick = Hof 2 Tisch 75 Nummer 2
300 Zimmermann Svenja = Hof 1 Tisch 37 Nummer 4
+568
View File
@@ -0,0 +1,568 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="2476.05" height="466.676" viewBox="0 0 2476.05 466.676">
<rect width="100%" height="100%" fill="white"/>
<circle cx="142.003" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="142.003" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="142.003" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">22</text>
<circle cx="507.344" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="507.344" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="507.344" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">26</text>
<circle cx="1740.37" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1740.37" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="1740.37" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">77</text>
<circle cx="2105.71" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2105.71" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="2105.71" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">81</text>
<circle cx="1968.71" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1968.71" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="1968.71" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">86</text>
<circle cx="370.341" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="370.341" cy="306.006" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="370.341" cy="343.341" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="370.341" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="370.341" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">33</text>
<circle cx="735.682" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="735.682" cy="306.006" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="735.682" cy="343.341" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="735.682" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="735.682" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">37</text>
<circle cx="50.6676" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="50.6676" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="66.8343" cy="60.0014" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="34.501" cy="60.0014" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="50.6676" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="50.6676" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">1</text>
<circle cx="233.338" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="233.338" cy="32" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="249.505" cy="60.0014" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="217.171" cy="60.0014" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="233.338" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="233.338" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">3</text>
<circle cx="187.67" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="187.67" cy="123.335" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="203.837" cy="151.337" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="171.504" cy="151.337" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="187.67" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="187.67" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">13</text>
<circle cx="370.341" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="370.341" cy="123.335" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="386.508" cy="151.337" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="354.174" cy="151.337" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="370.341" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="370.341" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">15</text>
<circle cx="553.011" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="553.011" cy="123.335" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="569.178" cy="151.337" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="536.845" cy="151.337" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="553.011" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="553.011" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">17</text>
<circle cx="735.682" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="735.682" cy="123.335" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="751.849" cy="151.337" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="719.515" cy="151.337" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="735.682" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="735.682" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">19</text>
<circle cx="324.673" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="324.673" cy="214.67" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="340.84" cy="242.672" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="308.507" cy="242.672" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="324.673" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="324.673" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">24</text>
<circle cx="416.009" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="416.009" cy="214.67" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="432.175" cy="242.672" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="399.842" cy="242.672" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="416.009" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="416.009" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">25</text>
<circle cx="279.006" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="279.006" cy="306.006" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="295.172" cy="334.007" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="262.839" cy="334.007" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="279.006" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="279.006" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">32</text>
<circle cx="2151.38" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2151.38" cy="32" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2167.54" cy="60.0014" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2135.21" cy="60.0014" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2151.38" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="2151.38" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">53</text>
<circle cx="1557.7" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1557.7" cy="123.335" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="1573.87" cy="151.337" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="1541.53" cy="151.337" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="1557.7" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="1557.7" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">57</text>
<circle cx="1649.03" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1649.03" cy="123.335" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="1665.2" cy="151.337" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="1632.87" cy="151.337" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="1649.03" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="1649.03" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">58</text>
<circle cx="1740.37" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1740.37" cy="123.335" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="1756.54" cy="151.337" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="1724.2" cy="151.337" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="1740.37" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="1740.37" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">59</text>
<circle cx="1831.7" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1831.7" cy="123.335" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="1847.87" cy="151.337" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="1815.54" cy="151.337" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="1831.7" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="1831.7" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">60</text>
<circle cx="1923.04" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1923.04" cy="123.335" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="1939.21" cy="151.337" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="1906.87" cy="151.337" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="1923.04" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="1923.04" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">61</text>
<circle cx="2197.05" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2197.05" cy="123.335" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="2213.21" cy="151.337" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="2180.88" cy="151.337" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="2197.05" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="2197.05" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">64</text>
<circle cx="2288.38" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2288.38" cy="123.335" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2304.55" cy="151.337" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2272.21" cy="151.337" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2288.38" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="2288.38" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">65</text>
<circle cx="2379.72" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2379.72" cy="123.335" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="2395.88" cy="151.337" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="2363.55" cy="151.337" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="2379.72" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="2379.72" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">66</text>
<circle cx="1786.04" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1786.04" cy="214.67" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1802.2" cy="242.672" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1769.87" cy="242.672" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1786.04" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="1786.04" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">69</text>
<circle cx="1968.71" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1968.71" cy="214.67" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1984.87" cy="242.672" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1952.54" cy="242.672" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1968.71" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="1968.71" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">71</text>
<circle cx="2060.04" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2060.04" cy="214.67" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2076.21" cy="242.672" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2043.88" cy="242.672" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2060.04" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="2060.04" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">72</text>
<circle cx="1831.7" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1831.7" cy="306.006" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1847.87" cy="334.007" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1815.54" cy="334.007" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1831.7" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="1831.7" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">78</text>
<circle cx="2014.38" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2014.38" cy="306.006" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="2030.54" cy="334.007" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1998.21" cy="334.007" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="2014.38" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="2014.38" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">80</text>
<circle cx="2197.05" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2197.05" cy="306.006" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="2213.21" cy="334.007" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="2180.88" cy="334.007" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="2197.05" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="2197.05" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">82</text>
<circle cx="2288.38" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2288.38" cy="306.006" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="2304.55" cy="334.007" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="2272.21" cy="334.007" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="2288.38" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="2288.38" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">83</text>
<circle cx="1786.04" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1786.04" cy="397.341" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1802.2" cy="425.342" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1769.87" cy="425.342" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1786.04" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="1786.04" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">84</text>
<circle cx="2060.04" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2060.04" cy="397.341" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2076.21" cy="425.342" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2043.88" cy="425.342" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2060.04" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="2060.04" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">87</text>
<circle cx="2151.38" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2151.38" cy="397.341" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="2167.54" cy="425.342" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="2135.21" cy="425.342" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="2151.38" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="2151.38" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">88</text>
<circle cx="142.003" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="142.003" cy="32" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="160.67" cy="50.6676" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="142.003" cy="69.3352" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="123.335" cy="50.6676" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="142.003" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="142.003" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">2</text>
<circle cx="324.673" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="324.673" cy="32" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="343.341" cy="50.6676" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="324.673" cy="69.3352" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="306.006" cy="50.6676" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="324.673" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="324.673" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">4</text>
<circle cx="416.009" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="416.009" cy="32" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="434.676" cy="50.6676" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="416.009" cy="69.3352" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="397.341" cy="50.6676" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="416.009" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="416.009" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">5</text>
<circle cx="507.344" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="507.344" cy="32" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="526.011" cy="50.6676" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="507.344" cy="69.3352" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="488.676" cy="50.6676" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="507.344" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="507.344" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">6</text>
<circle cx="598.679" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="598.679" cy="32" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="617.347" cy="50.6676" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="598.679" cy="69.3352" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="580.011" cy="50.6676" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="598.679" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="598.679" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">7</text>
<circle cx="690.014" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="690.014" cy="32" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="708.682" cy="50.6676" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="690.014" cy="69.3352" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="671.347" cy="50.6676" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="690.014" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="690.014" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">8</text>
<circle cx="781.35" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="781.35" cy="32" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="800.017" cy="50.6676" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="781.35" cy="69.3352" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="762.682" cy="50.6676" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="781.35" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="781.35" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">9</text>
<circle cx="872.685" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="872.685" cy="32" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="891.352" cy="50.6676" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="872.685" cy="69.3352" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="854.017" cy="50.6676" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="872.685" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="872.685" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">10</text>
<circle cx="964.02" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="964.02" cy="32" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="982.688" cy="50.6676" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="964.02" cy="69.3352" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="945.352" cy="50.6676" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="964.02" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="964.02" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">11</text>
<circle cx="96.3352" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="96.3352" cy="123.335" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="115.003" cy="142.003" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="96.3352" cy="160.67" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="77.6676" cy="142.003" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="96.3352" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="96.3352" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">12</text>
<circle cx="279.006" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="279.006" cy="123.335" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="297.673" cy="142.003" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="279.006" cy="160.67" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="260.338" cy="142.003" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="279.006" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="279.006" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">14</text>
<circle cx="461.676" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="461.676" cy="123.335" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="480.344" cy="142.003" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="461.676" cy="160.67" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="443.009" cy="142.003" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="461.676" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="461.676" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">16</text>
<circle cx="644.347" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="644.347" cy="123.335" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="663.014" cy="142.003" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="644.347" cy="160.67" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="625.679" cy="142.003" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="644.347" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="644.347" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">18</text>
<circle cx="827.017" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="827.017" cy="123.335" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="845.685" cy="142.003" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="827.017" cy="160.67" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="808.35" cy="142.003" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="827.017" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="827.017" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">20</text>
<circle cx="918.352" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="918.352" cy="123.335" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="937.02" cy="142.003" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="918.352" cy="160.67" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="899.685" cy="142.003" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="918.352" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="918.352" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">21</text>
<circle cx="233.338" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="233.338" cy="214.67" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="252.006" cy="233.338" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="233.338" cy="252.006" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="214.67" cy="233.338" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="233.338" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="233.338" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">23</text>
<circle cx="598.679" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="598.679" cy="214.67" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="617.347" cy="233.338" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="598.679" cy="252.006" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="580.011" cy="233.338" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="598.679" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="598.679" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">27</text>
<circle cx="690.014" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="690.014" cy="214.67" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="708.682" cy="233.338" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="690.014" cy="252.006" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="671.347" cy="233.338" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="690.014" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="690.014" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">28</text>
<circle cx="781.35" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="781.35" cy="214.67" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="800.017" cy="233.338" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="781.35" cy="252.006" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="762.682" cy="233.338" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="781.35" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="781.35" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">29</text>
<circle cx="872.685" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="872.685" cy="214.67" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="891.352" cy="233.338" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="872.685" cy="252.006" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="854.017" cy="233.338" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="872.685" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="872.685" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">30</text>
<circle cx="187.67" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="187.67" cy="306.006" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="206.338" cy="324.673" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="187.67" cy="343.341" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="169.003" cy="324.673" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="187.67" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="187.67" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">31</text>
<circle cx="461.676" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="461.676" cy="306.006" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="480.344" cy="324.673" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="461.676" cy="343.341" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="443.009" cy="324.673" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="461.676" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="461.676" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">34</text>
<circle cx="553.011" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="553.011" cy="306.006" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="571.679" cy="324.673" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="553.011" cy="343.341" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="534.344" cy="324.673" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="553.011" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="553.011" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">35</text>
<circle cx="644.347" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="644.347" cy="306.006" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="663.014" cy="324.673" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="644.347" cy="343.341" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="625.679" cy="324.673" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="644.347" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="644.347" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">36</text>
<circle cx="827.017" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="827.017" cy="306.006" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="845.685" cy="324.673" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="827.017" cy="343.341" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="808.35" cy="324.673" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="827.017" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="827.017" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">38</text>
<circle cx="233.338" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="233.338" cy="397.341" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="252.006" cy="416.009" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="233.338" cy="434.676" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="214.67" cy="416.009" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="233.338" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="233.338" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">39</text>
<circle cx="324.673" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="324.673" cy="397.341" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="343.341" cy="416.009" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="324.673" cy="434.676" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="306.006" cy="416.009" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="324.673" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="324.673" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">40</text>
<circle cx="416.009" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="416.009" cy="397.341" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="434.676" cy="416.009" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="416.009" cy="434.676" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="397.341" cy="416.009" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="416.009" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="416.009" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">41</text>
<circle cx="507.344" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="507.344" cy="397.341" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="526.011" cy="416.009" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="507.344" cy="434.676" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="488.676" cy="416.009" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="507.344" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="507.344" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">42</text>
<circle cx="598.679" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="598.679" cy="397.341" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="617.347" cy="416.009" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="598.679" cy="434.676" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="580.011" cy="416.009" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="598.679" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="598.679" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">43</text>
<circle cx="690.014" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="690.014" cy="397.341" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="708.682" cy="416.009" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="690.014" cy="434.676" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="671.347" cy="416.009" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="690.014" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="690.014" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">44</text>
<circle cx="781.35" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="781.35" cy="397.341" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="800.017" cy="416.009" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="781.35" cy="434.676" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="762.682" cy="416.009" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="781.35" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="781.35" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">45</text>
<circle cx="1512.03" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1512.03" cy="32" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="1530.7" cy="50.6676" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="1512.03" cy="69.3352" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="1493.36" cy="50.6676" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="1512.03" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="1512.03" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">46</text>
<circle cx="1603.37" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1603.37" cy="32" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1622.03" cy="50.6676" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1603.37" cy="69.3352" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1584.7" cy="50.6676" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1603.37" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="1603.37" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">47</text>
<circle cx="1694.7" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1694.7" cy="32" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1713.37" cy="50.6676" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="1694.7" cy="69.3352" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="1676.03" cy="50.6676" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1694.7" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="1694.7" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">48</text>
<circle cx="1786.04" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1786.04" cy="32" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1804.7" cy="50.6676" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1786.04" cy="69.3352" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1767.37" cy="50.6676" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="1786.04" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="1786.04" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">49</text>
<circle cx="1877.37" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1877.37" cy="32" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1896.04" cy="50.6676" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1877.37" cy="69.3352" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1858.7" cy="50.6676" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1877.37" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="1877.37" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">50</text>
<circle cx="1968.71" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1968.71" cy="32" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="1987.38" cy="50.6676" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="1968.71" cy="69.3352" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="1950.04" cy="50.6676" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="1968.71" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="1968.71" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">51</text>
<circle cx="2060.04" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2060.04" cy="32" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2078.71" cy="50.6676" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="2060.04" cy="69.3352" r="12" fill="#9a6324" stroke="black" stroke-width="1"/>
<circle cx="2041.38" cy="50.6676" r="12" fill="#dcbeff" stroke="black" stroke-width="1"/>
<circle cx="2060.04" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="2060.04" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">52</text>
<circle cx="2242.71" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2242.71" cy="32" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="2261.38" cy="50.6676" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="2242.71" cy="69.3352" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="2224.05" cy="50.6676" r="12" fill="#800000" stroke="black" stroke-width="1"/>
<circle cx="2242.71" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="2242.71" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">54</text>
<circle cx="2334.05" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2334.05" cy="32" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="2352.72" cy="50.6676" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="2334.05" cy="69.3352" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="2315.38" cy="50.6676" r="12" fill="#aaffc3" stroke="black" stroke-width="1"/>
<circle cx="2334.05" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="2334.05" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">55</text>
<circle cx="2425.38" cy="50.6676" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2425.38" cy="32" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="2444.05" cy="50.6676" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="2425.38" cy="69.3352" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="2406.72" cy="50.6676" r="12" fill="#000075" stroke="black" stroke-width="1"/>
<circle cx="2425.38" cy="50.6676" r="12" fill="white" fill-opacity="0.75"/>
<text x="2425.38" y="50.6676" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">56</text>
<circle cx="2014.38" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2014.38" cy="123.335" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="2033.04" cy="142.003" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="2014.38" cy="160.67" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="1995.71" cy="142.003" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="2014.38" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="2014.38" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">62</text>
<circle cx="2105.71" cy="142.003" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2105.71" cy="123.335" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2124.38" cy="142.003" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2105.71" cy="160.67" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2087.04" cy="142.003" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="2105.71" cy="142.003" r="12" fill="white" fill-opacity="0.75"/>
<text x="2105.71" y="142.003" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">63</text>
<circle cx="1603.37" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1603.37" cy="214.67" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="1622.03" cy="233.338" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="1603.37" cy="252.006" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="1584.7" cy="233.338" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="1603.37" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="1603.37" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">67</text>
<circle cx="1694.7" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1694.7" cy="214.67" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1713.37" cy="233.338" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1694.7" cy="252.006" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1676.03" cy="233.338" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1694.7" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="1694.7" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">68</text>
<circle cx="1877.37" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1877.37" cy="214.67" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1896.04" cy="233.338" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1877.37" cy="252.006" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="1858.7" cy="233.338" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1877.37" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="1877.37" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">70</text>
<circle cx="2151.38" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2151.38" cy="214.67" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2170.05" cy="233.338" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2151.38" cy="252.006" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2132.71" cy="233.338" r="12" fill="#42d4f4" stroke="black" stroke-width="1"/>
<circle cx="2151.38" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="2151.38" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">73</text>
<circle cx="2242.71" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2242.71" cy="214.67" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="2261.38" cy="233.338" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="2242.71" cy="252.006" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="2224.05" cy="233.338" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="2242.71" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="2242.71" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">74</text>
<circle cx="2334.05" cy="233.338" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="2334.05" cy="214.67" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="2352.72" cy="233.338" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="2334.05" cy="252.006" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="2315.38" cy="233.338" r="12" fill="#bfef45" stroke="black" stroke-width="1"/>
<circle cx="2334.05" cy="233.338" r="12" fill="white" fill-opacity="0.75"/>
<text x="2334.05" y="233.338" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">75</text>
<circle cx="1649.03" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1649.03" cy="306.006" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="1667.7" cy="324.673" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="1649.03" cy="343.341" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="1630.37" cy="324.673" r="12" fill="#fabed4" stroke="black" stroke-width="1"/>
<circle cx="1649.03" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="1649.03" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">76</text>
<circle cx="1923.04" cy="324.673" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1923.04" cy="306.006" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1941.71" cy="324.673" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1923.04" cy="343.341" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1904.37" cy="324.673" r="12" fill="#911eb4" stroke="black" stroke-width="1"/>
<circle cx="1923.04" cy="324.673" r="12" fill="white" fill-opacity="0.75"/>
<text x="1923.04" y="324.673" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">79</text>
<circle cx="1877.37" cy="416.009" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="1877.37" cy="397.341" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1896.04" cy="416.009" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1877.37" cy="434.676" r="12" fill="#f032e6" stroke="black" stroke-width="1"/>
<circle cx="1858.7" cy="416.009" r="12" fill="#469990" stroke="black" stroke-width="1"/>
<circle cx="1877.37" cy="416.009" r="12" fill="white" fill-opacity="0.75"/>
<text x="1877.37" y="416.009" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">85</text>
</svg>

After

Width:  |  Height:  |  Size: 53 KiB

+476
View File
@@ -0,0 +1,476 @@
[1]
Olga Hansen = Hof 1, Tisch 1, Nummer 1
Giovanna Hansen = Hof 1, Tisch 1, Nummer 2
Markus Hansen = Hof 1, Tisch 1, Nummer 3
[2]
Anja Richter = Hof 1, Tisch 2, Nummer 1
Matthias Richter = Hof 1, Tisch 2, Nummer 2
Greta Richter = Hof 1, Tisch 2, Nummer 3
Anton Richter = Hof 1, Tisch 2, Nummer 4
[3]
Prof. Dr. Yusuf Seidel = Hof 1, Tisch 3, Nummer 1
Emre Klein = Hof 1, Tisch 3, Nummer 2
Dr. Patrick Kaya = Hof 1, Tisch 3, Nummer 3
Yusuf Bauer = Hof 1, Tisch 3, Nummer 4
[4]
Dr. Jan Bianchi = Hof 1, Tisch 4, Nummer 1
Marco Winkler = Hof 1, Tisch 4, Nummer 2
Dr. Christina Krause = Hof 1, Tisch 4, Nummer 3
[5]
Dr. Ferdinand Kessler = Hof 1, Tisch 5, Nummer 1
Dr. h.c. Alexander Grothe = Hof 1, Tisch 5, Nummer 2
Viktoria Kessler = Hof 1, Tisch 5, Nummer 3
Beate Grothe = Hof 1, Tisch 5, Nummer 4
[6]
Georg Steinbach = Hof 1, Tisch 6, Nummer 1
Antonia Freifrau von Waldeck = Hof 1, Tisch 6, Nummer 2
Dr. Konstantin Freiherr von Waldeck = Hof 1, Tisch 6, Nummer 3
Elisabeth Steinbach = Hof 1, Tisch 6, Nummer 4
[7]
Charlotte Dallmann = Hof 1, Tisch 7, Nummer 1
Maximilian Dallmann = Hof 1, Tisch 7, Nummer 2
Heinrich Dallmann = Hof 1, Tisch 7, Nummer 3
Margarete Dallmann = Hof 1, Tisch 7, Nummer 4
[8]
Milan Lehmann = Hof 1, Tisch 8, Nummer 1
Layla Lehmann = Hof 1, Tisch 8, Nummer 2
Nicole Lehmann = Hof 1, Tisch 8, Nummer 3
Giovanna Lehmann = Hof 1, Tisch 8, Nummer 4
[9]
Markus Lehmann = Hof 1, Tisch 9, Nummer 1
Thomas Lehmann = Hof 1, Tisch 9, Nummer 2
[10]
Markus Kraus = Hof 1, Tisch 10, Nummer 1
Dr. Markus Seidel = Hof 1, Tisch 10, Nummer 2
Christian Peters = Hof 1, Tisch 10, Nummer 3
Giovanna Petrov = Hof 1, Tisch 10, Nummer 4
[11]
Dr. Nadine Stein = Hof 1, Tisch 11, Nummer 1
Julia Weber = Hof 1, Tisch 11, Nummer 2
Markus Schmitt = Hof 1, Tisch 11, Nummer 3
Prof. Dr. Kathrin Petrov = Hof 1, Tisch 11, Nummer 4
[12]
Hannah Ott = Hof 1, Tisch 12, Nummer 1
Matthias Ott = Hof 1, Tisch 12, Nummer 2
Olga Ott = Hof 1, Tisch 12, Nummer 3
[13]
Deniz Hartmann = Hof 1, Tisch 13, Nummer 1
Nadine Hartmann = Hof 1, Tisch 13, Nummer 2
Daniel Ott = Hof 1, Tisch 13, Nummer 3
Melanie Ott = Hof 1, Tisch 13, Nummer 4
[14]
[15]
Dr. Stefanie Seifert = Hof 1, Tisch 15, Nummer 1
Julia Nguyen = Hof 1, Tisch 15, Nummer 2
Prof. Dr. Julia Roth = Hof 1, Tisch 15, Nummer 3
Thomas Winkler = Hof 1, Tisch 15, Nummer 4
[16]
Dr. Jan Esposito = Hof 1, Tisch 16, Nummer 1
Thomas Hansen = Hof 1, Tisch 16, Nummer 2
Nicole Schmidt = Hof 1, Tisch 16, Nummer 3
[17]
Layla Berger = Hof 1, Tisch 17, Nummer 1
Svenja Berger = Hof 1, Tisch 17, Nummer 2
Stefan Berger = Hof 1, Tisch 17, Nummer 3
[18]
Andreas Berger = Hof 1, Tisch 18, Nummer 1
Jennifer Berger = Hof 1, Tisch 18, Nummer 2
Anton Berger = Hof 1, Tisch 18, Nummer 3
[19]
Nadine Werner = Hof 1, Tisch 19, Nummer 1
Andreas Werner = Hof 1, Tisch 19, Nummer 2
Marco Werner = Hof 1, Tisch 19, Nummer 3
Sandra Werner = Hof 1, Tisch 19, Nummer 4
[20]
Sarah Albrecht = Hof 1, Tisch 20, Nummer 1
Leni Arnold = Hof 1, Tisch 20, Nummer 2
Andreas Albrecht = Hof 1, Tisch 20, Nummer 3
Patrick Arnold = Hof 1, Tisch 20, Nummer 4
[21]
Dr. Anja Seidel = Hof 1, Tisch 21, Nummer 1
Prof. Dr. Dennis Werner = Hof 1, Tisch 21, Nummer 2
Dr. Thomas Hoffmann = Hof 1, Tisch 21, Nummer 3
Olga Tran = Hof 1, Tisch 21, Nummer 4
[22]
Michael Busch = Hof 1, Tisch 22, Nummer 1
Olga Busch = Hof 1, Tisch 22, Nummer 2
Emre Busch = Hof 1, Tisch 22, Nummer 3
Marie Busch = Hof 1, Tisch 22, Nummer 4
[23]
Olga Hartmann = Hof 1, Tisch 23, Nummer 1
Thomas Hartmann = Hof 1, Tisch 23, Nummer 2
Marija Hartmann = Hof 1, Tisch 23, Nummer 3
[24]
Markus Klein = Hof 1, Tisch 24, Nummer 1
Prof. Dr. Emre Ludwig = Hof 1, Tisch 24, Nummer 2
Daniel Krause = Hof 1, Tisch 24, Nummer 3
Nicole Aksoy = Hof 1, Tisch 24, Nummer 4
[25]
Lena Frank = Hof 1, Tisch 25, Nummer 1
Finn Frank = Hof 1, Tisch 25, Nummer 2
Sarah Koch = Hof 1, Tisch 25, Nummer 3
Michael Koch = Hof 1, Tisch 25, Nummer 4
[26]
Marie Frank = Hof 1, Tisch 26, Nummer 1
Dennis Frank = Hof 1, Tisch 26, Nummer 2
Nadine Frank = Hof 1, Tisch 26, Nummer 3
[27]
Anja Kovac = Hof 1, Tisch 27, Nummer 1
Marco Kovac = Hof 1, Tisch 27, Nummer 2
Lena Kovac = Hof 1, Tisch 27, Nummer 3
[28]
Svenja Simon = Hof 1, Tisch 28, Nummer 1
Prof. Dr. Yusuf Albrecht = Hof 1, Tisch 28, Nummer 2
Prof. Dr. Markus Stein = Hof 1, Tisch 28, Nummer 3
Matthias Popescu = Hof 1, Tisch 28, Nummer 4
[29]
Nicole Wagner = Hof 1, Tisch 29, Nummer 1
Marco Wagner = Hof 1, Tisch 29, Nummer 2
Sarah Wagner = Hof 1, Tisch 29, Nummer 3
Henry Wagner = Hof 1, Tisch 29, Nummer 4
[30]
Nadine Arnold = Hof 1, Tisch 30, Nummer 1
Marco Richter = Hof 1, Tisch 30, Nummer 2
Prof. Dr. Patrick Hoffmann = Hof 1, Tisch 30, Nummer 3
Marija Polat = Hof 1, Tisch 30, Nummer 4
[31]
Marija Aksoy = Hof 1, Tisch 31, Nummer 1
Prof. Dr. Thomas Tran = Hof 1, Tisch 31, Nummer 2
Sergej Petrov = Hof 1, Tisch 31, Nummer 3
Kathrin Polat = Hof 1, Tisch 31, Nummer 4
[32]
Sergej Aksoy = Hof 1, Tisch 32, Nummer 1
Sofia Aksoy = Hof 1, Tisch 32, Nummer 2
Nadine Aksoy = Hof 1, Tisch 32, Nummer 3
Luca Aksoy = Hof 1, Tisch 32, Nummer 4
[33]
Elif Celik = Hof 1, Tisch 33, Nummer 1
Patrick Celik = Hof 1, Tisch 33, Nummer 2
Marie Aksoy = Hof 1, Tisch 33, Nummer 3
Thomas Aksoy = Hof 1, Tisch 33, Nummer 4
[34]
Jan Koch = Hof 1, Tisch 34, Nummer 1
Olga Koch = Hof 1, Tisch 34, Nummer 2
Emil Koch = Hof 1, Tisch 34, Nummer 3
Marie Koch = Hof 1, Tisch 34, Nummer 4
[35]
Henry Kovac = Hof 1, Tisch 35, Nummer 1
Tobias Kovac = Hof 1, Tisch 35, Nummer 2
Marija Kovac = Hof 1, Tisch 35, Nummer 3
[36]
Svenja Lange = Hof 1, Tisch 36, Nummer 1
Dragana Lange = Hof 1, Tisch 36, Nummer 2
Michael Lange = Hof 1, Tisch 36, Nummer 3
[37]
Mia Zimmermann = Hof 1, Tisch 37, Nummer 1
Markus Zimmermann = Hof 1, Tisch 37, Nummer 2
Svenja Zimmermann = Hof 1, Tisch 37, Nummer 3
Jan Zimmermann = Hof 1, Tisch 37, Nummer 4
[38]
Andreas Krause = Hof 1, Tisch 38, Nummer 1
Emma Krause = Hof 1, Tisch 38, Nummer 2
Arda Krause = Hof 1, Tisch 38, Nummer 3
Julia Krause = Hof 1, Tisch 38, Nummer 4
[39]
Kathrin Horn = Hof 1, Tisch 39, Nummer 1
Michael Horn = Hof 1, Tisch 39, Nummer 2
Layla Horn = Hof 1, Tisch 39, Nummer 3
Leon Horn = Hof 1, Tisch 39, Nummer 4
[40]
Nadine Braun = Hof 1, Tisch 40, Nummer 1
Jan Braun = Hof 1, Tisch 40, Nummer 2
Dragana Braun = Hof 1, Tisch 40, Nummer 3
[41]
Milan Braun = Hof 1, Tisch 41, Nummer 1
Christian Braun = Hof 1, Tisch 41, Nummer 2
Julia Braun = Hof 1, Tisch 41, Nummer 3
[42]
Matteo Neumann = Hof 1, Tisch 42, Nummer 1
Jennifer Neumann = Hof 1, Tisch 42, Nummer 2
Thomas Neumann = Hof 1, Tisch 42, Nummer 3
Lina Neumann = Hof 1, Tisch 42, Nummer 4
[43]
Julia Wolf = Hof 1, Tisch 43, Nummer 1
Thomas Wolf = Hof 1, Tisch 43, Nummer 2
Emma Wolf = Hof 1, Tisch 43, Nummer 3
Ben Wolf = Hof 1, Tisch 43, Nummer 4
[44]
Prof. Dr. Sergej Ott = Hof 1, Tisch 44, Nummer 1
Dragana Bauer = Hof 1, Tisch 44, Nummer 2
Patrick Haas = Hof 1, Tisch 44, Nummer 3
Thomas Krause = Hof 1, Tisch 44, Nummer 4
[45]
Dr. Nicole Hansen = Hof 1, Tisch 45, Nummer 1
Nicole Peters = Hof 1, Tisch 45, Nummer 2
Prof. Dr. Timo Kaya = Hof 1, Tisch 45, Nummer 3
Dr. Sandra Demir = Hof 1, Tisch 45, Nummer 4
[46]
Patrick Winkler = Hof 2, Tisch 46, Nummer 1
Kathrin Petrov = Hof 2, Tisch 46, Nummer 2
Lena Winkler = Hof 2, Tisch 46, Nummer 3
Milan Petrov = Hof 2, Tisch 46, Nummer 4
[47]
Christian Arnold = Hof 2, Tisch 47, Nummer 1
Mila Arnold = Hof 2, Tisch 47, Nummer 2
Jennifer Arnold = Hof 2, Tisch 47, Nummer 3
[48]
Nicole Engel = Hof 2, Tisch 48, Nummer 1
Anton Engel = Hof 2, Tisch 48, Nummer 2
Julia Engel = Hof 2, Tisch 48, Nummer 3
[49]
[50]
Thomas Bianchi = Hof 2, Tisch 50, Nummer 1
Sandra Bianchi = Hof 2, Tisch 50, Nummer 2
Mia Bianchi = Hof 2, Tisch 50, Nummer 3
[51]
[52]
Kathrin Nowak = Hof 2, Tisch 52, Nummer 1
Jan Lange = Hof 2, Tisch 52, Nummer 2
Aylin Nowak = Hof 2, Tisch 52, Nummer 3
Sofia Lange = Hof 2, Tisch 52, Nummer 4
[53]
Lina Nowak = Hof 2, Tisch 53, Nummer 1
Noah Nowak = Hof 2, Tisch 53, Nummer 2
Markus Nowak = Hof 2, Tisch 53, Nummer 3
Felix Nowak = Hof 2, Tisch 53, Nummer 4
[54]
Emre Hoffmann = Hof 2, Tisch 54, Nummer 1
Patrick Busch = Hof 2, Tisch 54, Nummer 2
Prof. Dr. Dragana Dietrich = Hof 2, Tisch 54, Nummer 3
Sergej Lange = Hof 2, Tisch 54, Nummer 4
[55]
Marija Becker = Hof 2, Tisch 55, Nummer 1
Matthias Kovac = Hof 2, Tisch 55, Nummer 2
Prof. Dr. Yusuf Esposito = Hof 2, Tisch 55, Nummer 3
[56]
Christina Kaya = Hof 2, Tisch 56, Nummer 1
Nicole Kaya = Hof 2, Tisch 56, Nummer 2
Luca Kaya = Hof 2, Tisch 56, Nummer 3
[57]
Prof. Dr. Emre Albrecht = Hof 2, Tisch 57, Nummer 1
Dr. Jennifer Arnold = Hof 2, Tisch 57, Nummer 2
Dr. Olga Klein = Hof 2, Tisch 57, Nummer 3
[58]
Yusuf Engel = Hof 2, Tisch 58, Nummer 1
Anja Engel = Hof 2, Tisch 58, Nummer 2
Olga Roth = Hof 2, Tisch 58, Nummer 3
Thomas Bianchi = Hof 2, Tisch 58, Nummer 4
[59]
Andreas Weber = Hof 2, Tisch 59, Nummer 1
Matthias Petrov = Hof 2, Tisch 59, Nummer 2
Prof. Dr. Julia Richter = Hof 2, Tisch 59, Nummer 3
[60]
Marie Schmidt = Hof 2, Tisch 60, Nummer 1
Giovanna Schmidt = Hof 2, Tisch 60, Nummer 2
Matthias Schmidt = Hof 2, Tisch 60, Nummer 3
[61]
Jan Becker = Hof 2, Tisch 61, Nummer 1
Sandra Becker = Hof 2, Tisch 61, Nummer 2
Nicole Becker = Hof 2, Tisch 61, Nummer 3
[62]
Sofia Nguyen = Hof 2, Tisch 62, Nummer 1
Anja Nguyen = Hof 2, Tisch 62, Nummer 2
Daniel Nguyen = Hof 2, Tisch 62, Nummer 3
[63]
Ben Stein = Hof 2, Tisch 63, Nummer 1
Emilia Stein = Hof 2, Tisch 63, Nummer 2
Giovanna Stein = Hof 2, Tisch 63, Nummer 3
Thomas Stein = Hof 2, Tisch 63, Nummer 4
[64]
Katrin Werner = Hof 2, Tisch 64, Nummer 1
Dr. Aylin Ludwig = Hof 2, Tisch 64, Nummer 2
Matthias Krause = Hof 2, Tisch 64, Nummer 3
Dr. Michael Aksoy = Hof 2, Tisch 64, Nummer 4
[65]
Yusuf Weber = Hof 2, Tisch 65, Nummer 1
Giovanna Schwarz = Hof 2, Tisch 65, Nummer 2
Prof. Dr. Marco Seifert = Hof 2, Tisch 65, Nummer 3
Dr. Patrick Kraus = Hof 2, Tisch 65, Nummer 4
[66]
Stefan Richter = Hof 2, Tisch 66, Nummer 1
Prof. Dr. Dennis Schmitt = Hof 2, Tisch 66, Nummer 2
[67]
Stefanie Klein = Hof 2, Tisch 67, Nummer 1
Milan Klein = Hof 2, Tisch 67, Nummer 2
Lina Klein = Hof 2, Tisch 67, Nummer 3
[68]
Elif Seifert = Hof 2, Tisch 68, Nummer 1
Yusuf Seifert = Hof 2, Tisch 68, Nummer 2
Markus Barth = Hof 2, Tisch 68, Nummer 3
Dr. Jennifer Haas = Hof 2, Tisch 68, Nummer 4
[69]
Katrin Polat = Hof 2, Tisch 69, Nummer 1
Markus Polat = Hof 2, Tisch 69, Nummer 2
Stefanie Polat = Hof 2, Tisch 69, Nummer 3
[70]
Sarah Polat = Hof 2, Tisch 70, Nummer 1
Julia Franke = Hof 2, Tisch 70, Nummer 2
Olga Franke = Hof 2, Tisch 70, Nummer 3
Stefan Polat = Hof 2, Tisch 70, Nummer 4
[71]
Dr. Kathrin Koch = Hof 2, Tisch 71, Nummer 1
Katrin Weber = Hof 2, Tisch 71, Nummer 2
Dr. Julia Kuhn = Hof 2, Tisch 71, Nummer 3
[72]
Marija Tran = Hof 2, Tisch 72, Nummer 1
Luca Werner = Hof 2, Tisch 72, Nummer 2
Prof. Dr. Stefan Schmitt = Hof 2, Tisch 72, Nummer 3
[73]
Sarah Roth = Hof 2, Tisch 73, Nummer 1
Christian Roth = Hof 2, Tisch 73, Nummer 2
Sofia Roth = Hof 2, Tisch 73, Nummer 3
[74]
Thomas Sahin = Hof 2, Tisch 74, Nummer 1
Prof. Dr. Anja Richter = Hof 2, Tisch 74, Nummer 2
Dr. Jan Thiel = Hof 2, Tisch 74, Nummer 3
[75]
Patrick Zimmermann = Hof 2, Tisch 75, Nummer 1
Luca Kovac = Hof 2, Tisch 75, Nummer 2
Dr. Dragana Krause = Hof 2, Tisch 75, Nummer 3
Luca Ott = Hof 2, Tisch 75, Nummer 4
[76]
Leni Seifert = Hof 2, Tisch 76, Nummer 1
Timo Seifert = Hof 2, Tisch 76, Nummer 2
Nicole Seifert = Hof 2, Tisch 76, Nummer 3
[77]
Emre Pohl = Hof 2, Tisch 77, Nummer 1
Dragana Pohl = Hof 2, Tisch 77, Nummer 2
Theo Pohl = Hof 2, Tisch 77, Nummer 3
Mia Pohl = Hof 2, Tisch 77, Nummer 4
[78]
Sarah Maier = Hof 2, Tisch 78, Nummer 1
Arda Maier = Hof 2, Tisch 78, Nummer 2
Olga Maier = Hof 2, Tisch 78, Nummer 3
Christian Maier = Hof 2, Tisch 78, Nummer 4
[79]
Amira Franke = Hof 2, Tisch 79, Nummer 1
Marco Franke = Hof 2, Tisch 79, Nummer 2
Michael Franke = Hof 2, Tisch 79, Nummer 3
[80]
Nicole Sahin = Hof 2, Tisch 80, Nummer 1
Kathrin Sahin = Hof 2, Tisch 80, Nummer 2
Markus Sahin = Hof 2, Tisch 80, Nummer 3
Patrick Sahin = Hof 2, Tisch 80, Nummer 4
[81]
Dr. Nadine Berger = Hof 2, Tisch 81, Nummer 1
Patrick Lehmann = Hof 2, Tisch 81, Nummer 2
Prof. Dr. Yusuf Ott = Hof 2, Tisch 81, Nummer 3
[82]
Elias Ludwig = Hof 2, Tisch 82, Nummer 1
Elif Ludwig = Hof 2, Tisch 82, Nummer 2
Emre Ludwig = Hof 2, Tisch 82, Nummer 3
Giovanna Ludwig = Hof 2, Tisch 82, Nummer 4
[83]
Lina Barth = Hof 2, Tisch 83, Nummer 1
Nadine Barth = Hof 2, Tisch 83, Nummer 2
Timo Barth = Hof 2, Tisch 83, Nummer 3
Patrick Barth = Hof 2, Tisch 83, Nummer 4
[84]
Andreas Simon = Hof 2, Tisch 84, Nummer 1
Markus Simon = Hof 2, Tisch 84, Nummer 2
Emma Simon = Hof 2, Tisch 84, Nummer 3
Lena Simon = Hof 2, Tisch 84, Nummer 4
[85]
Sofia Dietrich = Hof 2, Tisch 85, Nummer 1
Marco Dietrich = Hof 2, Tisch 85, Nummer 2
Jennifer Dietrich = Hof 2, Tisch 85, Nummer 3
[86]
Yusuf Thiel = Hof 2, Tisch 86, Nummer 1
Christina Thiel = Hof 2, Tisch 86, Nummer 2
Julia Thiel = Hof 2, Tisch 86, Nummer 3
[87]
Sofia Schmitt = Hof 2, Tisch 87, Nummer 1
Sarah Schmitt = Hof 2, Tisch 87, Nummer 2
Markus Schmitt = Hof 2, Tisch 87, Nummer 3
[88]
Emilia Rossi = Hof 2, Tisch 88, Nummer 1
Melanie Rossi = Hof 2, Tisch 88, Nummer 2
Luca Rossi = Hof 2, Tisch 88, Nummer 3
+111
View File
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1240" height="420" viewBox="0 0 1240 420" font-family="sans-serif">
<rect width="100%" height="100%" fill="white"/>
<text x="620" y="26" text-anchor="middle" font-size="18" fill="#333">Sommerfest — Umfeld/Grundriss: zwei trapezfoermige Innenhoefe (88 Vierertische, 300 Gaeste)</text>
<polygon points="48,96 512,96 432,320 128,320" fill="#f5f0e6" stroke="#7a6a50" stroke-width="2"/>
<text x="280" y="342" text-anchor="middle" font-size="14" fill="#7a6a50">Hof 1 (Tische 1-45)</text>
<polygon points="688,96 1152,96 1032,320 808,320" fill="#f5f0e6" stroke="#7a6a50" stroke-width="2"/>
<text x="920" y="342" text-anchor="middle" font-size="14" fill="#7a6a50">Hof 2 (Tische 46-88)</text>
<rect x="536" y="24" width="168" height="48" fill="#d9c9e8" stroke="#6b4d8a" stroke-width="2"/>
<text x="620" y="50" text-anchor="middle" font-size="13" fill="#4a3465">Buehne — Band</text>
<rect x="536" y="92" width="168" height="176" fill="#cfe3f0" stroke="#4a7ba0" stroke-width="2" stroke-dasharray="8 4"/>
<text x="620" y="180" text-anchor="middle" font-size="13" fill="#2d566f">Tanzflaeche</text>
<text x="16" y="208" text-anchor="start" font-size="12" fill="#a03030">Eingang West</text>
<text x="1184" y="208" text-anchor="end" font-size="12" fill="#a03030">Eingang Ost</text>
<text x="620" y="328" text-anchor="middle" font-size="12" fill="#a03030">Eingang Sued</text>
<polygon points="42,192 42,224 66,208" fill="#a03030"/>
<polygon points="1158,192 1158,224 1134,208" fill="#a03030"/>
<polygon points="604,318 636,318 620,294" fill="#a03030"/>
<circle cx="80" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="120" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="160" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="200" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="240" cy="128" r="12" fill="white" stroke="#c0392b" stroke-width="2.5"/>
<text x="240" y="131" text-anchor="middle" font-size="9" fill="#c0392b">VIP</text>
<circle cx="280" cy="128" r="12" fill="white" stroke="#c0392b" stroke-width="2.5"/>
<text x="280" y="131" text-anchor="middle" font-size="9" fill="#c0392b">VIP</text>
<circle cx="320" cy="128" r="12" fill="white" stroke="#c0392b" stroke-width="2.5"/>
<text x="320" y="131" text-anchor="middle" font-size="9" fill="#c0392b">VIP</text>
<circle cx="360" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="400" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="440" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="480" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="100" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="140" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="180" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="220" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="260" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="300" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="340" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="380" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="420" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="460" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="120" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="160" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="200" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="240" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="280" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="320" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="360" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="400" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="440" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="140" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="180" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="220" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="260" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="300" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="340" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="380" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="420" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="160" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="200" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="240" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="280" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="320" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="360" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="400" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="720" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="760" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="800" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="840" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="880" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="920" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="960" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1000" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1040" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1080" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1120" cy="128" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="740" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="780" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="820" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="860" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="900" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="940" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="980" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1020" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1060" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1100" cy="168" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="760" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="800" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="840" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="880" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="920" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="960" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1000" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1040" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1080" cy="208" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="780" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="820" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="860" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="900" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="940" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="980" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1020" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1060" cy="248" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="840" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="880" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="920" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="960" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<circle cx="1000" cy="288" r="12" fill="white" stroke="#999999" stroke-width="1"/>
<text x="620" y="408" text-anchor="middle" font-size="11" fill="#888">Umfeld.svg — Grundriss-Elemente (Buehne, Band, Tanzflaeche, Eingaenge) werden nicht mitoptimiert; VIP-Tische 5/6/7 vor der Buehne</text>
</svg>

After

Width:  |  Height:  |  Size: 9.1 KiB

File diff suppressed because it is too large Load Diff
+615
View File
@@ -0,0 +1,615 @@
[1]
Nummer=1
Hof=1
Plaetze=4
Nachbarliste=[2, 12]
Koordinaten=(0,0)
[2]
Nummer=2
Hof=1
Plaetze=4
Nachbarliste=[1, 3, 12, 13]
Koordinaten=(1,0)
[3]
Nummer=3
Hof=1
Plaetze=4
Nachbarliste=[2, 4, 13, 14]
Koordinaten=(2,0)
[4]
Nummer=4
Hof=1
Plaetze=4
Nachbarliste=[3, 5, 14, 15]
Koordinaten=(3,0)
[5]
Nummer=5
Hof=1
Plaetze=4
Nachbarliste=[4, 6, 15, 16]
Koordinaten=(4,0)
[6]
Nummer=6
Hof=1
Plaetze=4
Nachbarliste=[5, 7, 16, 17]
Koordinaten=(5,0)
[7]
Nummer=7
Hof=1
Plaetze=4
Nachbarliste=[6, 8, 17, 18]
Koordinaten=(6,0)
[8]
Nummer=8
Hof=1
Plaetze=4
Nachbarliste=[7, 9, 18, 19]
Koordinaten=(7,0)
[9]
Nummer=9
Hof=1
Plaetze=4
Nachbarliste=[8, 10, 19, 20]
Koordinaten=(8,0)
[10]
Nummer=10
Hof=1
Plaetze=4
Nachbarliste=[9, 11, 20, 21]
Koordinaten=(9,0)
[11]
Nummer=11
Hof=1
Plaetze=4
Nachbarliste=[10, 21]
Koordinaten=(10,0)
[12]
Nummer=12
Hof=1
Plaetze=4
Nachbarliste=[1, 2, 13, 22]
Koordinaten=(0.5,1)
[13]
Nummer=13
Hof=1
Plaetze=4
Nachbarliste=[2, 3, 12, 14, 22, 23]
Koordinaten=(1.5,1)
[14]
Nummer=14
Hof=1
Plaetze=4
Nachbarliste=[3, 4, 13, 15, 23, 24]
Koordinaten=(2.5,1)
[15]
Nummer=15
Hof=1
Plaetze=4
Nachbarliste=[4, 5, 14, 16, 24, 25]
Koordinaten=(3.5,1)
[16]
Nummer=16
Hof=1
Plaetze=4
Nachbarliste=[5, 6, 15, 17, 25, 26]
Koordinaten=(4.5,1)
[17]
Nummer=17
Hof=1
Plaetze=4
Nachbarliste=[6, 7, 16, 18, 26, 27]
Koordinaten=(5.5,1)
[18]
Nummer=18
Hof=1
Plaetze=4
Nachbarliste=[7, 8, 17, 19, 27, 28]
Koordinaten=(6.5,1)
[19]
Nummer=19
Hof=1
Plaetze=4
Nachbarliste=[8, 9, 18, 20, 28, 29]
Koordinaten=(7.5,1)
[20]
Nummer=20
Hof=1
Plaetze=4
Nachbarliste=[9, 10, 19, 21, 29, 30]
Koordinaten=(8.5,1)
[21]
Nummer=21
Hof=1
Plaetze=4
Nachbarliste=[10, 11, 20, 30]
Koordinaten=(9.5,1)
[22]
Nummer=22
Hof=1
Plaetze=4
Nachbarliste=[12, 13, 23, 31]
Koordinaten=(1,2)
[23]
Nummer=23
Hof=1
Plaetze=4
Nachbarliste=[13, 14, 22, 24, 31, 32]
Koordinaten=(2,2)
[24]
Nummer=24
Hof=1
Plaetze=4
Nachbarliste=[14, 15, 23, 25, 32, 33]
Koordinaten=(3,2)
[25]
Nummer=25
Hof=1
Plaetze=4
Nachbarliste=[15, 16, 24, 26, 33, 34]
Koordinaten=(4,2)
[26]
Nummer=26
Hof=1
Plaetze=4
Nachbarliste=[16, 17, 25, 27, 34, 35]
Koordinaten=(5,2)
[27]
Nummer=27
Hof=1
Plaetze=4
Nachbarliste=[17, 18, 26, 28, 35, 36]
Koordinaten=(6,2)
[28]
Nummer=28
Hof=1
Plaetze=4
Nachbarliste=[18, 19, 27, 29, 36, 37]
Koordinaten=(7,2)
[29]
Nummer=29
Hof=1
Plaetze=4
Nachbarliste=[19, 20, 28, 30, 37, 38]
Koordinaten=(8,2)
[30]
Nummer=30
Hof=1
Plaetze=4
Nachbarliste=[20, 21, 29, 38]
Koordinaten=(9,2)
[31]
Nummer=31
Hof=1
Plaetze=4
Nachbarliste=[22, 23, 32, 39]
Koordinaten=(1.5,3)
[32]
Nummer=32
Hof=1
Plaetze=4
Nachbarliste=[23, 24, 31, 33, 39, 40]
Koordinaten=(2.5,3)
[33]
Nummer=33
Hof=1
Plaetze=4
Nachbarliste=[24, 25, 32, 34, 40, 41]
Koordinaten=(3.5,3)
[34]
Nummer=34
Hof=1
Plaetze=4
Nachbarliste=[25, 26, 33, 35, 41, 42]
Koordinaten=(4.5,3)
[35]
Nummer=35
Hof=1
Plaetze=4
Nachbarliste=[26, 27, 34, 36, 42, 43]
Koordinaten=(5.5,3)
[36]
Nummer=36
Hof=1
Plaetze=4
Nachbarliste=[27, 28, 35, 37, 43, 44]
Koordinaten=(6.5,3)
[37]
Nummer=37
Hof=1
Plaetze=4
Nachbarliste=[28, 29, 36, 38, 44, 45]
Koordinaten=(7.5,3)
[38]
Nummer=38
Hof=1
Plaetze=4
Nachbarliste=[29, 30, 37, 45]
Koordinaten=(8.5,3)
[39]
Nummer=39
Hof=1
Plaetze=4
Nachbarliste=[31, 32, 40]
Koordinaten=(2,4)
[40]
Nummer=40
Hof=1
Plaetze=4
Nachbarliste=[32, 33, 39, 41]
Koordinaten=(3,4)
[41]
Nummer=41
Hof=1
Plaetze=4
Nachbarliste=[33, 34, 40, 42]
Koordinaten=(4,4)
[42]
Nummer=42
Hof=1
Plaetze=4
Nachbarliste=[34, 35, 41, 43]
Koordinaten=(5,4)
[43]
Nummer=43
Hof=1
Plaetze=4
Nachbarliste=[35, 36, 42, 44]
Koordinaten=(6,4)
[44]
Nummer=44
Hof=1
Plaetze=4
Nachbarliste=[36, 37, 43, 45]
Koordinaten=(7,4)
[45]
Nummer=45
Hof=1
Plaetze=4
Nachbarliste=[37, 38, 44]
Koordinaten=(8,4)
[46]
Nummer=46
Hof=2
Plaetze=4
Nachbarliste=[47, 57]
Koordinaten=(16,0)
[47]
Nummer=47
Hof=2
Plaetze=4
Nachbarliste=[46, 48, 57, 58]
Koordinaten=(17,0)
[48]
Nummer=48
Hof=2
Plaetze=4
Nachbarliste=[47, 49, 58, 59]
Koordinaten=(18,0)
[49]
Nummer=49
Hof=2
Plaetze=4
Nachbarliste=[48, 50, 59, 60]
Koordinaten=(19,0)
[50]
Nummer=50
Hof=2
Plaetze=4
Nachbarliste=[49, 51, 60, 61]
Koordinaten=(20,0)
[51]
Nummer=51
Hof=2
Plaetze=4
Nachbarliste=[50, 52, 61, 62]
Koordinaten=(21,0)
[52]
Nummer=52
Hof=2
Plaetze=4
Nachbarliste=[51, 53, 62, 63]
Koordinaten=(22,0)
[53]
Nummer=53
Hof=2
Plaetze=4
Nachbarliste=[52, 54, 63, 64]
Koordinaten=(23,0)
[54]
Nummer=54
Hof=2
Plaetze=4
Nachbarliste=[53, 55, 64, 65]
Koordinaten=(24,0)
[55]
Nummer=55
Hof=2
Plaetze=4
Nachbarliste=[54, 56, 65, 66]
Koordinaten=(25,0)
[56]
Nummer=56
Hof=2
Plaetze=4
Nachbarliste=[55, 66]
Koordinaten=(26,0)
[57]
Nummer=57
Hof=2
Plaetze=4
Nachbarliste=[46, 47, 58, 67]
Koordinaten=(16.5,1)
[58]
Nummer=58
Hof=2
Plaetze=4
Nachbarliste=[47, 48, 57, 59, 67, 68]
Koordinaten=(17.5,1)
[59]
Nummer=59
Hof=2
Plaetze=4
Nachbarliste=[48, 49, 58, 60, 68, 69]
Koordinaten=(18.5,1)
[60]
Nummer=60
Hof=2
Plaetze=4
Nachbarliste=[49, 50, 59, 61, 69, 70]
Koordinaten=(19.5,1)
[61]
Nummer=61
Hof=2
Plaetze=4
Nachbarliste=[50, 51, 60, 62, 70, 71]
Koordinaten=(20.5,1)
[62]
Nummer=62
Hof=2
Plaetze=4
Nachbarliste=[51, 52, 61, 63, 71, 72]
Koordinaten=(21.5,1)
[63]
Nummer=63
Hof=2
Plaetze=4
Nachbarliste=[52, 53, 62, 64, 72, 73]
Koordinaten=(22.5,1)
[64]
Nummer=64
Hof=2
Plaetze=4
Nachbarliste=[53, 54, 63, 65, 73, 74]
Koordinaten=(23.5,1)
[65]
Nummer=65
Hof=2
Plaetze=4
Nachbarliste=[54, 55, 64, 66, 74, 75]
Koordinaten=(24.5,1)
[66]
Nummer=66
Hof=2
Plaetze=4
Nachbarliste=[55, 56, 65, 75]
Koordinaten=(25.5,1)
[67]
Nummer=67
Hof=2
Plaetze=4
Nachbarliste=[57, 58, 68, 76]
Koordinaten=(17,2)
[68]
Nummer=68
Hof=2
Plaetze=4
Nachbarliste=[58, 59, 67, 69, 76, 77]
Koordinaten=(18,2)
[69]
Nummer=69
Hof=2
Plaetze=4
Nachbarliste=[59, 60, 68, 70, 77, 78]
Koordinaten=(19,2)
[70]
Nummer=70
Hof=2
Plaetze=4
Nachbarliste=[60, 61, 69, 71, 78, 79]
Koordinaten=(20,2)
[71]
Nummer=71
Hof=2
Plaetze=4
Nachbarliste=[61, 62, 70, 72, 79, 80]
Koordinaten=(21,2)
[72]
Nummer=72
Hof=2
Plaetze=4
Nachbarliste=[62, 63, 71, 73, 80, 81]
Koordinaten=(22,2)
[73]
Nummer=73
Hof=2
Plaetze=4
Nachbarliste=[63, 64, 72, 74, 81, 82]
Koordinaten=(23,2)
[74]
Nummer=74
Hof=2
Plaetze=4
Nachbarliste=[64, 65, 73, 75, 82, 83]
Koordinaten=(24,2)
[75]
Nummer=75
Hof=2
Plaetze=4
Nachbarliste=[65, 66, 74, 83]
Koordinaten=(25,2)
[76]
Nummer=76
Hof=2
Plaetze=4
Nachbarliste=[67, 68, 77]
Koordinaten=(17.5,3)
[77]
Nummer=77
Hof=2
Plaetze=4
Nachbarliste=[68, 69, 76, 78, 84]
Koordinaten=(18.5,3)
[78]
Nummer=78
Hof=2
Plaetze=4
Nachbarliste=[69, 70, 77, 79, 84, 85]
Koordinaten=(19.5,3)
[79]
Nummer=79
Hof=2
Plaetze=4
Nachbarliste=[70, 71, 78, 80, 85, 86]
Koordinaten=(20.5,3)
[80]
Nummer=80
Hof=2
Plaetze=4
Nachbarliste=[71, 72, 79, 81, 86, 87]
Koordinaten=(21.5,3)
[81]
Nummer=81
Hof=2
Plaetze=4
Nachbarliste=[72, 73, 80, 82, 87, 88]
Koordinaten=(22.5,3)
[82]
Nummer=82
Hof=2
Plaetze=4
Nachbarliste=[73, 74, 81, 83, 88]
Koordinaten=(23.5,3)
[83]
Nummer=83
Hof=2
Plaetze=4
Nachbarliste=[74, 75, 82]
Koordinaten=(24.5,3)
[84]
Nummer=84
Hof=2
Plaetze=4
Nachbarliste=[77, 78, 85]
Koordinaten=(19,4)
[85]
Nummer=85
Hof=2
Plaetze=4
Nachbarliste=[78, 79, 84, 86]
Koordinaten=(20,4)
[86]
Nummer=86
Hof=2
Plaetze=4
Nachbarliste=[79, 80, 85, 87]
Koordinaten=(21,4)
[87]
Nummer=87
Hof=2
Plaetze=4
Nachbarliste=[80, 81, 86, 88]
Koordinaten=(22,4)
[88]
Nummer=88
Hof=2
Plaetze=4
Nachbarliste=[81, 82, 87]
Koordinaten=(23,4)
+19 -19
View File
@@ -1,19 +1,19 @@
AA, A, Dipl. Ing. = Hof 1, Tisch 1, Nummer 2 AA, A, Dipl. Ing. = Hof 1, Tisch 3, Nummer 2
AA, A, Dipl. Ing. = Hof 1, Tisch 1, Nummer 3 AA, A, Dipl. Ing. = Hof 1, Tisch 3, Nummer 3
AA, A, Dipl. Ing. = Hof 1, Tisch 1, Nummer 4 AA, A, Dipl. Ing. = Hof 1, Tisch 3, Nummer 4
AA, A, Dipl. Ing. = Hof 1, Tisch 1, Nummer 5 AA, A, Dipl. Ing. = Hof 1, Tisch 3, Nummer 5
AA, A, Dipl. Ing. = Hof 1, Tisch 1, Nummer 6 AA, A, Dipl. Ing. = Hof 1, Tisch 3, Nummer 6
AA, A, Dipl. Ing. = Hof 1, Tisch 2, Nummer 2 AA, A, Dipl. Ing. = Hof 1, Tisch 4, Nummer 2
AA, A, Dipl. Ing. = Hof 1, Tisch 2, Nummer 3 AA, A, Dipl. Ing. = Hof 1, Tisch 4, Nummer 3
AA, A, Dipl. Ing. = Hof 1, Tisch 2, Nummer 4 AA, A, Dipl. Ing. = Hof 1, Tisch 4, Nummer 4
AA, A, Dipl. Ing. = Hof 1, Tisch 2, Nummer 5 AA, A, Dipl. Ing. = Hof 1, Tisch 4, Nummer 5
AA, A, Dipl. Ing. = Hof 1, Tisch 2, Nummer 6 AA, A, Dipl. Ing. = Hof 1, Tisch 4, Nummer 6
BB, B, Prof. = Hof 1, Tisch 3, Nummer 2 BB, B, Prof. = Hof 1, Tisch 1, Nummer 2
BB, B, Prof. = Hof 1, Tisch 3, Nummer 3 BB, B, Prof. = Hof 1, Tisch 1, Nummer 3
BB, B, Prof. = Hof 1, Tisch 3, Nummer 4 BB, B, Prof. = Hof 1, Tisch 1, Nummer 4
BB, B, Prof. = Hof 1, Tisch 3, Nummer 5 BB, B, Prof. = Hof 1, Tisch 1, Nummer 5
CC, C, Dr. = Hof 1, Tisch 4, Nummer 2 CC, C, Dr. = Hof 1, Tisch 2, Nummer 2
CC, C, Dr. = Hof 1, Tisch 4, Nummer 3 CC, C, Dr. = Hof 1, Tisch 2, Nummer 3
CC, C, Dr. = Hof 1, Tisch 4, Nummer 4 CC, C, Dr. = Hof 1, Tisch 2, Nummer 4
CC, C, Dr. = Hof 1, Tisch 4, Nummer 5 CC, C, Dr. = Hof 1, Tisch 2, Nummer 5
CC, C, Dr. = Hof 1, Tisch 4, Nummer 6 CC, C, Dr. = Hof 1, Tisch 2, Nummer 6
1 AA A Dipl. Ing. = Hof 1 Tisch 1 Tisch 3 Nummer 2
2 AA A Dipl. Ing. = Hof 1 Tisch 1 Tisch 3 Nummer 3
3 AA A Dipl. Ing. = Hof 1 Tisch 1 Tisch 3 Nummer 4
4 AA A Dipl. Ing. = Hof 1 Tisch 1 Tisch 3 Nummer 5
5 AA A Dipl. Ing. = Hof 1 Tisch 1 Tisch 3 Nummer 6
6 AA A Dipl. Ing. = Hof 1 Tisch 2 Tisch 4 Nummer 2
7 AA A Dipl. Ing. = Hof 1 Tisch 2 Tisch 4 Nummer 3
8 AA A Dipl. Ing. = Hof 1 Tisch 2 Tisch 4 Nummer 4
9 AA A Dipl. Ing. = Hof 1 Tisch 2 Tisch 4 Nummer 5
10 AA A Dipl. Ing. = Hof 1 Tisch 2 Tisch 4 Nummer 6
11 BB B Prof. = Hof 1 Tisch 3 Tisch 1 Nummer 2
12 BB B Prof. = Hof 1 Tisch 3 Tisch 1 Nummer 3
13 BB B Prof. = Hof 1 Tisch 3 Tisch 1 Nummer 4
14 BB B Prof. = Hof 1 Tisch 3 Tisch 1 Nummer 5
15 CC C Dr. = Hof 1 Tisch 4 Tisch 2 Nummer 2
16 CC C Dr. = Hof 1 Tisch 4 Tisch 2 Nummer 3
17 CC C Dr. = Hof 1 Tisch 4 Tisch 2 Nummer 4
18 CC C Dr. = Hof 1 Tisch 4 Tisch 2 Nummer 5
19 CC C Dr. = Hof 1 Tisch 4 Tisch 2 Nummer 6
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="405.657" height="108.914" viewBox="0 0 405.657 108.914">
<rect width="100%" height="100%" fill="white"/>
<circle cx="252.286" cy="54.4572" r="44.4572" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="252.286" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="274.743" cy="54.4572" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="252.286" cy="76.9144" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="229.829" cy="54.4572" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="252.286" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="252.286" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">3</text>
<circle cx="54.4572" cy="54.4572" r="44.4572" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="54.4572" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="75.8152" cy="47.5175" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="67.6572" cy="72.6254" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="41.2572" cy="72.6254" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="33.0991" cy="47.5175" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="54.4572" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="54.4572" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">1</text>
<circle cx="153.372" cy="54.4572" r="44.4572" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="153.372" cy="32" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="174.73" cy="47.5175" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="166.572" cy="72.6254" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="140.172" cy="72.6254" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="132.013" cy="47.5175" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="153.372" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="153.372" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">2</text>
<circle cx="351.2" cy="54.4572" r="44.4572" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="351.2" cy="32" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="372.558" cy="47.5175" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="364.4" cy="72.6254" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="338" cy="72.6254" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="329.842" cy="47.5175" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="351.2" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="351.2" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">4</text>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+19 -19
View File
@@ -1,27 +1,27 @@
[1] [1]
Dipl. Ing. A AA = Hof 1, Tisch 1, Nummer 1 Prof. B BB = Hof 1, Tisch 1, Nummer 1
Dipl. Ing. A AA = Hof 1, Tisch 1, Nummer 2 Prof. B BB = Hof 1, Tisch 1, Nummer 2
Dipl. Ing. A AA = Hof 1, Tisch 1, Nummer 3 Prof. B BB = Hof 1, Tisch 1, Nummer 3
Dipl. Ing. A AA = Hof 1, Tisch 1, Nummer 4 Prof. B BB = Hof 1, Tisch 1, Nummer 4
Dipl. Ing. A AA = Hof 1, Tisch 1, Nummer 5
[2] [2]
Dipl. Ing. A AA = Hof 1, Tisch 2, Nummer 1 Dr. C CC = Hof 1, Tisch 2, Nummer 1
Dipl. Ing. A AA = Hof 1, Tisch 2, Nummer 2 Dr. C CC = Hof 1, Tisch 2, Nummer 2
Dipl. Ing. A AA = Hof 1, Tisch 2, Nummer 3 Dr. C CC = Hof 1, Tisch 2, Nummer 3
Dipl. Ing. A AA = Hof 1, Tisch 2, Nummer 4 Dr. C CC = Hof 1, Tisch 2, Nummer 4
Dipl. Ing. A AA = Hof 1, Tisch 2, Nummer 5 Dr. C CC = Hof 1, Tisch 2, Nummer 5
[3] [3]
Prof. B BB = Hof 1, Tisch 3, Nummer 1 Dipl. Ing. A AA = Hof 1, Tisch 3, Nummer 1
Prof. B BB = Hof 1, Tisch 3, Nummer 2 Dipl. Ing. A AA = Hof 1, Tisch 3, Nummer 2
Prof. B BB = Hof 1, Tisch 3, Nummer 3 Dipl. Ing. A AA = Hof 1, Tisch 3, Nummer 3
Prof. B BB = Hof 1, Tisch 3, Nummer 4 Dipl. Ing. A AA = Hof 1, Tisch 3, Nummer 4
Dipl. Ing. A AA = Hof 1, Tisch 3, Nummer 5
[4] [4]
Dr. C CC = Hof 1, Tisch 4, Nummer 1 Dipl. Ing. A AA = Hof 1, Tisch 4, Nummer 1
Dr. C CC = Hof 1, Tisch 4, Nummer 2 Dipl. Ing. A AA = Hof 1, Tisch 4, Nummer 2
Dr. C CC = Hof 1, Tisch 4, Nummer 3 Dipl. Ing. A AA = Hof 1, Tisch 4, Nummer 3
Dr. C CC = Hof 1, Tisch 4, Nummer 4 Dipl. Ing. A AA = Hof 1, Tisch 4, Nummer 4
Dr. C CC = Hof 1, Tisch 4, Nummer 5 Dipl. Ing. A AA = Hof 1, Tisch 4, Nummer 5
+34
View File
@@ -0,0 +1,34 @@
{
"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."
}
]
}
]
}
-32
View File
@@ -1,32 +0,0 @@
<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>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="500" viewBox="0 0 900 500" font-family="sans-serif">
<rect width="100%" height="100%" fill="white"/>
<text x="450" y="30" text-anchor="middle" font-size="16" fill="#333">Entwicklung der Loesungsfindung</text>
<line x1="60" y1="60" x2="60" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440" x2="840" y2="440" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="440.0" x2="840" y2="440.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="444.0" text-anchor="end" font-size="11" fill="#666">-76</text>
<line x1="60" y1="364.0" x2="840" y2="364.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="368.0" text-anchor="end" font-size="11" fill="#666">-62</text>
<line x1="60" y1="288.0" x2="840" y2="288.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="292.0" text-anchor="end" font-size="11" fill="#666">-48</text>
<line x1="60" y1="212.0" x2="840" y2="212.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="216.0" text-anchor="end" font-size="11" fill="#666">-34</text>
<line x1="60" y1="136.0" x2="840" y2="136.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="140.0" text-anchor="end" font-size="11" fill="#666">-20</text>
<line x1="60" y1="60.0" x2="840" y2="60.0" stroke="#eee" stroke-width="1"/>
<text x="52" y="64.0" text-anchor="end" font-size="11" fill="#666">-6</text>
<text x="18" y="250" text-anchor="middle" font-size="12" fill="#333" transform="rotate(-90 18 250)">Wertigkeit (hoeher = besser)</text>
<text x="450" y="482" text-anchor="middle" font-size="12" fill="#333">Zyklus-Aktion (Zeit)</text>
<text x="60.0" y="456" text-anchor="middle" font-size="10" fill="#999">e</text>
<text x="108.8" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="157.5" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="206.2" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="255.0" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="303.8" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="352.5" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="401.2" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="450.0" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="498.8" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="547.5" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="596.2" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<text x="645.0" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="693.8" y="456" text-anchor="middle" font-size="10" fill="#999">m</text>
<text x="742.5" y="456" text-anchor="middle" font-size="10" fill="#999">j</text>
<text x="791.2" y="456" text-anchor="middle" font-size="10" fill="#999">z</text>
<text x="840.0" y="456" text-anchor="middle" font-size="10" fill="#999">s</text>
<polyline points="60.0,60.0 108.8,60.0 157.5,60.0 206.2,60.0 255.0,60.0 303.8,60.0 352.5,60.0 401.2,60.0 450.0,60.0 498.8,60.0 547.5,60.0 596.2,60.0 645.0,60.0 693.8,60.0 742.5,60.0 791.2,60.0 840.0,60.0" fill="none" stroke="#2f6f4f" stroke-width="2"/>
<polyline points="60.0,168.6 108.8,77.4 157.5,77.4 206.2,142.5 255.0,120.8 303.8,120.8 352.5,64.3 401.2,64.3 450.0,94.7 498.8,84.6 547.5,84.6 596.2,60.0 645.0,60.0 693.8,66.5 742.5,64.3 791.2,64.3 840.0,60.0" fill="none" stroke="#c08a2d" stroke-width="2"/>
<polyline points="60.0,440.0 108.8,81.7 157.5,81.7 206.2,418.3 255.0,418.3 303.8,418.3 352.5,81.7 401.2,81.7 450.0,255.4 498.8,255.4 547.5,255.4 596.2,60.0 645.0,60.0 693.8,81.7 742.5,81.7 791.2,81.7 840.0,60.0" fill="none" stroke="#a03030" stroke-width="2"/>
<line x1="680" y1="70" x2="704" y2="70" stroke="#2f6f4f" stroke-width="2"/>
<text x="710" y="74" font-size="11" fill="#333">beste Loesung</text>
<line x1="680" y1="88" x2="704" y2="88" stroke="#c08a2d" stroke-width="2"/>
<text x="710" y="92" font-size="11" fill="#333">Durchschnitt</text>
<line x1="680" y1="106" x2="704" y2="106" stroke="#a03030" stroke-width="2"/>
<text x="710" y="110" font-size="11" fill="#333">schlechteste Loesung</text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+17
View File
@@ -0,0 +1,17 @@
Bloedkopf, Bestelli, = Hof 1, Tisch 3, Nummer 2
Bloedkopf, Bestelli, = Hof 1, Tisch 3, Nummer 3
Bloedkopf, Bestelli, = Hof 1, Tisch 3, Nummer 4
Brandt, Willi, Dr. = Hof 1, Tisch 4, Nummer 2
Bruegge, Heinz, = Hof 1, Tisch 5, Nummer 5
Dietrich, Marlene, = Hof 1, Tisch 5, Nummer 3
Emmerlich, Walter, = Hof 1, Tisch 4, Nummer 3
Frenzen, Heinz Harald, = Hof 1, Tisch 4, Nummer 4
Hoehn, Bernd Robert, Professor = Hof 1, Tisch 1, Nummer 4
Junke, Harald, = Hof 1, Tisch 2, Nummer 2
K, Hubert, = Hof 1, Tisch 5, Nummer 4
Leone, Sergio, = Hof 1, Tisch 2, Nummer 4
Mueller, Magnus, Professor = Hof 1, Tisch 1, Nummer 2
Ruehmann, Heinz, = Hof 1, Tisch 5, Nummer 2
Schumacher, Michael, = Hof 1, Tisch 2, Nummer 3
Schumacher, Ralf, = Hof 1, Tisch 4, Nummer 5
Stangl, Michael, Dipl. Ing. = Hof 1, Tisch 1, Nummer 3
1 Bloedkopf Bestelli = Hof 1 Tisch 3 Nummer 2
2 Bloedkopf Bestelli = Hof 1 Tisch 3 Nummer 3
3 Bloedkopf Bestelli = Hof 1 Tisch 3 Nummer 4
4 Brandt Willi Dr. = Hof 1 Tisch 4 Nummer 2
5 Bruegge Heinz = Hof 1 Tisch 5 Nummer 5
6 Dietrich Marlene = Hof 1 Tisch 5 Nummer 3
7 Emmerlich Walter = Hof 1 Tisch 4 Nummer 3
8 Frenzen Heinz Harald = Hof 1 Tisch 4 Nummer 4
9 Hoehn Bernd Robert Professor = Hof 1 Tisch 1 Nummer 4
10 Junke Harald = Hof 1 Tisch 2 Nummer 2
11 K Hubert = Hof 1 Tisch 5 Nummer 4
12 Leone Sergio = Hof 1 Tisch 2 Nummer 4
13 Mueller Magnus Professor = Hof 1 Tisch 1 Nummer 2
14 Ruehmann Heinz = Hof 1 Tisch 5 Nummer 2
15 Schumacher Michael = Hof 1 Tisch 2 Nummer 3
16 Schumacher Ralf = Hof 1 Tisch 4 Nummer 5
17 Stangl Michael Dipl. Ing. = Hof 1 Tisch 1 Nummer 3
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="504.572" height="108.914" viewBox="0 0 504.572 108.914">
<rect width="100%" height="100%" fill="white"/>
<circle cx="54.4572" cy="54.4572" r="37.242" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="54.4572" cy="39.2151" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="67.6572" cy="62.0782" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="41.2572" cy="62.0782" r="12" fill="#888888" stroke="black" stroke-width="1"/>
<circle cx="54.4572" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="54.4572" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">1</text>
<circle cx="153.372" cy="54.4572" r="44.4572" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="153.372" cy="32" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="172.82" cy="65.6858" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="133.923" cy="65.6858" r="12" fill="#e6194b" stroke="black" stroke-width="1"/>
<circle cx="153.372" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="153.372" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">2</text>
<circle cx="252.286" cy="54.4572" r="37.242" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="252.286" cy="39.2151" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="265.486" cy="62.0782" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="239.086" cy="62.0782" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="252.286" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="252.286" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">3</text>
<circle cx="351.2" cy="54.4572" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="351.2" cy="35.7896" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="369.868" cy="54.4572" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="351.2" cy="73.1248" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="332.533" cy="54.4572" r="12" fill="#3cb44b" stroke="black" stroke-width="1"/>
<circle cx="351.2" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="351.2" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">4</text>
<circle cx="450.115" cy="54.4572" r="40.6676" fill="white" stroke="black" stroke-width="1.5"/>
<circle cx="450.115" cy="35.7896" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="468.782" cy="54.4572" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="450.115" cy="73.1248" r="12" fill="#f58231" stroke="black" stroke-width="1"/>
<circle cx="431.447" cy="54.4572" r="12" fill="#4363d8" stroke="black" stroke-width="1"/>
<circle cx="450.115" cy="54.4572" r="12" fill="white" fill-opacity="0.75"/>
<text x="450.115" y="54.4572" text-anchor="middle" dominant-baseline="central" font-size="13.2" font-weight="bold" fill="black">5</text>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+27
View File
@@ -0,0 +1,27 @@
[1]
Professor Magnus Mueller = Hof 1, Tisch 1, Nummer 1
Dipl. Ing. Michael Stangl = Hof 1, Tisch 1, Nummer 2
Professor Bernd Robert Hoehn = Hof 1, Tisch 1, Nummer 3
[2]
Harald Junke = Hof 1, Tisch 2, Nummer 1
Michael Schumacher = Hof 1, Tisch 2, Nummer 2
Sergio Leone = Hof 1, Tisch 2, Nummer 3
[3]
Bestelli Bloedkopf = Hof 1, Tisch 3, Nummer 1
Bestelli Bloedkopf = Hof 1, Tisch 3, Nummer 2
Bestelli Bloedkopf = Hof 1, Tisch 3, Nummer 3
[4]
Dr. Willi Brandt = Hof 1, Tisch 4, Nummer 1
Walter Emmerlich = Hof 1, Tisch 4, Nummer 2
Heinz Harald Frenzen = Hof 1, Tisch 4, Nummer 3
Ralf Schumacher = Hof 1, Tisch 4, Nummer 4
[5]
Heinz Ruehmann = Hof 1, Tisch 5, Nummer 1
Marlene Dietrich = Hof 1, Tisch 5, Nummer 2
Hubert K = Hof 1, Tisch 5, Nummer 3
Heinz Bruegge = Hof 1, Tisch 5, Nummer 4
+93
View File
@@ -0,0 +1,93 @@
{
"gruppen": [
{
"name": "einfache Gruppe aus drei Personen\n\t 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"
}
]
},
{
"personen": [
{
"vorname": "Heinz",
"nachname": "Ruehmann"
},
{
"vorname": "Marlene",
"nachname": "Dietrich"
}
]
},
{
"personen": [
{
"vorname": "Sergio",
"nachname": "Leone"
},
{
"vorname": "Walter",
"nachname": "Emmerlich"
},
{
"vorname": "Harald",
"nachname": "Junke"
},
{
"vorname": "Willi",
"nachname": "Brandt",
"titel": "Dr."
},
{
"vorname": "Michael",
"nachname": "Schumacher"
},
{
"vorname": "Ralf",
"nachname": "Schumacher"
},
{
"vorname": "Heinz Harald",
"nachname": "Frenzen"
}
]
},
{
"name": "Gruppe aus drei anonymen Personen",
"anzahl": 3,
"personen": [
{
"vorname": "Bestelli",
"nachname": "Bloedkopf"
}
]
}
]
}
-90
View File
@@ -1,90 +0,0 @@
<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>
<Person>
<Vorname>Heinz</Vorname>
<Nachname>Ruehmann</Nachname>
</Person>
<Person>
<Vorname>Marlene</Vorname>
<Nachname>Dietrich</Nachname>
</Person>
</Gruppe>
<Gruppe>
<Person>
<Vorname>Sergio</Vorname>
<Nachname>Leone</Nachname>
</Person>
<Person>
<Vorname>Walter</Vorname>
<Nachname>Emmerlich</Nachname>
</Person>
<Person>
<Vorname>Harald</Vorname>
<Nachname>Junke</Nachname>
</Person>
<Person>
<Vorname>Willi</Vorname>
<Nachname>Brandt</Nachname>
<Titel>Dr.</Titel>
</Person>
<Person>
<Vorname>Michael</Vorname>
<Nachname>Schumacher</Nachname>
</Person>
<Person>
<Vorname>Ralf</Vorname>
<Nachname>Schumacher</Nachname>
</Person>
<Person>
<Vorname>Heinz Harald</Vorname>
<Nachname>Frenzen</Nachname>
</Person>
</Gruppe>
<!--Gruppe aus drei anonymen Personen-->
<Gruppe>
<Person>
<Vorname>Bestelli</Vorname>
<Nachname>Bloedkopf</Nachname>
</Person>
<Anzahl>3</Anzahl>
</Gruppe>
</Bestellung>