Dispogruppe-Header korrigiert, Container-Referenzen und --type 2D/3D fuer set_attributs.py

Sivasnr-Werte mit 0_BGxxxxxx-Mustern werden jetzt ueber die neue
[container]-Config-Sektion in kommaseparierte Listen aufgeloest.
set_attributs.py kann nun explizit zwischen data/omniflo/2D und /3D
als Quellverzeichnis waehlen.
This commit is contained in:
2026-07-09 16:03:22 +02:00
parent 335c6d7875
commit 0b89ed6c5e
2 changed files with 61 additions and 10 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
export_sivas.py - Erzeugt Sivas-Export-CSV mit gruppierten Zeilen und Summierungszeilen. export_sivas.py - Erzeugt Sivas-Export-CSV mit gruppierten Zeilen und Summierungszeilen.
Neues CSV-Format: Neues CSV-Format:
Nr;TeileArt;SivasNummer;Dispgruppe;Anzahl;Laenge[mm];Hoehe[m];details Nr;TeileArt;SivasNummer;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details
- Omniflo Bogen/Weiche: gruppiert nach SivasNummer, Anzahl summiert - Omniflo Bogen/Weiche: gruppiert nach SivasNummer, Anzahl summiert
- Omniflo Gerade: gruppiert nach ARTINR-Attribut, Laenge[mm] summiert - Omniflo Gerade: gruppiert nach ARTINR-Attribut, Laenge[mm] summiert
@@ -492,7 +492,7 @@ def main():
items, _ = process_blocks(blocks, lookup) items, _ = process_blocks(blocks, lookup)
header = "Nr;TeileArt;SivasNummer;Dispgruppe;Anzahl;Laenge[mm];Hoehe[m];details" header = "Nr;TeileArt;SivasNummer;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details"
with open(output_csv, "w", encoding="utf-8") as f: with open(output_csv, "w", encoding="utf-8") as f:
f.write(header + "\n") f.write(header + "\n")
for item in items: for item in items:
+59 -8
View File
@@ -39,13 +39,14 @@ Umgebungsvariablen:
DXFM_RESULTS - Pfad zum results/-Verzeichnis DXFM_RESULTS - Pfad zum results/-Verzeichnis
Aufruf: Aufruf:
python lib/set_attributs.py [--number SIVASNR] [--dry-run] python lib/set_attributs.py [--number SIVASNR] [--dry-run] [--type 2D|3D]
""" """
import argparse import argparse
import configparser import configparser
import json import json
import os import os
import re
import sys import sys
import ezdxf import ezdxf
@@ -64,6 +65,7 @@ def load_config(cfg_path):
"bogen": [("BESCHR", "ProfilTyp"), ("ARTINR", "Sivasnr"), ...], "bogen": [("BESCHR", "ProfilTyp"), ("ARTINR", "Sivasnr"), ...],
"weiche": [("BESCHR", "ProfilTyp"), ...], "weiche": [("BESCHR", "ProfilTyp"), ...],
"layer": {"bogen": "OF Boegen A-2", "weiche_90": "Weiche 90 Grad A-2", ...}, "layer": {"bogen": "OF Boegen A-2", "weiche_90": "Weiche 90 Grad A-2", ...},
"container": {"0_BG071090": "0_B10071, 0_B10090, 0_B10090", ...},
} }
""" """
config = configparser.ConfigParser() config = configparser.ConfigParser()
@@ -81,6 +83,13 @@ def load_config(cfg_path):
val = val[1:-1] val = val[1:-1]
layer_map[key] = val layer_map[key] = val
result["layer"] = layer_map result["layer"] = layer_map
elif section == "container":
# Container-Sektion: 0_BGxxxxxx -> bereinigte, kommaseparierte Liste
container_map = {}
for key, val in config.items(section):
parts = [p.strip() for p in val.split(",") if p.strip()]
container_map[key] = ", ".join(parts)
result["container"] = container_map
else: else:
attrs = [] attrs = []
for tag, source in config.items(section): for tag, source in config.items(section):
@@ -131,12 +140,33 @@ def get_layer_for_entry(typ, json_entry, layer_map):
return "" return ""
def resolve_value(source, json_entry, sivasnr): CONTAINER_PATTERN = re.compile(r"0_BG\d+")
def resolve_container_refs(text, container_map):
"""
Ersetzt jedes Vorkommen von 0_BGxxxxxx in text durch die zugehoerige
kommaseparierte Liste aus [container]. Kommt das Muster mehrfach vor,
wird jedes Vorkommen einzeln ersetzt. Unbekannte 0_BG-Werte bleiben
unveraendert stehen.
"""
if not container_map or "0_BG" not in text:
return text
return CONTAINER_PATTERN.sub(
lambda m: container_map.get(m.group(0), m.group(0)), text
)
def resolve_value(source, json_entry, sivasnr, container_map=None):
""" """
Loest einen Quell-Ausdruck aus der Config auf: Loest einen Quell-Ausdruck aus der Config auf:
- "fester Text" -> fester Text - "fester Text" -> fester Text
- {sivasnr} -> SivasNr-Wert - {sivasnr} -> SivasNr-Wert
- Feldname -> Wert aus json_entry - Feldname -> Wert aus json_entry
Wird das Feld "Sivasnr" aufgeloest und enthaelt der Wert ein oder
mehrere 0_BGxxxxxx-Muster, werden diese anhand von [container] durch
die dort hinterlegte kommaseparierte Liste ersetzt.
""" """
source = source.strip() source = source.strip()
@@ -152,7 +182,10 @@ def resolve_value(source, json_entry, sivasnr):
val = json_entry.get(source) val = json_entry.get(source)
if val is None: if val is None:
return "" return ""
return str(val) val = str(val)
if source == "Sivasnr":
val = resolve_container_refs(val, container_map)
return val
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -225,7 +258,7 @@ def wrap_entities_in_block(doc, block_name):
def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr, def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr,
layer_value=""): layer_value="", container_map=None):
""" """
Oeffnet eine DXF-Datei, fuegt ATTDEF-Entities in den Modelspace ein Oeffnet eine DXF-Datei, fuegt ATTDEF-Entities in den Modelspace ein
und speichert das Ergebnis. und speichert das Ergebnis.
@@ -233,6 +266,7 @@ def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr,
attr_defs: Liste von (TAG, source_expression) aus der Config attr_defs: Liste von (TAG, source_expression) aus der Config
json_entry: Dict mit den JSON-Daten fuer dieses Element json_entry: Dict mit den JSON-Daten fuer dieses Element
layer_value: Layer-Name aus [layer]-Sektion (wird als LAYER-ATTDEF gesetzt) layer_value: Layer-Name aus [layer]-Sektion (wird als LAYER-ATTDEF gesetzt)
container_map: Dict aus [container]-Sektion fuer 0_BG-Aufloesung in Sivasnr
""" """
doc = ezdxf.readfile(dxf_path) doc = ezdxf.readfile(dxf_path)
msp = doc.modelspace() msp = doc.modelspace()
@@ -265,7 +299,7 @@ def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr,
y_pos = y_start y_pos = y_start
count = 0 count = 0
for tag, source in all_defs: for tag, source in all_defs:
default_value = resolve_value(source, json_entry, sivasnr) default_value = resolve_value(source, json_entry, sivasnr, container_map)
prompt = tag prompt = tag
msp.add_attdef( msp.add_attdef(
@@ -296,6 +330,7 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
# Config laden # Config laden
attr_config = load_config(cfg_path) attr_config = load_config(cfg_path)
layer_map = attr_config.pop("layer", {}) layer_map = attr_config.pop("layer", {})
container_map = attr_config.pop("container", {})
print(f"[set_attributs] Config geladen: {list(attr_config.keys())}") print(f"[set_attributs] Config geladen: {list(attr_config.keys())}")
if layer_map: if layer_map:
print(f" [layer] {len(layer_map)} Zuordnungen: " print(f" [layer] {len(layer_map)} Zuordnungen: "
@@ -319,7 +354,18 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
out_dir = os.path.join(results_dir, "omniflo") out_dir = os.path.join(results_dir, "omniflo")
os.makedirs(out_dir, exist_ok=True) os.makedirs(out_dir, exist_ok=True)
omniflo_dir = os.environ.get("DXFM_OMNIFLO") or os.path.join(data_dir, "omniflo") dxfm_dim = os.environ.get("DXFM_DIM")
if dxfm_dim:
omniflo_dir = os.path.join(data_dir, "omniflo", dxfm_dim)
else:
omniflo_dir = os.environ.get("DXFM_OMNIFLO") or os.path.join(data_dir, "omniflo")
if not os.path.isdir(omniflo_dir) or not any(
f.lower().endswith(".dxf") for f in os.listdir(omniflo_dir)
):
print(f"[set_attributs] FEHLER: Im Quellverzeichnis '{omniflo_dir}' "
f"wurden keine DXF-Dateien gefunden. eventuell DXFM_DIM=2D in Umgebung setzen")
sys.exit(1)
processed = 0 processed = 0
skipped = 0 skipped = 0
errors = 0 errors = 0
@@ -348,7 +394,7 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
output_path = os.path.join(out_dir, dxf_name) output_path = os.path.join(out_dir, dxf_name)
if dry_run: if dry_run:
values = {tag: resolve_value(src, entry, sivasnr) values = {tag: resolve_value(src, entry, sivasnr, container_map)
for tag, src in attr_defs} for tag, src in attr_defs}
if layer_value: if layer_value:
values["LAYER"] = layer_value values["LAYER"] = layer_value
@@ -359,7 +405,8 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
try: try:
count = add_attdefs_to_dxf(dxf_path, output_path, attr_defs, count = add_attdefs_to_dxf(dxf_path, output_path, attr_defs,
entry, sivasnr, entry, sivasnr,
layer_value=layer_value) layer_value=layer_value,
container_map=container_map)
processed += 1 processed += 1
print(f" OK: {sivasnr} ({typ}) -> {count} Attribute -> {output_path}") print(f" OK: {sivasnr} ({typ}) -> {count} Attribute -> {output_path}")
except Exception as e: except Exception as e:
@@ -379,9 +426,13 @@ def main():
help="Nur diese SivasNr verarbeiten") help="Nur diese SivasNr verarbeiten")
parser.add_argument("--dry-run", "-d", action="store_true", parser.add_argument("--dry-run", "-d", action="store_true",
help="Nur anzeigen, nicht schreiben") help="Nur anzeigen, nicht schreiben")
parser.add_argument("--type", "-t", choices=["2D", "3D"], default="2D",
help="Ueberschreibt DXFM_DIM und damit das "
"Quellverzeichnis data/omniflo/<type> (Default: 2D)")
args = parser.parse_args() args = parser.parse_args()
# Umgebungsvariablen # Umgebungsvariablen
os.environ["DXFM_DIM"] = args.type
data_dir = os.environ.get("DXFM_DATA") data_dir = os.environ.get("DXFM_DATA")
cfg_dir = os.environ.get("DXFM_CFG") cfg_dir = os.environ.get("DXFM_CFG")
results_dir = os.environ.get("DXFM_RESULTS") results_dir = os.environ.get("DXFM_RESULTS")