[CHANGE] eine zusätzliche csv für den Sivas export wird erstellt, die alle identischen Zeilen summiert und in einer Zeile mit neuer Anzahl schreibt; [ADD] Neue Gerüstkomponenten für die Baugruppenerstellung on the fly durch hinzugefügt; [CHANGE] Testskripte angepasst

This commit is contained in:
Simon Hensch
2026-09-29 17:08:14 +02:00
parent 4e39c64ec1
commit 7e98b85e5e
14 changed files with 101 additions and 11 deletions
+1 -1
View File
@@ -183,7 +183,7 @@ Abhaengigkeiten: `ezdxf`, Standard-Library. Benoetigen Umgebungsvariablen (`DXFM
| `omniflo_utils.py` | Gemeinsame Hilfsfunktionen fuer Omniflo-Skripte: `ROW_GROUPS` (Reihen-Gruppierung), `SWITCH_FILTERS` (Schalter-Filter), `build_row_layout`, `import_element_as_block`, `draw_cross`, `load_omniflo_data` | | `omniflo_utils.py` | Gemeinsame Hilfsfunktionen fuer Omniflo-Skripte: `ROW_GROUPS` (Reihen-Gruppierung), `SWITCH_FILTERS` (Schalter-Filter), `build_row_layout`, `import_element_as_block`, `draw_cross`, `load_omniflo_data` |
| `set_einfuegepkt.py` | Setzt `$INSBASE` (Einfuegepunkt) in Omniflo-DXF-Dateien. Schalter je Typ: `--boegen`, `--weichen45`, `--weichen90`, `--weichenkoerper`, `--weichen-parallel`, `--delta`, `--dreifachweiche`, `--sternweiche`, `--show-omniflo` | | `set_einfuegepkt.py` | Setzt `$INSBASE` (Einfuegepunkt) in Omniflo-DXF-Dateien. Schalter je Typ: `--boegen`, `--weichen45`, `--weichen90`, `--weichenkoerper`, `--weichen-parallel`, `--delta`, `--dreifachweiche`, `--sternweiche`, `--show-omniflo` |
| `set_koords.py` | Setzt Koordinatensystem-Bloecke (K1-K4) in Omniflo-DXF-Dateien. Schalter: `--k1set` bis `--k4set`, `--set-all`, `--force`, `--show-omniflo`, `--test`, `--number` | | `set_koords.py` | Setzt Koordinatensystem-Bloecke (K1-K4) in Omniflo-DXF-Dateien. Schalter: `--k1set` bis `--k4set`, `--set-all`, `--force`, `--show-omniflo`, `--test`, `--number` |
| `export_sivas.py` | Erzeugt Sivas-Export-CSV mit Summierungszeilen (ILS Automation, Omniflo Sum). Omniflo-Elemente werden ueber das TEILEART-Attribut (`Omniflo *`) erkannt, ihre Sivas-Nummern kommen - wie beim Geruest - NUR aus dem kommagetrennten `ARTINR`-Attribut (eine Zeile je Nummer, TEF-Teile `0_B*` als `Omniflo TEF`); KEIN Katalog-Lookup mehr (den nutzt nur noch `export_csv.py` fuer die Merkmale). Aufruf: `python export_sivas.py <export_raw.json> <data_dir> <output.csv>` | | `export_sivas.py` | Erzeugt Sivas-Export-CSV mit Summierungszeilen (ILS Automation, Omniflo Sum). Omniflo-Elemente werden ueber das TEILEART-Attribut (`Omniflo *`) erkannt, ihre Sivas-Nummern kommen - wie beim Geruest - NUR aus dem kommagetrennten `ARTINR`-Attribut (eine Zeile je Nummer, TEF-Teile `0_B*` als `Omniflo TEF`); KEIN Katalog-Lookup mehr (den nutzt nur noch `export_csv.py` fuer die Merkmale). Schreibt zusaetzlich immer `<output>_zusammengefasst.csv`: Zeilen, die sich nur in Nr/Anzahl/IDs unterscheiden (v.a. Geruest, je Block eine Zeile), zu einer Zeile zusammengefasst (`items_zusammenfassen`, Anzahl addiert, IDs vereinigt); die Hauptdatei bleibt unveraendert. Aufruf: `python export_sivas.py <export_raw.json> <data_dir> <output.csv>` |
| `export_csv.py` | Erzeugt einfache Item-Liste als CSV (ohne Summierung). Aufruf: `python export_csv.py <export_raw.json> <data_dir> <output.csv>` | | `export_csv.py` | Erzeugt einfache Item-Liste als CSV (ohne Summierung). Aufruf: `python export_csv.py <export_raw.json> <data_dir> <output.csv>` |
| `split_block_library.py` | Teilt `ils_library.dxf` in einzelne DXF-Dateien pro Block auf (mit Abhaengigkeiten). Export-Prefixe: Vario_, Staustrecke_, _3D_AS_, _3D_ES_ | | `split_block_library.py` | Teilt `ils_library.dxf` in einzelne DXF-Dateien pro Block auf (mit Abhaengigkeiten). Export-Prefixe: Vario_, Staustrecke_, _3D_AS_, _3D_ES_ |
| `vf_journal_export.py` | Liest die Eingabe-Journale der `VF_n`-Bloecke (XDATA-App `SSG_VF_EDIT`, Marker `"linienzug"`) aus einer Zeichnung und schreibt sie als Frage-Antwort-Protokoll im Schema von `tests/testdata/linienzug_tests.json` (Gegenstueck zu `vfl-entry->string`; kein ezdxf, reine Gruppencode-Lesung). Optional `--csv <sivas_export.csv>` fuer Strecken-ID und Erwartungswerte je Kette. Aufruf: `python vf_journal_export.py <dxf> <output.json> [--csv ...] [--kein-startpunkt-fix]` — erzeugt `tests/testdata/hm_recformat.json` aus `data/polylines.dxf` (die Aufzeichnung, aus der `tests/testdata/hundm05.json` als Spec abgeleitet wird) | | `vf_journal_export.py` | Liest die Eingabe-Journale der `VF_n`-Bloecke (XDATA-App `SSG_VF_EDIT`, Marker `"linienzug"`) aus einer Zeichnung und schreibt sie als Frage-Antwort-Protokoll im Schema von `tests/testdata/linienzug_tests.json` (Gegenstueck zu `vfl-entry->string`; kein ezdxf, reine Gruppencode-Lesung). Optional `--csv <sivas_export.csv>` fuer Strecken-ID und Erwartungswerte je Kette. Aufruf: `python vf_journal_export.py <dxf> <output.json> [--csv ...] [--kein-startpunkt-fix]` — erzeugt `tests/testdata/hm_recformat.json` aus `data/polylines.dxf` (die Aufzeichnung, aus der `tests/testdata/hundm05.json` als Spec abgeleitet wird) |
+3 -1
View File
@@ -118,7 +118,9 @@
;; --- Sivas-Export zusaetzlich mit Klartext-Bezeichnung schreiben --- ;; --- Sivas-Export zusaetzlich mit Klartext-Bezeichnung schreiben ---
;; Standard aus (nil): EXPORTSIVAS schreibt nur die eigentliche <name>_sivas.csv ;; Standard aus (nil): EXPORTSIVAS schreibt nur die eigentliche <name>_sivas.csv
;; (fuer den Sivas-Import). Nur bei eingeschaltetem Schalter schreibt ;; (fuer den Sivas-Import) plus - immer, ohne Schalter - die Kopie
;; <name>_sivas_zusammengefasst.csv (identische Zeilen zusammengefasst, Anzahl
;; addiert, siehe items_zusammenfassen in lib/export_sivas.py). Nur bei eingeschaltetem Schalter schreibt
;; lib/export_sivas.py zusaetzlich <name>_sivas_mit_bezeichnung.csv (Merkmal- ;; lib/export_sivas.py zusaetzlich <name>_sivas_mit_bezeichnung.csv (Merkmal-
;; Keys durch Klartext aus cfg/Merkmalsstamm_in_Sivas.xlsx ersetzt, reine ;; Keys durch Klartext aus cfg/Merkmalsstamm_in_Sivas.xlsx ersetzt, reine
;; Lesehilfe zur manuellen Pruefung, wird von Sivas selbst nicht eingelesen). ;; Lesehilfe zur manuellen Pruefung, wird von Sivas selbst nicht eingelesen).
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+58
View File
@@ -48,6 +48,11 @@ Argumente:
Pruefung, wird von Sivas selbst nicht eingelesen. Pruefung, wird von Sivas selbst nicht eingelesen.
Schalter im Dialog CSV_EXPORT_KONFIG Schalter im Dialog CSV_EXPORT_KONFIG
("Sivas Export mit Bezeichner"), Standard aus. ("Sivas Export mit Bezeichner"), Standard aus.
Zusaetzlich wird IMMER "<output>_zusammengefasst.csv" geschrieben: gleiches
Format, aber Zeilen, die sich nur in Nr/Anzahl/IDs unterscheiden, sind zu
einer Zeile zusammengefasst (Anzahl addiert, IDs vereinigt, Nr neu) - siehe
items_zusammenfassen. output.csv selbst bleibt unveraendert.
""" """
import json import json
@@ -921,6 +926,48 @@ def lesbarer_dateiname(output_csv):
return f"{stamm}_mit_bezeichnung{endung}" return f"{stamm}_mit_bezeichnung{endung}"
def zusammengefasster_dateiname(output_csv):
"""Leitet den Dateinamen der zusammengefassten Kopie aus output_csv ab,
z.B. "500651_sivas.csv" -> "500651_sivas_zusammengefasst.csv"."""
stamm, endung = os.path.splitext(output_csv)
return f"{stamm}_zusammengefasst{endung}"
def items_zusammenfassen(items):
"""Fasst Zeilen zusammen, die sich nur in Nr, Anzahl und IDs
unterscheiden: Anzahl wird addiert, die IDs werden (ohne Dubletten, in
Reihenfolge des Auftretens) vereinigt, Nr wird neu durchgezaehlt.
Reihenfolge der Gruppen = erstes Auftreten.
Betrifft in der Praxis vor allem die Geruest-Zeilen (je Block und
Artikelnummer eine Zeile, siehe process_blocks) - die uebrigen Teilearten
sind dort bereits gruppiert, und ihre Zeilen unterscheiden sich meist in
Bezeichnung/details. Die Laenge gehoert zum Schluessel: Saeulen mit
unterschiedlicher Laenge bleiben getrennte Zeilen.
Die Eingabe bleibt unveraendert (die Hauptdatei wird aus denselben
items geschrieben)."""
gruppen = {}
for item in items:
key = (
item["teileart"], str(item["sivasnr"]), item["sivasnr_quoted"],
item["bezeichnung"], item["dispgruppe"], str(item["laenge_mm"]),
json.dumps(item["details"], ensure_ascii=False, sort_keys=True),
)
g = gruppen.get(key)
if g is None:
g = dict(item, anzahl=0, ids=[])
gruppen[key] = g
g["anzahl"] += safe_int(item["anzahl"], 0)
for i in item.get("ids", []):
if i and i not in g["ids"]:
g["ids"].append(i)
ergebnis = list(gruppen.values())
for nr, g in enumerate(ergebnis, 1):
g["nr"] = nr
return ergebnis
def main(): def main():
if len(sys.argv) < 4: if len(sys.argv) < 4:
print("Aufruf: python export_sivas.py <export_raw.json> <data_dir> <output.csv> [--mit-bezeichnung]") print("Aufruf: python export_sivas.py <export_raw.json> <data_dir> <output.csv> [--mit-bezeichnung]")
@@ -945,6 +992,17 @@ def main():
f.write(format_csv_line(item) + "\n") f.write(format_csv_line(item) + "\n")
print(f"[export_sivas] CSV geschrieben: {output_csv} ({len(items)} Zeilen)") print(f"[export_sivas] CSV geschrieben: {output_csv} ({len(items)} Zeilen)")
# Zusaetzliche Datei mit zusammengefassten identischen Zeilen (siehe
# items_zusammenfassen) - output_csv selbst bleibt unveraendert.
items_zus = items_zusammenfassen(items)
output_csv_zus = zusammengefasster_dateiname(output_csv)
with open(output_csv_zus, "w", encoding="utf-8") as f:
f.write(header + "\n")
for item in items_zus:
f.write(format_csv_line(item) + "\n")
print(f"[export_sivas] Zusammengefasste CSV geschrieben: {output_csv_zus} "
f"({len(items_zus)} Zeilen)")
# Zweite, lesbare Kopie fuer die manuelle Pruefung (Merkmalnummer + # Zweite, lesbare Kopie fuer die manuelle Pruefung (Merkmalnummer +
# Bezeichnung aus cfg/Merkmalsstamm_in_Sivas.xlsx) - wird von Sivas # Bezeichnung aus cfg/Merkmalsstamm_in_Sivas.xlsx) - wird von Sivas
# selbst NICHT eingelesen, nur output_csv ist der eigentliche Import. # selbst NICHT eingelesen, nur output_csv ist der eigentliche Import.
+39 -9
View File
@@ -789,6 +789,23 @@
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; TESTS: GeruestInsert.lsp - reine Hilfsfunktionen ;; TESTS: GeruestInsert.lsp - reine Hilfsfunktionen
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; Anzahl der Artikel (Blatt-Eintraege, Gruppen "teile" rekursiv) ohne
;; sivas_id oder ohne farbig-Schluessel. assoc statt ssg-json-get: false
;; wird zu nil geparst, der Schluessel selbst muss aber vorhanden sein.
(defun tu-geruest-artikel-unvollstaendig (eintraege / n teile)
(setq n 0)
(foreach e eintraege
(setq teile (ssg-json-get e "teile"))
(if teile
(setq n (+ n (tu-geruest-artikel-unvollstaendig teile)))
(if (or (null (ssg-json-get e "sivas_id"))
(null (assoc "farbig" e)))
(setq n (1+ n)))
)
)
n
)
(defun tu-tests-geruest ( / _eintrag _gruppe _teile _konfig _rec _pfad _f (defun tu-tests-geruest ( / _eintrag _gruppe _teile _konfig _rec _pfad _f
_zurueck _bgtest _saeulen) _zurueck _bgtest _saeulen)
(princ "\n[GeruestInsert]") (princ "\n[GeruestInsert]")
@@ -938,16 +955,29 @@
(tu-eq "geruest/saeulen-vorhanden" T (if _saeulen T nil)) (tu-eq "geruest/saeulen-vorhanden" T (if _saeulen T nil))
(if _saeulen (if _saeulen
(progn (progn
(tu-eq "geruest/saeulen-varianten" 6 ;; Aufbau statt fester Anzahl - geruest.json wird noch gepflegt.
(length (ssg-json-get _saeulen "geometrie_varianten"))) ;; Jeder Artikel braucht sivas_id und ein (auch false) farbig-Feld.
(tu-eq "geruest/saeulen-anbauteile" 7 (tu-eq "geruest/saeulen-varianten" T
(length (ssg-json-get _saeulen "anbaugruppen"))) (if (ssg-json-get _saeulen "geometrie_varianten") T nil))
(tu-eq "geruest/saeulen-varianten-artikel" 0
(tu-geruest-artikel-unvollstaendig
(ssg-json-get _saeulen "geometrie_varianten")))
(tu-eq "geruest/saeulen-anbauteile" T
(if (ssg-json-get _saeulen "anbaugruppen") T nil))
(tu-eq "geruest/saeulen-anbauteile-artikel" 0
(tu-geruest-artikel-unvollstaendig
(ssg-json-get _saeulen "anbaugruppen")))
(tu-eq "geruest/saeulen-titel" "Saeulen" (geruest-titel _saeulen)) (tu-eq "geruest/saeulen-titel" "Saeulen" (geruest-titel _saeulen))
;; R 1" ist die erste Variante und traegt die eigene TEILEART ;; Variante R 1" traegt die eigene TEILEART - per Name gesucht,
(tu-eq "geruest/saeulen-1zoll-teileart" "Geruest Saeule 1 Zoll" ;; die Reihenfolge in geruest.json ist nicht fest.
(geruest-variante-wert (setq _rec (geruest-name-index "R 1\""
(car (ssg-json-get _saeulen "geometrie_varianten")) (ssg-json-get _saeulen "geometrie_varianten")))
_saeulen "teileart")) (tu-eq "geruest/saeulen-1zoll-vorhanden" T (if _rec T nil))
(if _rec
(tu-eq "geruest/saeulen-1zoll-teileart" "Geruest Saeule 1 Zoll"
(geruest-variante-wert
(nth _rec (ssg-json-get _saeulen "geometrie_varianten"))
_saeulen "teileart")))
) )
) )