refactor: create_numbers ohne Kopie von error_collector

Fehler/Warnungen als zwei einfache Listen (neue_meldungen,
meldungen_als_dict) statt der aus kabellaengen kopierten
ErrorCollector-Klasse; lib/error_collector.py entfernt. Format der
Fehlerdatei unveraendert, an sechs Testzeichnungen gegen das Original
verglichen (identische Nummern und Fehlerdateien).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Michael StanglandClaude Opus 5.5 committed 2026-11-03 17:00:42 +01:00
1 parent 8abb6b3c65
commit 4100cbfb97
4 files changed
+97 -184

No files matched your search

+88 -59
View File
@@ -1,4 +1,5 @@
import argparse
import json
import os
import sys
from pathlib import Path
@@ -6,7 +7,35 @@ from pathlib import Path
import ezdxf
from ezdxf.lldxf.const import DXFStructureError
from error_collector import ErrorCollector, write_json_file
# ============================================================================
# Meldungen: zwei einfache Listen von (art, text) - Fehler und Warnungen.
# Ersetzt die Fehlersammler-Klasse ErrorCollector aus kabellaengen (lib/error_collector.py),
# damit keine Kopie davon in diesem Repo liegt.
# ============================================================================
def neue_meldungen():
"""Leere Meldungssammlung: {"errors": [(art, text), ...], "warnings": [...]}."""
return {"errors": [], "warnings": []}
def meldungen_als_dict(meldungen):
"""Fuer die Fehlerdatei nach Art gruppiert, Format wie bisher:
{"errors": {art: [text, ...]}, "warnings": {...}} - leere Teile entfallen."""
erg = {}
for teil in ("errors", "warnings"):
gruppen = {}
for art, text in meldungen[teil]:
gruppen.setdefault(art, []).append(text)
if gruppen:
erg[teil] = gruppen
return erg
def write_json_file(data, out_dir, filename):
"""data als JSON (UTF-8, eingerueckt) nach out_dir/filename schreiben."""
with open(Path(out_dir) / filename, 'w', encoding='utf-8') as fh:
json.dump(data, fh, indent=2, ensure_ascii=False, default=str)
# ============================================================================
@@ -37,7 +66,7 @@ def get_dxf_file(filepath: Path):
return doc
def extract_block_attributes(doc, insert, error_collector=None) -> dict:
def extract_block_attributes(doc, insert, meldungen=None) -> dict:
"""
Alle Attribute eines INSERT als {tag: text}. Unterstuetzt die zweistufige
Blockstruktur der Renamer-Bloecke (aeusserer Block mit LWPOLYLINE und einem
@@ -64,9 +93,9 @@ def extract_block_attributes(doc, insert, error_collector=None) -> dict:
attributes[attrib.dxf.tag] = attrib.dxf.text
break
except Exception as e:
if error_collector:
if meldungen:
error_msg = f"Fehler beim Extrahieren der Attribute aus Block '{insert.dxf.name}': {e}"
error_collector.add_warnings({"attribute_extraction_error": error_msg})
meldungen["warnings"].append(("attribute_extraction_error", error_msg))
return attributes
@@ -377,7 +406,7 @@ def point_near_polyline(point, polyline_points, max_distance=500.0):
return min_dist_sq <= (max_distance * max_distance)
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, error_collector=None, polyline_max_distance=500.0):
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, meldungen=None, polyline_max_distance=500.0):
"""
Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern.
@@ -411,7 +440,7 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, erro
# Prüfe ob der Block Attribute hat (direkt oder in zweistufiger Struktur)
# Verwende extract_block_attributes um beides zu unterstützen
symbol_attribs = extract_block_attributes(doc, entity, error_collector)
symbol_attribs = extract_block_attributes(doc, entity, meldungen)
if not symbol_attribs:
continue
@@ -758,7 +787,7 @@ def validate_direction_with_geometry(direction, is_rectangle):
return True, f"Unbekannte DIRECTION '{direction}' - Verarbeitung wird fortgesetzt"
def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
def collect_and_group_renamer_blocks(doc, msp, renamer_layers, meldungen):
"""
Erster Pass: Sammelt und gruppiert alle Renamer-Blöcke nach Konfiguration.
@@ -773,7 +802,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
# Finde alle INSERT-Blöcke auf diesem Layer
for insert in msp.query(f'INSERT[layer=="{layer}"]'):
# Extrahiere Attribute (unterstützt zweistufige Blockstruktur)
attributes = extract_block_attributes(doc, insert, error_collector)
attributes = extract_block_attributes(doc, insert, meldungen)
if not attributes:
print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}")
@@ -796,7 +825,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
if boundary is None:
error_msg = f"Keine Polylinie/Rechteck im Renamer-Block '{block_name}' an Position {insert.dxf.insert} gefunden"
print(f" FEHLER: {error_msg}")
error_collector.add_errors({"missing_boundary": error_msg})
meldungen["errors"].append(("missing_boundary", error_msg))
continue
print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})")
@@ -809,12 +838,12 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
if not is_valid:
error_msg = f"Renamer-Block '{block_name}' an Position {insert.dxf.insert}: {validation_error}"
print(f" FEHLER: {error_msg}")
error_collector.add_errors({"direction_geometry_mismatch": error_msg})
meldungen["errors"].append(("direction_geometry_mismatch", error_msg))
continue
elif validation_error:
# Warnung bei unbekannter DIRECTION, aber fortfahren
print(f" WARNUNG: {validation_error}")
error_collector.add_warnings({"unknown_direction": validation_error})
meldungen["warnings"].append(("unknown_direction", validation_error))
# Erstelle Konfigurationsschlüssel und gruppiere
config_key = get_renamer_config_key(attributes)
@@ -825,7 +854,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
return renamer_groups
def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
def find_template_symbols_in_geometry(doc, msp, group_blocks, meldungen):
"""
Findet alle Symbole mit '@' die geometrisch in den Renamer-Bereichen liegen,
unabhängig vom Layer.
@@ -834,7 +863,7 @@ def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
doc: DXF-Dokument
msp: Modelspace
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
error_collector: ErrorCollector für Fehlerbehandlung
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
Returns:
Liste von Dictionaries mit 'layer', 'position', 'io' für jedes gefundene Symbol
@@ -842,7 +871,7 @@ def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
symbols_in_geometry = []
for insert, boundary, attributes, is_rectangle in group_blocks:
for entity in msp.query('INSERT'):
symbol_attribs = extract_block_attributes(doc, entity, error_collector)
symbol_attribs = extract_block_attributes(doc, entity, meldungen)
if not symbol_attribs:
continue
@@ -862,7 +891,7 @@ def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
def create_no_symbols_found_error_message(block_name, direction, layer_names, group_blocks,
doc, msp, error_collector):
doc, msp, meldungen):
"""
Erstellt eine detaillierte Fehlermeldung wenn keine Symbole in einem Renamer-Bereich gefunden wurden.
@@ -873,7 +902,7 @@ def create_no_symbols_found_error_message(block_name, direction, layer_names, gr
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
doc: DXF-Dokument
msp: Modelspace
error_collector: ErrorCollector für Fehlerbehandlung
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
Returns:
Fehlermeldungs-String
@@ -882,7 +911,7 @@ def create_no_symbols_found_error_message(block_name, direction, layer_names, gr
for insert, _, _, _ in group_blocks]
# Prüfe ob es Symbole mit @ gibt, die geometrisch im Bereich liegen (egal auf welchem Layer)
symbols_in_geometry = find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector)
symbols_in_geometry = find_template_symbols_in_geometry(doc, msp, group_blocks, meldungen)
# Erstelle Fehlermeldung basierend auf gefundenen Symbolen
if symbols_in_geometry and not layer_names:
@@ -917,7 +946,7 @@ def create_no_symbols_found_error_message(block_name, direction, layer_names, gr
)
def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, error_collector, polyline_max_distance=500.0):
def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, meldungen, polyline_max_distance=500.0):
"""
Sammelt alle Symbole aus allen Boundaries einer Renamer-Gruppe.
Prüft auf Duplikate (Symbole in mehreren Bereichen).
@@ -928,7 +957,7 @@ def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_nam
renamer_layers: Layer auf denen nach Renamer-Blöcken gesucht wird
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
block_name: Name des Renamer-Blocks (für Fehlermeldungen)
error_collector: ErrorCollector für Fehlerbehandlung
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
Returns:
@@ -939,7 +968,7 @@ def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_nam
for insert, boundary, attributes, is_rectangle in group_blocks:
# Finde Symbole innerhalb dieses Bereichs
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, error_collector, polyline_max_distance)
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, meldungen, polyline_max_distance)
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
for symbol in symbols:
@@ -962,12 +991,12 @@ def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_nam
f"{', '.join([f'({p[0]:.1f}, {p[1]:.1f})' for p in existing_positions])}"
)
print(f" FEHLER: {error_msg}")
error_collector.add_errors({"symbol_in_multiple_boundaries": error_msg})
meldungen["errors"].append(("symbol_in_multiple_boundaries", error_msg))
return all_symbols
def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector, polyline_max_distance=500.0):
def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, meldungen, polyline_max_distance=500.0):
"""
Zweiter Pass: Verarbeitet jede Renamer-Block-Gruppe.
Sammelt Symbole, sortiert sie und nummeriert sie.
@@ -992,7 +1021,7 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
print(f"\n Verarbeite Gruppe: {block_name} ({len(group_blocks)} Renamer-Blöcke)")
# Sammle Symbole aus ALLEN Boundaries dieser Gruppe
all_symbols = collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, error_collector, polyline_max_distance)
all_symbols = collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, meldungen, polyline_max_distance)
print(f" Gesamt {len(all_symbols)} eindeutige Template-Symbole aus {len(group_blocks)} Blöcken")
@@ -1000,10 +1029,10 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
# Fehlermeldung: Keine Symbole gefunden
layer_names = get_layer_names_from_attributes(reference_attributes)
error_msg = create_no_symbols_found_error_message(
block_name, direction, layer_names, group_blocks, doc, msp, error_collector
block_name, direction, layer_names, group_blocks, doc, msp, meldungen
)
print(f" FEHLER: {error_msg}")
error_collector.add_errors({"no_symbols_found": error_msg})
meldungen["errors"].append(("no_symbols_found", error_msg))
continue
# Für POLYLINE_PATH: Hole die Polylinie vom ersten Block
@@ -1029,21 +1058,21 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
return all_renamed
def find_symbols_with_at_in_io(doc, msp, error_collector):
def find_symbols_with_at_in_io(doc, msp, meldungen):
"""
Findet alle Symbole mit "@" im IO-Attribut.
Args:
doc: DXF-Dokument
msp: Modelspace
error_collector: ErrorCollector für Fehlerbehandlung
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
Returns:
Liste von Dictionaries mit Symbol-Informationen
"""
symbols_with_at = []
for entity in msp.query('INSERT'):
symbol_attribs = extract_block_attributes(doc, entity, error_collector)
symbol_attribs = extract_block_attributes(doc, entity, meldungen)
if not symbol_attribs:
continue
@@ -1138,7 +1167,7 @@ def create_wrong_layer_warning(symbol, wrong_layer_info):
)
def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyline_max_distance=500.0):
def check_symbols_in_boundaries(doc, msp, renamer_groups, meldungen, polyline_max_distance=500.0):
"""
Dritter Pass: Prüft ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen.
@@ -1156,7 +1185,7 @@ def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyl
all_boundaries.append((boundary, attributes, insert.dxf.insert))
# Finde alle Symbole mit "@" im IO-Attribut
symbols_with_at = find_symbols_with_at_in_io(doc, msp, error_collector)
symbols_with_at = find_symbols_with_at_in_io(doc, msp, meldungen)
# Prüfe für jedes Symbol, ob es in mindestens einem Renamer-Bereich liegt
symbols_not_in_any_boundary = []
@@ -1180,7 +1209,7 @@ def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyl
# Warnung: Symbol liegt geometrisch im Bereich, aber Layer stimmt nicht
warning_msg = create_wrong_layer_warning(symbol, wrong_layer_info)
print(f" WARNUNG: {warning_msg}")
error_collector.add_warnings({"symbol_wrong_layer": warning_msg})
meldungen["warnings"].append(("symbol_wrong_layer", warning_msg))
# NICHT als Fehler hinzufügen - Warnung reicht aus
else:
# Fehler: Symbol liegt weder geometrisch noch vom Layer her in einem Renamer-Bereich
@@ -1195,7 +1224,7 @@ def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyl
f"enthält '@' im IO-Attribut, liegt aber in keinem Renamer-Bereich."
)
print(f" FEHLER: {error_msg}")
error_collector.add_errors({"symbol_with_at_not_in_boundary": error_msg})
meldungen["errors"].append(("symbol_with_at_not_in_boundary", error_msg))
else:
print(f" Alle {len(symbols_with_at)} Symbole mit '@' im IO liegen in einem Renamer-Bereich.")
@@ -1231,14 +1260,14 @@ def rectangles_overlap(boundary1, boundary2):
return not no_overlap
def check_overlapping_renamer_blocks(renamer_groups, error_collector):
def check_overlapping_renamer_blocks(renamer_groups, meldungen):
"""
Prüft ob sich Renamer-Rechtecke überlappen.
Polyline-Pfade (DIRECTION=POLYLINE_PATH) werden nicht geprüft.
Args:
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
error_collector: ErrorCollector für Fehlerbehandlung
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
"""
print("\n" + "="*60)
print("Prüfe auf überlappende Renamer-Bereiche")
@@ -1287,12 +1316,12 @@ def check_overlapping_renamer_blocks(renamer_groups, error_collector):
f"Symbole könnten mehrfach nummeriert werden."
)
print(f" {error_msg}")
error_collector.add_errors({"overlapping_renamer_blocks": error_msg})
meldungen["errors"].append(("overlapping_renamer_blocks", error_msg))
else:
print(f"\nOK: Keine Überlappungen zwischen {len(rectangle_renamers)} Renamer-Rechtecken gefunden")
def process_renamer_blocks(doc, msp, renamer_layers, error_collector, polyline_max_distance=500.0):
def process_renamer_blocks(doc, msp, renamer_layers, meldungen, polyline_max_distance=500.0):
"""
Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern.
Gruppiert Blöcke mit identischer Konfiguration und nummeriert alle Symbole
@@ -1307,21 +1336,21 @@ def process_renamer_blocks(doc, msp, renamer_layers, error_collector, polyline_m
- renamer_groups: Dictionary der gruppierten Renamer-Blöcke
"""
# Erster Pass: Sammle und gruppiere alle Renamer-Blöcke
renamer_groups = collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector)
renamer_groups = collect_and_group_renamer_blocks(doc, msp, renamer_layers, meldungen)
# Prüfe auf überlappende Renamer-Bereiche
check_overlapping_renamer_blocks(renamer_groups, error_collector)
check_overlapping_renamer_blocks(renamer_groups, meldungen)
# Zweiter Pass: Verarbeite jede Gruppe
all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector, polyline_max_distance)
all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, meldungen, polyline_max_distance)
# Dritter Pass: Prüfe ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen
check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyline_max_distance)
check_symbols_in_boundaries(doc, msp, renamer_groups, meldungen, polyline_max_distance)
return all_renamed, renamer_groups
def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector, polyline_max_distance=500.0):
def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, meldungen, polyline_max_distance=500.0):
"""
Sammelt alle Symbole aus allen Renamer-Gruppen für Visualisierungszwecke.
@@ -1330,7 +1359,7 @@ def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, er
msp: Modelspace
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
renamer_layers: Liste der Layer mit Renamer-Blöcken
error_collector: ErrorCollector für Fehlerbehandlung
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
Returns:
@@ -1347,7 +1376,7 @@ def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, er
for insert, boundary, attributes, is_rectangle in group_blocks:
# Finde Symbole innerhalb dieses Bereichs
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, error_collector, polyline_max_distance)
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, meldungen, polyline_max_distance)
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
for symbol in symbols:
@@ -1371,12 +1400,12 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
args: Argparse-Argumente
Returns:
Tuple (success, error_collector):
Tuple (success, meldungen):
- success: True wenn erfolgreich verarbeitet
- error_collector: ErrorCollector mit gesammelten Fehlern/Warnungen
- meldungen: Meldungen (neue_meldungen) mit gesammelten Fehlern/Warnungen
"""
# Initialisiere ErrorCollector für diese Datei
error_collector = ErrorCollector()
# Meldungen fuer diese Datei
meldungen = neue_meldungen()
print(f"\n{'='*80}")
print(f"Verarbeite Datei: {dxf_path.name}")
@@ -1397,7 +1426,7 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
print("Starte Verarbeitung der Renamer-Blöcke")
print("="*60 + "\n")
renamed_symbols, renamer_groups = process_renamer_blocks(doc, msp, renamer_layers, error_collector, polyline_max_distance)
renamed_symbols, renamer_groups = process_renamer_blocks(doc, msp, renamer_layers, meldungen, polyline_max_distance)
print("\n" + "="*60)
print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert")
@@ -1411,7 +1440,7 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
# Zeichne Symbol-Rahmen wenn gewünscht
if args.show_symbol_frames:
all_symbols = collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector, polyline_max_distance)
all_symbols = collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, meldungen, polyline_max_distance)
if all_symbols:
draw_symbol_frames(doc, msp, all_symbols)
else:
@@ -1467,9 +1496,9 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
print(f"Ergebnisse in JSON gespeichert: {output_dir / output_filename}")
# Schreibe Fehler-Datei nur wenn Fehler oder Warnungen vorhanden sind
if error_collector.has_errors_or_warnings():
if (meldungen["errors"] or meldungen["warnings"]):
if args.errorfile:
error_data = error_collector.get_all_issues()
error_data = meldungen_als_dict(meldungen)
error_path = Path(args.errorfile)
# Wenn nur ein Dateiname ohne Pfad angegeben wurde, speichere im work-Ordner
@@ -1486,19 +1515,19 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
else:
# Keine explizite Fehlerdatei angegeben, generiere eine basierend auf Input-Datei
error_filename = f"{dxf_path.stem}_errors.json"
error_data = error_collector.get_all_issues()
error_data = meldungen_als_dict(meldungen)
write_json_file(error_data, dxf_path.parent, error_filename)
print(f"Fehler/Warnungen gespeichert: {dxf_path.parent / error_filename}")
# Status-Meldung
if error_collector.has_errors():
if meldungen["errors"]:
print("\n!!! Es sind Fehler aufgetreten !!!")
return False, error_collector
elif error_collector.has_errors_or_warnings():
return False, meldungen
elif (meldungen["errors"] or meldungen["warnings"]):
print("\n(Warnungen vorhanden, aber keine kritischen Fehler)")
return True, error_collector
return True, meldungen
else:
return True, error_collector
return True, meldungen
def main(argv=None):
@@ -1552,9 +1581,9 @@ def main(argv=None):
print(f"Datei nicht gefunden: {dxf_path}")
return 1
_success, error_collector = process_single_file(dxf_path, dxf_path.parent, renamer_layers,
_success, meldungen = process_single_file(dxf_path, dxf_path.parent, renamer_layers,
polyline_max_distance, args)
return 1 if error_collector.has_errors() else 0
return 1 if meldungen["errors"] else 0
if __name__ == '__main__':
-121
View File
@@ -1,121 +0,0 @@
"""
ErrorCollector - Gemeinsame Klasse zum Sammeln von Fehlern und Warnungen
Diese Klasse wird von getpositions.py und drawdxf.py verwendet, um Fehler
und Warnungen während der Verarbeitung zu sammeln und zu verwalten.
"""
import json
from pathlib import Path
class ErrorCollector:
"""Sammelt alle Fehler und Warnungen während der Verarbeitung."""
def __init__(self):
self.errors = {}
self.warnings = {}
def add_errors(self, error_dict):
"""Fügt Fehler aus einem Dictionary hinzu."""
for key, value in error_dict.items():
if key in self.errors:
# Key existiert bereits - Verhalten hängt vom Typ ab
existing = self.errors[key]
if isinstance(existing, dict) and isinstance(value, dict):
# Beide sind Dictionaries → Merge/Update
existing.update(value)
elif isinstance(existing, list):
# Existierender Wert ist Liste
if isinstance(value, list):
existing.extend(value)
else:
existing.append(value)
else:
# Existierender Wert ist weder dict noch list → Mache Liste daraus
if isinstance(value, list):
self.errors[key] = [existing] + value
else:
self.errors[key] = [existing, value]
else:
# Neuer Key - behalte Typ des Wertes
if isinstance(value, (dict, list)):
# Dictionary oder Liste → direkt speichern
self.errors[key] = value
else:
# Primitiver Wert (String, etc.) → als Liste speichern
self.errors[key] = [value]
def add_warnings(self, warning_dict):
"""Fügt Warnungen aus einem Dictionary hinzu."""
for key, value in warning_dict.items():
if key in self.warnings:
# Key existiert bereits - Verhalten hängt vom Typ ab
existing = self.warnings[key]
if isinstance(existing, dict) and isinstance(value, dict):
# Beide sind Dictionaries → Merge/Update
existing.update(value)
elif isinstance(existing, list):
# Existierender Wert ist Liste
if isinstance(value, list):
existing.extend(value)
else:
existing.append(value)
else:
# Existierender Wert ist weder dict noch list → Mache Liste daraus
if isinstance(value, list):
self.warnings[key] = [existing] + value
else:
self.warnings[key] = [existing, value]
else:
# Neuer Key - behalte Typ des Wertes
if isinstance(value, (dict, list)):
# Dictionary oder Liste → direkt speichern
self.warnings[key] = value
else:
# Primitiver Wert (String, etc.) → als Liste speichern
self.warnings[key] = [value]
def has_errors_or_warnings(self):
"""Prüft ob Fehler oder Warnungen vorhanden sind."""
return bool(self.errors) or bool(self.warnings)
def has_errors(self):
"""Prüft ob Fehler vorhanden sind."""
return bool(self.errors)
def get_all_issues(self):
"""Gibt alle Fehler und Warnungen als Dictionary zurück."""
result = {}
if self.errors:
result["errors"] = self.errors
if self.warnings:
result["warnings"] = self.warnings
return result
def write_errorfile(self, work_dir: Path, filename: str):
"""Schreibt Fehlerdatei nur wenn Fehler oder Warnungen vorhanden sind."""
if self.has_errors():
print("Fehler gefunden. Schreibe Error-Datei.")
all_issues = self.get_all_issues()
write_json_file(all_issues, work_dir, filename)
else:
print("Keine Fehler gefunden. Keine Error-Datei geschrieben.")
def write_json_file(data: dict, out_dir: Path, filename: str) -> None:
"""
Schreibt Daten in eine JSON-Datei.
Args:
data: Dictionary mit zu schreibenden Daten
out_dir: Ausgabeverzeichnis
filename: Dateiname für die JSON-Datei
"""
print("writing data to a json file ...")
outfile = out_dir / filename if isinstance(out_dir, Path) else Path(out_dir) / filename
with open(outfile, 'w', encoding='utf-8') as fh:
json.dump(data, fh, indent=2, ensure_ascii=False, default=str)
print("done")