From b6b71c5997d632592d78a8bd4e296fcb4d33f173 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Thu, 23 Jul 2026 12:52:44 +0200 Subject: [PATCH] safe_float/safe_int Hilfsfunktionen: doppelten try/except-Code konsolidiert export_csv.py und export_sivas.py enthielten je ca. 10 nahezu identische try/except (ValueError, TypeError)-Bloecke fuer "String zu float/int mit Fallback". Neue gemeinsame Helfer safe_float()/safe_int() (export_blockpatterns.py) ersetzen diese Bloecke; dabei auch zwei bislang ungeschuetzte int()/float()-Aufrufe in export_sivas.py (Kreisel-Zaehler) abgesichert, die bei fehlerhaften Attributwerten sonst abgestuerzt waeren. Co-Authored-By: Claude Sonnet 5 --- lib/export_blockpatterns.py | 16 +++++++++ lib/export_csv.py | 28 ++++------------ lib/export_sivas.py | 67 +++++++++---------------------------- 3 files changed, 38 insertions(+), 73 deletions(-) diff --git a/lib/export_blockpatterns.py b/lib/export_blockpatterns.py index 92a2999..fb5cabd 100644 --- a/lib/export_blockpatterns.py +++ b/lib/export_blockpatterns.py @@ -68,6 +68,22 @@ def load_patterns(cfg_path=None): return patterns +def safe_float(value, default=0.0): + """Wandelt value in float um; bei fehlendem/ungueltigem Wert Fallback default.""" + try: + return float(value) + except (TypeError, ValueError): + return default + + +def safe_int(value, default=0): + """Wandelt value in int um; bei fehlendem/ungueltigem Wert Fallback default.""" + try: + return int(value) + except (TypeError, ValueError): + return default + + def matches_any(bname, patterns): """True, wenn bname (case-insensitiv) zu mindestens einem Muster passt.""" upper = bname.upper() diff --git a/lib/export_csv.py b/lib/export_csv.py index 6502015..7b745a2 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -41,7 +41,7 @@ import json import sys import os -from export_blockpatterns import load_patterns, matches_any, csv_quote +from export_blockpatterns import load_patterns, matches_any, csv_quote, safe_float from export_planquadrat import load_planquadrat_config, resolve_origins, compute_planquadrat from export_neighbors import ( load_neighbor_tolerance_mm, @@ -108,12 +108,9 @@ def get_drehung(block): """Liest DREHUNG aus Block-Attributen. Fallback: CAD-Rotation.""" attribs = block.get("attribs", {}) drehung_raw = attribs.get("DREHUNG") - if drehung_raw is not None: - try: - return float(drehung_raw) - except (ValueError, TypeError): - pass - return block.get("rotation", 0.0) + if drehung_raw is None: + return block.get("rotation", 0.0) + return safe_float(drehung_raw, block.get("rotation", 0.0)) def build_bogen_merkmale(block, eintrag): @@ -162,10 +159,7 @@ def get_laenge_mm(block): """Liest LAENGE (oder A) aus Block-Attributen. Fallback: 2000mm.""" attribs = block.get("attribs", {}) laenge = attribs.get("LAENGE") or attribs.get("A") - try: - return float(laenge) - except (TypeError, ValueError): - return 2000.0 + return safe_float(laenge, 2000.0) def build_gerade_merkmale(block): @@ -228,16 +222,8 @@ def build_omni_sum_merkmale(cnt_boegen, cnt_wk, cnt_einzel, cnt_delta, cnt_doppe def build_kreisel_merkmale(block): attribs = block.get("attribs", {}) - abstand_mm = attribs.get("ABSTAND", "2300") - try: - abstand_m = str(round(float(abstand_mm) / 1000.0, 2)) - except (ValueError, TypeError): - abstand_m = "2.3" - hoehe_mm = attribs.get("HOEHE", "0") - try: - hoehe_m = str(round(float(hoehe_mm) / 1000.0, 2)) - except (ValueError, TypeError): - hoehe_m = "0" + abstand_m = str(round(safe_float(attribs.get("ABSTAND", "2300"), 2300.0) / 1000.0, 2)) + hoehe_m = str(round(safe_float(attribs.get("HOEHE", "0"), 0.0) / 1000.0, 2)) return { "Abstand (Kreiselachse A - Kreiselachse) in Meter": abstand_m, "Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")), diff --git a/lib/export_sivas.py b/lib/export_sivas.py index 9d40404..b50826a 100644 --- a/lib/export_sivas.py +++ b/lib/export_sivas.py @@ -33,7 +33,7 @@ import json import sys import os -from export_blockpatterns import load_patterns, matches_any, csv_quote +from export_blockpatterns import load_patterns, matches_any, csv_quote, safe_float, safe_int BLOCKPATTERNS = load_patterns() @@ -99,10 +99,7 @@ def build_gefaellestrecke_details(block): attribs = block.get("attribs", {}) def mm_to_m(key): - try: - return str(round(float(attribs.get(key, "0")) / 1000.0, 2)) - except (ValueError, TypeError): - return "0" + return str(round(safe_float(attribs.get(key, "0")) / 1000.0, 2)) return { "Hoehe oben": mm_to_m("HOEHE_VON_mm"), @@ -177,10 +174,7 @@ def build_variofoerderer_details(block): attribs = block.get("attribs", {}) def to_int(key): - try: - return int(attribs.get(key, "0") or "0") - except (ValueError, TypeError): - return 0 + return safe_int(attribs.get(key, "0") or "0") return { "Anzahl Foerdergruppen": to_int("ANZAHL_VF"), @@ -218,16 +212,8 @@ def build_variofoerderer_details(block): def build_kreisel_details(block): """Merkmale-Dict fuer einen ILS Kreisel.""" attribs = block.get("attribs", {}) - abstand_mm = attribs.get("ABSTAND", "2300") - try: - abstand_m = str(round(float(abstand_mm) / 1000.0, 2)) - except (ValueError, TypeError): - abstand_m = "2.3" - hoehe_mm = attribs.get("HOEHE", "0") - try: - hoehe_m = str(round(float(hoehe_mm) / 1000.0, 2)) - except (ValueError, TypeError): - hoehe_m = "0" + abstand_m = str(round(safe_float(attribs.get("ABSTAND", "2300"), 2300.0) / 1000.0, 2)) + hoehe_m = str(round(safe_float(attribs.get("HOEHE", "0"), 0.0) / 1000.0, 2)) return { "Hoehe in m": hoehe_m, "Kreiselart": attribs.get("KREISELART", "STANDARD"), @@ -353,13 +339,8 @@ def process_blocks(blocks, lookup): attribs = block.get("attribs", {}) artinr = attribs.get("ARTINR", "") key = artinr if artinr else "AP110" - laenge = 2000.0 laenge_attr = attribs.get("LAENGE") or attribs.get("A") - if laenge_attr: - try: - laenge = float(laenge_attr) - except (ValueError, TypeError): - pass + laenge = safe_float(laenge_attr, 2000.0) if key not in gerade_groups: gerade_groups[key] = { "anzahl": 0, "laenge_mm": 0.0, @@ -384,17 +365,17 @@ def process_blocks(blocks, lookup): kreisel_list.append(block) attribs = block.get("attribs", {}) counters["anzahl_kreisel"] += 1 - n_sep = int(attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")) or "2") + n_sep = safe_int(attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")) or "2", 2) kreiselart = attribs.get("KREISELART", "STANDARD") if kreiselart == "PIN": counters["anzahl_kreisel_pinband"] += 1 counters["sep_kreisel_pin"] += n_sep else: counters["sep_kreisel_ohne_pin"] += n_sep - abstand_mm = float(attribs.get("ABSTAND", "2300") or "2300") + abstand_mm = safe_float(attribs.get("ABSTAND", "2300") or "2300", 2300.0) counters["laengen_kreisel"] += abstand_mm / 1000.0 counters["anzahl_separatoren"] += n_sep - counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")) or "0") + counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")) or "0") if counters["anzahl_streckengruppen"] == 0: counters["anzahl_streckengruppen"] = 1 continue @@ -406,18 +387,9 @@ def process_blocks(blocks, lookup): attribs = block.get("attribs", {}) counters["anzahl_variofoerderer"] += 1 vario_list.append(block) - try: - counters["laengen_variofoerderer"] += float(attribs.get("L_VF_m", "0") or "0") - except (ValueError, TypeError): - pass - try: - counters["anzahl_separatoren"] += int(attribs.get("ANZAHL_SEPARATOR", "0") or "0") - except (ValueError, TypeError): - pass - try: - counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", "0") or "0") - except (ValueError, TypeError): - pass + counters["laengen_variofoerderer"] += safe_float(attribs.get("L_VF_m", "0") or "0") + counters["anzahl_separatoren"] += safe_int(attribs.get("ANZAHL_SEPARATOR", "0") or "0") + counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", "0") or "0") if counters["anzahl_streckengruppen"] == 0: counters["anzahl_streckengruppen"] = 1 continue @@ -428,18 +400,9 @@ def process_blocks(blocks, lookup): attribs = block.get("attribs", {}) counters["anzahl_gefaellestrecken"] += 1 gefaelle_list.append(block) - try: - counters["laengen_gefaellestrecken"] += float(attribs.get("L_GF_m", "0") or "0") - except (ValueError, TypeError): - pass - try: - counters["anzahl_separatoren"] += int(attribs.get("ANZAHL_SEPARATOR", "0") or "0") - except (ValueError, TypeError): - pass - try: - counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", "0") or "0") - except (ValueError, TypeError): - pass + counters["laengen_gefaellestrecken"] += safe_float(attribs.get("L_GF_m", "0") or "0") + counters["anzahl_separatoren"] += safe_int(attribs.get("ANZAHL_SEPARATOR", "0") or "0") + counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", "0") or "0") if counters["anzahl_streckengruppen"] == 0: counters["anzahl_streckengruppen"] = 1 continue