Testrun-Export nach tests/output umbiegen, Sivas-Bezeichner-Datei optional, Sivas-Merkmalzuordnung in Config
SSG_RUN_ALL_TESTS_EXPORT setzte *export-test-override* nicht, dadurch landeten CSV/Sivas-Exporte trotz Automatik-Modus in DXFM_RESULTS statt in tests/output wie die DXF/DWG-Dateien selbst - jetzt analog zu TEST_EXPORT_ALL behoben. EXPORTSIVAS schrieb bisher immer zusaetzlich eine "_mit_bezeichnung.csv" (Klartext-Kopie). Neuer Schalter "Sivas Export mit Bezeichner" im Dialog CSV_EXPORT_KONFIG (Standard aus) macht das optional. Die Attribut->Sivas-Merkmal-Zuordnung der build_*_details-Funktionen in export_sivas.py (Gefaellestrecke/Kreisel/Eckrad/Geruest/VarioFoerderer, soweit nicht von der Segmentanzahl abhaengig) liegt jetzt deklarativ in cfg/sivas.cfg statt hartcodiert im Python - neuer Loader lib/sivas_mapping.py. Ablauflogik (Gruppierung, Automation-Summenzeile, Segment-Zaehlung) bleibt Code. Gegen den alten Code mit synthetischen Testblöcken verifiziert: identische Ausgabe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
33d0688763
commit
e8c5cd0a63
+21
-1
@@ -116,6 +116,18 @@
|
||||
(defun csv:gefrorene-ausschliessen-setzen (an)
|
||||
(setq *csv-gefrorene-ausschliessen* (if an T nil)))
|
||||
|
||||
;; --- Sivas-Export zusaetzlich mit Klartext-Bezeichnung schreiben ---
|
||||
;; Standard aus (nil): EXPORTSIVAS schreibt nur die eigentliche <name>_sivas.csv
|
||||
;; (fuer den Sivas-Import). Nur bei eingeschaltetem Schalter schreibt
|
||||
;; lib/export_sivas.py zusaetzlich <name>_sivas_mit_bezeichnung.csv (Merkmal-
|
||||
;; Keys durch Klartext aus cfg/Merkmalsstamm_in_Sivas.xlsx ersetzt, reine
|
||||
;; Lesehilfe zur manuellen Pruefung, wird von Sivas selbst nicht eingelesen).
|
||||
;; Bedienbar im Dialog CSV_EXPORT_KONFIG ("Sivas Export mit Bezeichner").
|
||||
(if (not (boundp '*csv-sivas-mit-bezeichnung*)) (setq *csv-sivas-mit-bezeichnung* nil))
|
||||
(defun csv:sivas-mit-bezeichnung-p () (if *csv-sivas-mit-bezeichnung* T nil))
|
||||
(defun csv:sivas-mit-bezeichnung-setzen (an)
|
||||
(setq *csv-sivas-mit-bezeichnung* (if an T nil)))
|
||||
|
||||
;; Hilfsobjekte (car eines Records), deren Wrapper (nth 1) gefroren-fn
|
||||
;; erfuellt. records = Record-Listen der Form (hilfsobjekt wrapper ...), wie
|
||||
;; sie csv:sep-proxies-erzeugen, csv:element-proxies-erzeugen,
|
||||
@@ -2622,8 +2634,14 @@
|
||||
;; Synchron ausfuehren und Exitcode auswerten - ein stiller
|
||||
;; Fehlschlag (z.B. WindowsApps-Platzhalter statt Interpreter)
|
||||
;; darf nicht mehr als "Export gestartet" durchgehen.
|
||||
;; --mit-bezeichnung nur an export_sivas.py anhaengen (Schalter
|
||||
;; *csv-sivas-mit-bezeichnung*, Dialog CSV_EXPORT_KONFIG) - export_csv.py
|
||||
;; kennt dieses Argument nicht.
|
||||
(setq rc (export:run-python py-exe py-skript
|
||||
(list out-pfad (getenv "DXFM_DATA") ergebnis-pfad)
|
||||
(append (list out-pfad (getenv "DXFM_DATA") ergebnis-pfad)
|
||||
(if (and (= py-name (export:filename "sivas_script"))
|
||||
(csv:sivas-mit-bezeichnung-p))
|
||||
(list "--mit-bezeichnung")))
|
||||
log-pfad))
|
||||
(cond
|
||||
((null rc)
|
||||
@@ -2786,6 +2804,7 @@
|
||||
(set_tile "modus" (itoa idx))
|
||||
(set_tile "hilfskopien_behalten" (if (csv:hilfskopien-behalten-p) "1" "0"))
|
||||
(set_tile "gefrorene_ausschliessen" (if (csv:gefrorene-ausschliessen-p) "1" "0"))
|
||||
(set_tile "sivas_mit_bezeichnung" (if (csv:sivas-mit-bezeichnung-p) "1" "0"))
|
||||
)
|
||||
|
||||
(defun csv:export-konfig-accept ( / idx modus)
|
||||
@@ -2794,6 +2813,7 @@
|
||||
(if modus (csv:export-modus-setzen modus))
|
||||
(csv:hilfskopien-behalten-setzen (= (get_tile "hilfskopien_behalten") "1"))
|
||||
(csv:gefrorene-ausschliessen-setzen (= (get_tile "gefrorene_ausschliessen") "1"))
|
||||
(csv:sivas-mit-bezeichnung-setzen (= (get_tile "sivas_mit_bezeichnung") "1"))
|
||||
)
|
||||
|
||||
(defun c:CSV_EXPORT_KONFIG ()
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
# ============================================================
|
||||
# sivas.cfg - Attribut->Sivas-Merkmal-Zuordnung fuer lib/export_sivas.py
|
||||
#
|
||||
# Deckt NUR die "einfachen" Faelle ab: eine Sivas-Merkmalnummer entspricht
|
||||
# genau einem (ggf. per Fallback alternativen) Block-Attribut, optional mit
|
||||
# festem Default und/oder einer benannten Transform-Funktion. Berechnete
|
||||
# Werte, die von der Anzahl vorhandener Segmente abhaengen oder mehrere
|
||||
# Attribute kombinieren (VarioFoerderer-Gefaelle-/Vario-Segmentpaare 4312-
|
||||
# 4392/4353-4386, die ILS-Automation-Summenzeile) bleiben bewusst Python-Code
|
||||
# in export_sivas.py - ein flaches Attribut-Mapping kann bedingte/berechnete
|
||||
# Werte nicht abbilden.
|
||||
#
|
||||
# Format je Zeile (Abschnitt = Elementtyp, Key = Sivas-Merkmalnummer):
|
||||
# merkmal = attribut
|
||||
# merkmal = attribut : default
|
||||
# merkmal = attribut | alt_attribut : default
|
||||
# merkmal = attribut : default | transform
|
||||
#
|
||||
# attribut - Name des Block-Attributs (ssg-attrib-*, siehe jeweiliges
|
||||
# .lsp-Modul). Fehlt es in der Zeichnung, greift default.
|
||||
# | alt_attribut - optionaler Fallback-Attributname (aeltere Zeichnungen mit
|
||||
# abweichendem Attributnamen, z.B. N_SEPARATOREN vor der
|
||||
# Umbenennung zu ANZAHL_SEPARATOR). Wird nur genutzt, wenn
|
||||
# attribut selbst fehlt.
|
||||
# : default - Vorgabewert, falls weder attribut noch alt_attribut in
|
||||
# der Zeichnung gesetzt sind. Fehlt ":", ist default "".
|
||||
# | transform - Name einer Python-Funktion in export_sivas.py
|
||||
# (TRANSFORMS-Dict), die den ermittelten Rohwert
|
||||
# (Attribut- oder Default-Wert) in die Sivas-Anzeige
|
||||
# umwandelt (z.B. "0"/"1" -> "Ja"/"Nein"). Bekannte Namen:
|
||||
# ja_nein, geruest_typ_anzeige. Ein unbekannter Name
|
||||
# bricht NICHT ab, der Rohwert wird unveraendert
|
||||
# uebernommen (siehe sivas_mapping.anwenden).
|
||||
#
|
||||
# Abschnittsnamen entsprechen den bisherigen build_*_details-Funktionen in
|
||||
# export_sivas.py: [gefaellestrecke], [kreisel], [eckrad], [geruest]. Die
|
||||
# geruest-typ-Anzeige-Tabelle (ASCII-Attributwert -> Sivas-Klartext mit
|
||||
# Umlauten) UND SEPARATOR_BEARBEITUNG_ANZEIGE bleiben eigene, kleine
|
||||
# Abschnitte weiter unten.
|
||||
# ============================================================
|
||||
|
||||
[gefaellestrecke]
|
||||
2950= MONTAGEHOEHE_m : 0
|
||||
4384= : Gefällestrecke
|
||||
4318= L_GF_m
|
||||
4391= GF_WINKEL
|
||||
4337= ANZAHL_SEPARATOR : 0
|
||||
4338= : ohne HARTCOAT HRC 50 ± 5
|
||||
4339= ANZAHL_SCANNER : 0
|
||||
3992= GERUEST_EINZELMODUL : 0 | ja_nein
|
||||
3993= GERUEST_TYP : Schoenenberger Geruest | geruest_typ_anzeige
|
||||
# 4300 (Streckengruppe "ein Objekt"/"mehrere Objekte") und 4311 (Anzahl der
|
||||
# gruppierten Bloecke) bleiben Code (streckengruppe_objekt_text, anzahl-Param.).
|
||||
|
||||
[kreisel]
|
||||
2952= MONTAGEHOEHE_m : 0
|
||||
3511= : 1
|
||||
3515= ANZAHL_SEPARATOR | N_SEPARATOREN : 2
|
||||
3517= ANZAHL_SCANNER | N_SCANNER : 0
|
||||
3519= ANZAHL_RAMPEN | N_RAMPEN : 0
|
||||
3992= GERUEST_EINZELMODUL : 0 | ja_nein
|
||||
3993= GERUEST_TYP : Schoenenberger Geruest | geruest_typ_anzeige
|
||||
# 3510 (Kreiselart-Anzeigetext) und 3512 (Abstand in m, gerundet) bleiben
|
||||
# Code (bedingte Umbenennung bzw. Umrechnung mm->m).
|
||||
|
||||
[eckrad]
|
||||
# Bewusst (noch) beschreibende Klartext-Keys statt Merkmalnummern (kein
|
||||
# bekanntes Sivas-Merkmal fuer Eckrad-Geruest hinterlegt) - details_mit_
|
||||
# bezeichnung() in export_sivas.py laesst nicht-numerische Keys unveraendert.
|
||||
# "ist_eins" liefert ein ECHTES bool (True/False), NICHT "Ja"/"Nein" wie
|
||||
# ja_nein bei Kreisel/Gefaellestrecke - json.dumps schreibt dann true/false
|
||||
# ohne Anfuehrungszeichen (bisheriges Verhalten von build_eckrad_details).
|
||||
Geruest fuer Einzelmodul= GERUEST_EINZELMODUL : 0 | ist_eins
|
||||
Geruestoption fuer Einzelmodul= GERUEST_TYP : Schoenenberger Geruest
|
||||
|
||||
[geruest]
|
||||
Farbe= FARBE
|
||||
|
||||
[variofoerderer]
|
||||
# Nur die von der Segmentanzahl UNABHAENGIGEN Top-Level-Felder einer VF_N-
|
||||
# Streckengruppe (siehe build_variofoerderer_details in export_sivas.py).
|
||||
# Die Gefaelle-/Vario-Segmentpaare (4312-4392, 4353/4354/4386) haengen an der
|
||||
# Listenlaenge von L_GF_m/L_VF_m und bleiben Code.
|
||||
2950= MONTAGEHOEHE_m : 0
|
||||
4329= GF_Bogen_L_90 : 0
|
||||
4330= GF_Bogen_L_60 : 0
|
||||
4331= GF_Bogen_L_30 : 0
|
||||
4332= GF_Bogen_R_90 : 0
|
||||
4333= GF_Bogen_R_60 : 0
|
||||
4334= GF_Bogen_R_30 : 0
|
||||
4373= VF_Bogen_A_90 : 0
|
||||
4374= VF_Bogen_A_60 : 0
|
||||
4375= VF_Bogen_A_30 : 0
|
||||
4376= VF_Bogen_I_90 : 0
|
||||
4377= VF_Bogen_I_60 : 0
|
||||
4378= VF_Bogen_I_30 : 0
|
||||
4381= ANZAHL_SEPARATOR : 0
|
||||
4382= : ohne HARTCOAT HRC 50 ± 5
|
||||
4383= ANZAHL_SCANNER : 0
|
||||
3992= GERUEST_EINZELMODUL : 0 | ja_nein
|
||||
3993= GERUEST_TYP : Schoenenberger Geruest | geruest_typ_anzeige
|
||||
|
||||
[geruest_typ_anzeige]
|
||||
# ASCII-Attributwert (GERUEST_TYP) -> Sivas-Klartext mit Umlauten/Sonder-
|
||||
# zeichen (Attribute selbst bleiben ASCII, siehe CLAUDE.md-Konvention).
|
||||
Schoenenberger Geruest= Schönenberger-Gerüst
|
||||
@@ -1,9 +1,10 @@
|
||||
// ============================================================
|
||||
// csv_export_konfig.dcl - CSV-Export Konfiguration (c:CSV_EXPORT_KONFIG,
|
||||
// export.lsp). Steuert *csv-export-modus* (Combobox mit drei sich
|
||||
// gegenseitig ausschliessenden Eintraegen) sowie den Debug-Schalter
|
||||
// csv_leave_proxies (Hilfskopien nach dem Export stehen lassen) und
|
||||
// *csv-gefrorene-ausschliessen* (gefrorene Layer nicht exportieren).
|
||||
// gegenseitig ausschliessenden Eintraegen), *csv-gefrorene-ausschliessen*
|
||||
// (gefrorene Layer nicht exportieren), *csv-sivas-mit-bezeichnung* (Sivas-
|
||||
// Export zusaetzlich mit Klartext-Bezeichnung schreiben) sowie den
|
||||
// Debug-Schalter csv_leave_proxies (Hilfskopien nach dem Export stehen lassen).
|
||||
//
|
||||
// popup_list statt radio_column: set_tile/get_tile auf einer
|
||||
// boxed_radio_column liefern/erwarten den KEY des gewaehlten radio_button
|
||||
@@ -29,6 +30,11 @@ csv_export_konfig : dialog {
|
||||
label = "Bloecke auf gefrorenen Layern nicht exportieren";
|
||||
}
|
||||
|
||||
: toggle {
|
||||
key = "sivas_mit_bezeichnung";
|
||||
label = "Sivas Export mit Bezeichner";
|
||||
}
|
||||
|
||||
spacer;
|
||||
|
||||
: boxed_column {
|
||||
|
||||
+93
-97
@@ -31,7 +31,7 @@ Neues CSV-Format:
|
||||
cfg/Merkmalsstamm_in_Sivas.xlsx fuer die Bezeichnung je Nummer.
|
||||
|
||||
Aufruf:
|
||||
python export_sivas.py <export_raw.json> <data_dir> <output.csv>
|
||||
python export_sivas.py <export_raw.json> <data_dir> <output.csv> [--mit-bezeichnung]
|
||||
|
||||
Argumente:
|
||||
export_raw.json - JSON-Datei mit allen INSERT-Entities aus der Zeichnung
|
||||
@@ -39,13 +39,15 @@ Argumente:
|
||||
fuer den Aufrufer export.lsp erhalten)
|
||||
output.csv - Ziel-CSV-Datei (reine Merkmalnummern, fuer den
|
||||
Sivas-Import)
|
||||
|
||||
Zusaetzlich zu output.csv wird immer eine zweite Datei
|
||||
"<output>_mit_bezeichnung.csv" geschrieben: gleicher Inhalt, aber jeder
|
||||
Merkmalnummer-Key im details-JSON wird durch die zugehoerige Klartext-
|
||||
Bezeichnung aus cfg/Merkmalsstamm_in_Sivas.xlsx ersetzt (Wert bleibt
|
||||
unveraendert) - reine Lesehilfe zur Pruefung, wird von Sivas selbst nicht
|
||||
eingelesen.
|
||||
--mit-bezeichnung - optional. Nur dann wird zusaetzlich zu output.csv
|
||||
eine zweite Datei "<output>_mit_bezeichnung.csv"
|
||||
geschrieben: gleicher Inhalt, aber jeder Merkmalnummer-
|
||||
Key im details-JSON wird durch die zugehoerige Klartext-
|
||||
Bezeichnung aus cfg/Merkmalsstamm_in_Sivas.xlsx ersetzt
|
||||
(Wert bleibt unveraendert) - reine Lesehilfe zur
|
||||
Pruefung, wird von Sivas selbst nicht eingelesen.
|
||||
Schalter im Dialog CSV_EXPORT_KONFIG
|
||||
("Sivas Export mit Bezeichner"), Standard aus.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -54,6 +56,7 @@ import os
|
||||
|
||||
from export_blockpatterns import load_patterns, matches_any, csv_quote, safe_float, safe_int
|
||||
from merkmalstamm import lade_merkmalstamm, bezeichnung_fuer
|
||||
import sivas_mapping
|
||||
|
||||
BLOCKPATTERNS = load_patterns()
|
||||
|
||||
@@ -81,11 +84,10 @@ def get_id(block):
|
||||
# --- Sivas-Merkmalnummern: Anzeigetexte, die nicht 1:1 aus dem Attribut
|
||||
# uebernommen werden koennen (Attribute bleiben ASCII, siehe CLAUDE.md-
|
||||
# Konvention "Alle Texte in Dateien ohne Umlaute" - Sivas erwartet aber die
|
||||
# deutsche Schreibweise mit Umlauten/Sonderzeichen). Nur hier zentral, nicht
|
||||
# ueber die uebliche ssg-text-Sprachdatei, da rein Sivas-seitig.
|
||||
GERUEST_TYP_ANZEIGE = {
|
||||
"Schoenenberger Geruest": "Schönenberger-Gerüst",
|
||||
}
|
||||
# deutsche Schreibweise mit Umlauten/Sonderzeichen). Tabelle kommt aus
|
||||
# cfg/sivas.cfg [geruest_typ_anzeige], nicht ueber die uebliche ssg-text-
|
||||
# Sprachdatei, da rein Sivas-seitig.
|
||||
GERUEST_TYP_ANZEIGE = sivas_mapping.load_flat_mapping("geruest_typ_anzeige")
|
||||
|
||||
SEPARATOR_BEARBEITUNG_ANZEIGE = "ohne HARTCOAT HRC 50 ± 5"
|
||||
|
||||
@@ -100,6 +102,20 @@ def ja_nein(flag_1_0):
|
||||
return "Ja" if flag_1_0 == "1" else "Nein"
|
||||
|
||||
|
||||
def ist_eins(flag_1_0):
|
||||
"""Wandelt ein "0"/"1"-Attribut in ein echtes bool um (True/False im
|
||||
JSON, NICHT "Ja"/"Nein" wie ja_nein - siehe build_eckrad_details)."""
|
||||
return flag_1_0 == "1"
|
||||
|
||||
|
||||
# --- Transform-Namen, wie sie in cfg/sivas.cfg hinter "|" stehen koennen ---
|
||||
TRANSFORMS = {
|
||||
"ja_nein": ja_nein,
|
||||
"geruest_typ_anzeige": geruest_typ_anzeige,
|
||||
"ist_eins": ist_eins,
|
||||
}
|
||||
|
||||
|
||||
def streckengruppe_objekt_text(attribs):
|
||||
"""Merkmal 4300 "Streckengruppe": "mehrere Objekte", wenn der Block ein
|
||||
kombinierter Streckengruppe-Block (Gefaelle+Vario, TYP-Attribut
|
||||
@@ -108,27 +124,24 @@ def streckengruppe_objekt_text(attribs):
|
||||
return "mehrere Objekte" if typ == "Streckengruppe" else "ein Objekt"
|
||||
|
||||
|
||||
SIVAS_MAPPING_GEFAELLESTRECKE = sivas_mapping.load_section_mapping("gefaellestrecke")
|
||||
|
||||
|
||||
def build_gefaellestrecke_details(block, anzahl=1):
|
||||
"""Merkmale-Dict (Sivas-Merkmalnummern) fuer eine Gruppe gleichartiger
|
||||
ILS Gefaellestrecken (GF_N-Bloecke). anzahl ist die Anzahl der in dieser
|
||||
Zeile zusammengefassten, baugleichen Bloecke (4311) - anders als bei
|
||||
Kreisel steht hier auf der Sivas-Hauptzeile selbst immer Anzahl=1 (siehe
|
||||
process_blocks), die reale Stueckzahl steckt nur in 4311."""
|
||||
attribs = block.get("attribs", {})
|
||||
process_blocks), die reale Stueckzahl steckt nur in 4311.
|
||||
|
||||
return {
|
||||
"2950": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"4300": streckengruppe_objekt_text(attribs),
|
||||
"4384": "Gefällestrecke",
|
||||
"4311": str(anzahl),
|
||||
"4318": attribs.get("L_GF_m", ""),
|
||||
"4391": attribs.get("GF_WINKEL", ""),
|
||||
"4337": attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
"4338": SEPARATOR_BEARBEITUNG_ANZEIGE,
|
||||
"4339": attribs.get("ANZAHL_SCANNER", "0"),
|
||||
"3992": ja_nein(attribs.get("GERUEST_EINZELMODUL", "0")),
|
||||
"3993": geruest_typ_anzeige(attribs.get("GERUEST_TYP", "Schoenenberger Geruest")),
|
||||
}
|
||||
Die reine Attribut->Merkmal-Zuordnung kommt aus cfg/sivas.cfg
|
||||
[gefaellestrecke]; 4300 (Streckengruppe-Text) und 4311 (Anzahl-Parameter)
|
||||
sind berechnete/parametrisierte Werte und bleiben hier Code."""
|
||||
attribs = block.get("attribs", {})
|
||||
details = sivas_mapping.anwenden(SIVAS_MAPPING_GEFAELLESTRECKE, attribs, TRANSFORMS)
|
||||
details["4300"] = streckengruppe_objekt_text(attribs)
|
||||
details["4311"] = str(anzahl)
|
||||
return details
|
||||
|
||||
|
||||
def kreisel_signature(block):
|
||||
@@ -203,6 +216,9 @@ def parse_str_list(csv_str):
|
||||
return [p.strip() for p in str(csv_str or "").split(",") if p.strip()]
|
||||
|
||||
|
||||
SIVAS_MAPPING_VARIOFOERDERER = sivas_mapping.load_section_mapping("variofoerderer")
|
||||
|
||||
|
||||
def build_variofoerderer_details(block):
|
||||
"""
|
||||
Merkmale-Dict (Sivas-Merkmalnummern) fuer eine VF_N-Streckengruppe
|
||||
@@ -216,12 +232,14 @@ def build_variofoerderer_details(block):
|
||||
ausstehenden Neuzuordnung von Separator/Scanner zum jeweiligen
|
||||
Streckenabschnitt) - bis dahin steht 4337/4339 fest auf "0" und der volle
|
||||
Attributwert steht auf 4381/4383.
|
||||
|
||||
Die von der Segmentanzahl UNABHAENGIGEN Felder (2950, GF_Bogen_*/
|
||||
VF_Bogen_*-Zaehler, 4381-4383, 3992/3993) kommen aus cfg/sivas.cfg
|
||||
[variofoerderer]. Die Gefaelle-/Vario-Segmentpaare (4312-4392, 4353/
|
||||
4354/4386, abhaengig von len(gf_laengen)/len(vf_laengen)) bleiben Code.
|
||||
"""
|
||||
attribs = block.get("attribs", {})
|
||||
|
||||
def to_int(key):
|
||||
return safe_int(attribs.get(key, "0") or "0")
|
||||
|
||||
gf_laengen = parse_float_list(attribs.get("L_GF_m", ""))
|
||||
gf_winkel = parse_float_list(attribs.get("GF_WINKEL", ""))
|
||||
vf_laengen = parse_float_list(attribs.get("L_VF_m", ""))
|
||||
@@ -229,11 +247,9 @@ def build_variofoerderer_details(block):
|
||||
vf_richtung = parse_str_list(attribs.get("ANTRIEBFAHRTRICHTUNG", ""))
|
||||
motorseiten = parse_str_list(attribs.get("MOTORSEITE", ""))
|
||||
|
||||
details = {
|
||||
"2950": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"4300": "mehrere Objekte",
|
||||
"4311": "1",
|
||||
}
|
||||
details = sivas_mapping.anwenden(SIVAS_MAPPING_VARIOFOERDERER, attribs, TRANSFORMS)
|
||||
details["4300"] = "mehrere Objekte"
|
||||
details["4311"] = "1"
|
||||
|
||||
if len(gf_laengen) >= 1:
|
||||
details["4312"] = "Ja"
|
||||
@@ -249,85 +265,62 @@ def build_variofoerderer_details(block):
|
||||
else:
|
||||
details["4314"] = "Nein"
|
||||
|
||||
details.update({
|
||||
"4329": to_int("GF_Bogen_L_90"),
|
||||
"4330": to_int("GF_Bogen_L_60"),
|
||||
"4331": to_int("GF_Bogen_L_30"),
|
||||
"4332": to_int("GF_Bogen_R_90"),
|
||||
"4333": to_int("GF_Bogen_R_60"),
|
||||
"4334": to_int("GF_Bogen_R_30"),
|
||||
"4337": "0",
|
||||
"4338": SEPARATOR_BEARBEITUNG_ANZEIGE,
|
||||
"4339": "0",
|
||||
"4340": "Ja" if vf_laengen else "Nein",
|
||||
"4341": sum(1 for seite in motorseiten if seite.lower() == "links"),
|
||||
"4342": sum(1 for seite in motorseiten if seite.lower() == "rechts"),
|
||||
})
|
||||
details["4337"] = "0"
|
||||
details["4338"] = SEPARATOR_BEARBEITUNG_ANZEIGE
|
||||
details["4339"] = "0"
|
||||
details["4340"] = "Ja" if vf_laengen else "Nein"
|
||||
details["4341"] = sum(1 for seite in motorseiten if seite.lower() == "links")
|
||||
details["4342"] = sum(1 for seite in motorseiten if seite.lower() == "rechts")
|
||||
|
||||
if vf_laengen:
|
||||
details["4353"] = vf_laengen[0]
|
||||
details["4354"] = vf_richtung[0].lower() if vf_richtung else ""
|
||||
details["4386"] = str(int(vf_winkel[0])) if vf_winkel else ""
|
||||
|
||||
details.update({
|
||||
"4373": to_int("VF_Bogen_A_90"),
|
||||
"4374": to_int("VF_Bogen_A_60"),
|
||||
"4375": to_int("VF_Bogen_A_30"),
|
||||
"4376": to_int("VF_Bogen_I_90"),
|
||||
"4377": to_int("VF_Bogen_I_60"),
|
||||
"4378": to_int("VF_Bogen_I_30"),
|
||||
"4381": attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
"4382": SEPARATOR_BEARBEITUNG_ANZEIGE,
|
||||
"4383": attribs.get("ANZAHL_SCANNER", "0"),
|
||||
"3992": ja_nein(attribs.get("GERUEST_EINZELMODUL", "0")),
|
||||
"3993": geruest_typ_anzeige(attribs.get("GERUEST_TYP", "Schoenenberger Geruest")),
|
||||
})
|
||||
return {k: str(v) for k, v in details.items()}
|
||||
|
||||
|
||||
SIVAS_MAPPING_KREISEL = sivas_mapping.load_section_mapping("kreisel")
|
||||
SIVAS_MAPPING_ECKRAD = sivas_mapping.load_section_mapping("eckrad")
|
||||
SIVAS_MAPPING_GERUEST = sivas_mapping.load_section_mapping("geruest")
|
||||
|
||||
|
||||
def build_kreisel_details(block):
|
||||
"""Merkmale-Dict (Sivas-Merkmalnummern) fuer eine Gruppe gleichartiger
|
||||
ILS Kreisel. 3511 ("Kreisel - zu fertigende Menge") steht bewusst fest
|
||||
auf "1" - die reale Stueckzahl der Gruppe steht auf der Sivas-Hauptzeile
|
||||
selbst (Anzahl-Spalte, siehe kreisel_groups in process_blocks)."""
|
||||
selbst (Anzahl-Spalte, siehe kreisel_groups in process_blocks).
|
||||
|
||||
Die reine Attribut->Merkmal-Zuordnung kommt aus cfg/sivas.cfg [kreisel];
|
||||
3510 (Kreiselart-Anzeigetext) und 3512 (Abstand mm->m, gerundet) sind
|
||||
berechnete Werte und bleiben hier Code."""
|
||||
attribs = block.get("attribs", {})
|
||||
abstand_m = str(round(safe_float(attribs.get("ABSTAND", "2300"), 2300.0) / 1000.0, 2))
|
||||
kreiselart = "Kreisel mit PINBAND" if attribs.get("KREISELART", "STANDARD") == "PIN" else "Kreisel"
|
||||
return {
|
||||
"2952": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"3510": kreiselart,
|
||||
"3511": "1",
|
||||
"3512": abstand_m,
|
||||
"3515": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")),
|
||||
"3517": attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")),
|
||||
"3519": attribs.get("ANZAHL_RAMPEN", attribs.get("N_RAMPEN", "0")),
|
||||
"3992": ja_nein(attribs.get("GERUEST_EINZELMODUL", "0")),
|
||||
"3993": geruest_typ_anzeige(attribs.get("GERUEST_TYP", "Schoenenberger Geruest")),
|
||||
}
|
||||
details = sivas_mapping.anwenden(SIVAS_MAPPING_KREISEL, attribs, TRANSFORMS)
|
||||
details["3510"] = kreiselart
|
||||
details["3512"] = abstand_m
|
||||
return details
|
||||
|
||||
|
||||
def build_eckrad_details(block):
|
||||
"""Merkmale-Dict fuer ein ILS Eckrad."""
|
||||
"""Merkmale-Dict fuer ein ILS Eckrad. Vollstaendig aus cfg/sivas.cfg
|
||||
[eckrad] (keine berechneten Werte)."""
|
||||
attribs = block.get("attribs", {})
|
||||
return {
|
||||
"Geruest fuer Einzelmodul": attribs.get("GERUEST_EINZELMODUL", "0") == "1",
|
||||
"Geruestoption fuer Einzelmodul": attribs.get("GERUEST_TYP", "Schoenenberger Geruest"),
|
||||
}
|
||||
return sivas_mapping.anwenden(SIVAS_MAPPING_ECKRAD, attribs, TRANSFORMS)
|
||||
|
||||
|
||||
def build_geruest_details(block):
|
||||
"""Merkmale-Dict fuer eine GR_-Geruest-Baugruppe (siehe pattern_geruest in
|
||||
cfg/export.cfg, GeruestInsert.lsp *geruest-attrib-defs*). ARTINR/
|
||||
BEZEICHNUNG/TEILEART/LAENGE wandern in feste Sivas-Spalten (sivasnr/
|
||||
bezeichnung/teileart/laenge_mm), FARBE bleibt als Rest-Attribut hier.
|
||||
LAYER ist reine BricsCAD-Zeichnungsinformation ohne Sivas-Bedeutung und
|
||||
wird bewusst NICHT mit exportiert (anders als im CSV-Export
|
||||
export_csv.py, wo sie fuer die Zuordnung in der Zeichnung nuetzlich
|
||||
bleibt)."""
|
||||
bezeichnung/teileart/laenge_mm), FARBE bleibt als Rest-Attribut hier
|
||||
(cfg/sivas.cfg [geruest]). LAYER ist reine BricsCAD-Zeichnungsinformation
|
||||
ohne Sivas-Bedeutung und wird bewusst NICHT mit exportiert (anders als im
|
||||
CSV-Export export_csv.py, wo sie fuer die Zuordnung in der Zeichnung
|
||||
nuetzlich bleibt)."""
|
||||
attribs = block.get("attribs", {})
|
||||
return {
|
||||
"Farbe": attribs.get("FARBE", ""),
|
||||
}
|
||||
return sivas_mapping.anwenden(SIVAS_MAPPING_GERUEST, attribs, TRANSFORMS)
|
||||
|
||||
|
||||
# --- Bekannte Bloecke ignorieren ---
|
||||
@@ -890,7 +883,7 @@ def lesbarer_dateiname(output_csv):
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("Aufruf: python export_sivas.py <export_raw.json> <data_dir> <output.csv>")
|
||||
print("Aufruf: python export_sivas.py <export_raw.json> <data_dir> <output.csv> [--mit-bezeichnung]")
|
||||
sys.exit(1)
|
||||
|
||||
raw_json_path = sys.argv[1]
|
||||
@@ -898,6 +891,7 @@ def main():
|
||||
# kommen aus ARTINR, nicht mehr aus dem Katalog. Argument bleibt fuer
|
||||
# den Aufrufer (export.lsp) bestehen.
|
||||
output_csv = sys.argv[3]
|
||||
mit_bezeichnung = "--mit-bezeichnung" in sys.argv[4:]
|
||||
|
||||
blocks = load_json(raw_json_path)
|
||||
print(f"[export_sivas] {len(blocks)} Bloecke geladen.")
|
||||
@@ -914,14 +908,16 @@ def main():
|
||||
# Zweite, lesbare Kopie fuer die manuelle Pruefung (Merkmalnummer +
|
||||
# Bezeichnung aus cfg/Merkmalsstamm_in_Sivas.xlsx) - wird von Sivas
|
||||
# selbst NICHT eingelesen, nur output_csv ist der eigentliche Import.
|
||||
merkmalstamm = lade_merkmalstamm()
|
||||
output_csv_lesbar = lesbarer_dateiname(output_csv)
|
||||
with open(output_csv_lesbar, "w", encoding="utf-8") as f:
|
||||
f.write(header + "\n")
|
||||
for item in items:
|
||||
details = details_mit_bezeichnung(item["details"], merkmalstamm)
|
||||
f.write(format_csv_line(item, details=details) + "\n")
|
||||
print(f"[export_sivas] Lesbare Kopie geschrieben: {output_csv_lesbar}")
|
||||
# Nur bei explizitem --mit-bezeichnung-Schalter (Dialog CSV_EXPORT_KONFIG).
|
||||
if mit_bezeichnung:
|
||||
merkmalstamm = lade_merkmalstamm()
|
||||
output_csv_lesbar = lesbarer_dateiname(output_csv)
|
||||
with open(output_csv_lesbar, "w", encoding="utf-8") as f:
|
||||
f.write(header + "\n")
|
||||
for item in items:
|
||||
details = details_mit_bezeichnung(item["details"], merkmalstamm)
|
||||
f.write(format_csv_line(item, details=details) + "\n")
|
||||
print(f"[export_sivas] Lesbare Kopie geschrieben: {output_csv_lesbar}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sivas_mapping.py - Liest die Attribut->Sivas-Merkmal-Zuordnung aus
|
||||
cfg/sivas.cfg und wendet sie auf ein Block-Attribut-Dict an.
|
||||
|
||||
Einzige Quelle fuer die "einfachen" build_*_details-Zuordnungen in
|
||||
export_sivas.py (siehe cfg/sivas.cfg fuer das Zeilenformat und die
|
||||
Begruendung, warum nur die einfachen Faelle hier landen).
|
||||
"""
|
||||
|
||||
import configparser
|
||||
import functools
|
||||
import os
|
||||
|
||||
|
||||
def cfg_path_from_env(default_dir=None):
|
||||
"""Pfad zu sivas.cfg: DXFM_CFG env var, sonst <repo>/cfg."""
|
||||
dxfm_cfg = os.environ.get("DXFM_CFG")
|
||||
if dxfm_cfg:
|
||||
return os.path.join(dxfm_cfg, "sivas.cfg")
|
||||
base = default_dir or os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.join(base, "cfg", "sivas.cfg")
|
||||
|
||||
|
||||
def _parse_wert(roh):
|
||||
"""Zerlegt eine Config-Wertzeile "attribut|alt_attribut:default|transform"
|
||||
in (attribut, alt_attribut_oder_None, default, transform_oder_None).
|
||||
|
||||
Reihenfolge beim Parsen: erst am ":" in (attributteil, defaultteil)
|
||||
trennen (nur der erste ":" zaehlt, ein Default-Text soll selbst keinen
|
||||
":" enthalten muessen), danach im defaultteil am LETZTEN "|" den
|
||||
Transform-Namen abtrennen (ein Default-Text wie "ohne HARTCOAT..."
|
||||
enthaelt kein "|"). attributteil wird am ERSTEN "|" in Attribut/
|
||||
Fallback-Attribut gesplittet.
|
||||
"""
|
||||
roh = roh.strip()
|
||||
if ":" in roh:
|
||||
attribut_teil, rest = roh.split(":", 1)
|
||||
else:
|
||||
attribut_teil, rest = roh, ""
|
||||
|
||||
transform = None
|
||||
default = rest.strip()
|
||||
if "|" in rest:
|
||||
default_teil, transform_teil = rest.rsplit("|", 1)
|
||||
default = default_teil.strip()
|
||||
transform = transform_teil.strip() or None
|
||||
|
||||
attribut_teil = attribut_teil.strip()
|
||||
alt_attribut = None
|
||||
if "|" in attribut_teil:
|
||||
attribut, alt_attribut = (p.strip() for p in attribut_teil.split("|", 1))
|
||||
else:
|
||||
attribut = attribut_teil
|
||||
alt_attribut = alt_attribut or None
|
||||
|
||||
return (attribut or None, alt_attribut, default, transform)
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def load_sivas_cfg(cfg_path):
|
||||
"""Liest sivas.cfg einmal je Pfad (Prozess-Cache)."""
|
||||
parser = configparser.ConfigParser(inline_comment_prefixes=None)
|
||||
parser.optionxform = str # Keys (Merkmalnummern/Klartext-Keys) case-sensitive erhalten
|
||||
parser.read(cfg_path, encoding="utf-8")
|
||||
return parser
|
||||
|
||||
|
||||
def load_section_mapping(section, cfg_path=None):
|
||||
"""Liest einen Abschnitt aus sivas.cfg.
|
||||
|
||||
Rueckgabe: dict merkmal -> (attribut, alt_attribut, default, transform),
|
||||
wie von _parse_wert geliefert. Bei fehlender Datei/Sektion: leeres dict -
|
||||
Aufrufer bekommt dann einfach keine Merkmale aus der Config (bisheriges
|
||||
Verhalten vor der Config-Einfuehrung, kein harter Fehler).
|
||||
"""
|
||||
if cfg_path is None:
|
||||
cfg_path = cfg_path_from_env()
|
||||
parser = load_sivas_cfg(cfg_path)
|
||||
mapping = {}
|
||||
if parser.has_section(section):
|
||||
for merkmal, roh in parser.items(section):
|
||||
mapping[merkmal] = _parse_wert(roh)
|
||||
return mapping
|
||||
|
||||
|
||||
def load_flat_mapping(section, cfg_path=None):
|
||||
"""Liest einen Abschnitt als einfaches dict key -> value (z.B.
|
||||
[geruest_typ_anzeige]: ASCII-Wert -> Sivas-Klartext), ohne das
|
||||
attribut/default/transform-Format von load_section_mapping."""
|
||||
if cfg_path is None:
|
||||
cfg_path = cfg_path_from_env()
|
||||
parser = load_sivas_cfg(cfg_path)
|
||||
if parser.has_section(section):
|
||||
return {k: v.strip() for k, v in parser.items(section)}
|
||||
return {}
|
||||
|
||||
|
||||
def anwenden(section_mapping, attribs, transforms):
|
||||
"""Wendet eine per load_section_mapping() gelesene Zuordnung auf ein
|
||||
Block-Attribut-Dict an.
|
||||
|
||||
transforms: dict transform_name -> Funktion(wert) -> wert. Ein in der
|
||||
Config genannter, aber hier unbekannter Transform-Name wird ignoriert
|
||||
(Rohwert bleibt unveraendert) statt abzubrechen - ein Tippfehler in
|
||||
sivas.cfg darf den Export nicht zum Stillstand bringen.
|
||||
|
||||
Rueckgabe: dict merkmal -> wert. Ohne Transform wird der Rohwert zu str()
|
||||
gewandelt (bisheriges Verhalten der build_*_details-Funktionen: alles
|
||||
Merkmalnummer-Werte sind Strings). Ein Transform liefert seinen
|
||||
Rueckgabewert UNVERAENDERT (z.B. build_eckrad_details erwartet fuer
|
||||
"Geruest fuer Einzelmodul" ein echtes bool, kein "True"/"False"-String -
|
||||
json.dumps schreibt dann true/false statt eines Strings).
|
||||
"""
|
||||
ergebnis = {}
|
||||
for merkmal, (attribut, alt_attribut, default, transform) in section_mapping.items():
|
||||
if attribut is not None:
|
||||
wert = attribs.get(attribut)
|
||||
if wert is None and alt_attribut is not None:
|
||||
wert = attribs.get(alt_attribut)
|
||||
if wert is None:
|
||||
wert = default
|
||||
else:
|
||||
wert = default
|
||||
if transform:
|
||||
fn = transforms.get(transform)
|
||||
if fn is not None:
|
||||
ergebnis[merkmal] = fn(wert)
|
||||
continue
|
||||
ergebnis[merkmal] = str(wert)
|
||||
return ergebnis
|
||||
@@ -185,6 +185,16 @@
|
||||
(setq auto-export nil)
|
||||
)
|
||||
|
||||
;; *export-test-override* (Lisp/export.lsp, csv:run-export) fuer die Dauer
|
||||
;; des Automatik-Laufs setzen, damit c:EXPORTCSV/c:EXPORTSIVAS nach
|
||||
;; tests/output statt in den normalen Ergebnisordner (DXFM_RESULTS)
|
||||
;; schreiben - ein Testrun soll vollstaendig in tests/output landen.
|
||||
;; Analog zu TEST_EXPORT_ALL (tests/test_export_all.lsp). WICHTIG:
|
||||
;; Absichtlich KEIN (setenv "DXFM_RESULTS" ...), siehe dortiger Kommentar.
|
||||
(if auto-export
|
||||
(setq *export-test-override* tests-out-dir)
|
||||
)
|
||||
|
||||
(princ "\n")
|
||||
(princ "\n================================================================")
|
||||
(princ "\n SSG_RUN_ALL_TESTS")
|
||||
@@ -412,6 +422,8 @@
|
||||
;; GUI wieder einschalten (Batch fertig).
|
||||
(if (car (atoms-family 1 '("SSG-GUI-AN"))) (ssg-gui-an))
|
||||
|
||||
(setq *export-test-override* nil)
|
||||
|
||||
(princ "\n\n================================================================")
|
||||
(princ "\n SSG_RUN_ALL_TESTS abgeschlossen.")
|
||||
(princ (strcat "\n Ergebnisse in: " (getenv "DXFMAKRO") "/tests/output/"))
|
||||
|
||||
Reference in New Issue
Block a user