Files
kabellaengen/lib/translate.py
T
2025-12-02 22:56:45 +01:00

299 lines
9.5 KiB
Python

import argparse
import os
import re
import sys
from pathlib import Path
from configparser import RawConfigParser
from fnmatch import fnmatch
import ezdxf
from openpyxl import Workbook
"""
Dieses Programm:
- liest eine DXF Datei und sammelt alle TEXT und MTEXT Objekte
- schreibt diese in die erste Spalte einer Excel-Datei
- speichert die Excel-Datei im PROJECT_WORK Verzeichnis
"""
def check_environment_var(env_str: str) -> Path:
"""Prüft ob eine Umgebungsvariable gesetzt ist und gibt den Pfad zurück."""
out_path = os.environ.get(env_str)
if out_path:
return Path(out_path)
else:
print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
sys.exit(1)
def load_ignore_patterns(cfg_path: Path) -> tuple[list[str], list[str]]:
"""
Liest die ignore_pattern aus der translator.cfg Datei.
Unterstützt sowohl Wildcard-Muster (*,?) als auch reguläre Ausdrücke.
Args:
cfg_path: Pfad zum Konfigurationsverzeichnis (PROJECT_CFG)
Returns:
Tuple mit (Wildcard-Mustern, Regex-Mustern)
"""
config_file = cfg_path / "translator.cfg"
if not config_file.exists():
print(f"FEHLER: Konfigurationsdatei nicht gefunden: {config_file}")
print(f"Bitte erstellen Sie die Datei 'translator.cfg' im Verzeichnis: {cfg_path}")
sys.exit(1)
# RawConfigParser verhindert dass Backslashes in Regex-Mustern escaped werden
config = RawConfigParser()
try:
config.read(config_file, encoding='utf-8')
except Exception as e:
print(f"Fehler beim Lesen der Konfigurationsdatei: {e}")
sys.exit(1)
wildcard_patterns = []
regex_patterns = []
# Lade Wildcard-Muster
if config.has_section('ignore_pattern_wildcard'):
for key in config['ignore_pattern_wildcard']:
pattern = config['ignore_pattern_wildcard'][key].strip()
if pattern and not pattern.startswith('#'):
wildcard_patterns.append(pattern)
# Lade Regex-Muster
if config.has_section('ignore_pattern_regex'):
for key in config['ignore_pattern_regex']:
pattern = config['ignore_pattern_regex'][key].strip()
if pattern and not pattern.startswith('#'):
regex_patterns.append(pattern)
print(f"Geladene Wildcard-Muster: {len(wildcard_patterns)}")
for pattern in wildcard_patterns:
print(f" - {pattern}")
print(f"Geladene Regex-Muster: {len(regex_patterns)}")
for pattern in regex_patterns:
print(f" - {pattern}")
return wildcard_patterns, regex_patterns
def should_ignore_text(text: str, wildcard_patterns: list[str], regex_patterns: list[str], debug: bool = False) -> bool:
"""
Prüft ob ein Text ignoriert werden soll basierend auf den Mustern.
Args:
text: Zu prüfender Text
wildcard_patterns: Liste der Wildcard-Muster (*,?)
regex_patterns: Liste der regulären Ausdrücke
debug: Wenn True, gibt Debug-Informationen aus
Returns:
True wenn Text ignoriert werden soll, False sonst
"""
# Prüfe Wildcard-Muster
for pattern in wildcard_patterns:
if fnmatch(text, pattern):
if debug:
print(f" DEBUG: '{text}' matched wildcard '{pattern}'")
return True
# Prüfe Regex-Muster
for pattern in regex_patterns:
try:
if re.search(pattern, text):
if debug:
print(f" DEBUG: '{text}' matched regex '{pattern}'")
return True
except re.error:
# Falls ungültiger Regex, ignoriere dieses Muster
print(f"WARNUNG: Ungültiger regulärer Ausdruck: {pattern}")
continue
return False
def extract_text_from_dxf(filename: Path, wildcard_patterns: list[str], regex_patterns: list[str], debug: bool = False) -> list[str]:
"""
Extrahiert alle TEXT und MTEXT Objekte aus einer DXF Datei.
Verwendet ein Set, um Duplikate zu vermeiden.
Args:
filename: Pfad zur DXF Datei
wildcard_patterns: Liste der Wildcard-Muster zum Filtern
regex_patterns: Liste der Regex-Muster zum Filtern
debug: Wenn True, gibt Debug-Informationen aus
Returns:
Alphabetisch sortierte Liste mit allen gefundenen Texten (nach Filterung, ohne Duplikate)
"""
texts_set = set() # Set für eindeutige Texte
ignored_count = 0
total_count = 0
try:
doc = ezdxf.readfile(filename)
msp = doc.modelspace()
# Sammle alle TEXT Objekte
for entity in msp.query('TEXT'):
if entity.dxf.text:
total_count += 1
text = entity.dxf.text.strip() # Entferne führende/nachfolgende Leerzeichen
# Ignoriere leere Texte
if not text:
ignored_count += 1
continue
if should_ignore_text(text, wildcard_patterns, regex_patterns, debug):
ignored_count += 1
else:
texts_set.add(text)
# Sammle alle MTEXT Objekte
for entity in msp.query('MTEXT'):
if entity.text:
total_count += 1
text = entity.text.strip() # Entferne führende/nachfolgende Leerzeichen
# Ignoriere leere Texte
if not text:
ignored_count += 1
continue
if should_ignore_text(text, wildcard_patterns, regex_patterns, debug):
ignored_count += 1
else:
texts_set.add(text)
# Konvertiere Set zu sortierter Liste
texts = sorted(texts_set)
print(f"Anzahl gefundener TEXT/MTEXT Objekte: {total_count}")
print(f"Ignorierte Texte (nach Filter): {ignored_count}")
print(f"Duplikate entfernt: {total_count - ignored_count - len(texts)}")
print(f"Eindeutige Texte für Export: {len(texts)}")
except Exception as e:
print(f"Fehler beim Lesen der DXF Datei: {e}")
sys.exit(1)
return texts
def write_texts_to_excel(texts: list[str], output_path: Path):
"""
Schreibt die Texte in die erste Spalte einer Excel-Datei.
Args:
texts: Liste der zu schreibenden Texte
output_path: Pfad zur Ausgabe-Excel-Datei
"""
wb = Workbook()
ws = wb.active
ws.title = "Texte"
# Kopfzeile
ws['A1'] = "Text"
# Texte in die erste Spalte schreiben
for idx, text in enumerate(texts, start=2):
ws[f'A{idx}'] = text
# Spaltenbreite anpassen
ws.column_dimensions['A'].width = 50
try:
wb.save(output_path)
print(f"Excel-Datei erfolgreich erstellt: {output_path}")
except Exception as e:
print(f"Fehler beim Schreiben der Excel-Datei: {e}")
sys.exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Extrahiert TEXT und MTEXT Objekte aus einer DXF Datei und schreibt sie in eine Excel-Datei',
prog='translate'
)
parser.add_argument(
'-f', '--filename',
action='store',
required=True,
help='DXF Datei die verarbeitet werden soll',
metavar='myfile.dxf'
)
parser.add_argument(
'-e', '--extract',
action='store_true',
required=True,
help='Hole alle Texte aus der .dxf in eine Excel Datei'
)
parser.add_argument(
'-o', '--outname',
action='store',
help='Name der Ausgabedatei (ohne Pfad, mit oder ohne .xlsx Endung)',
metavar='output.xlsx'
)
parser.add_argument(
'-d', '--debug',
action='store_true',
help='Aktiviert Debug-Ausgabe für Pattern-Matching'
)
args = parser.parse_args()
# Prüfe Umgebungsvariablen
work_dir = check_environment_var('PROJECT_WORK')
cfg_dir = check_environment_var('PROJECT_CFG')
# Lade Ignore-Muster aus Konfigurationsdatei
wildcard_patterns, regex_patterns = load_ignore_patterns(cfg_dir)
# Prüfe ob Datei eine DXF Datei ist
input_filename = args.filename
if not input_filename.lower().endswith(('.dxf', '.dwg')):
print("Nur verfügbar für .dxf oder .dwg Dateien")
sys.exit(1)
# Versuche zuerst Datei im Work-Ordner zu finden
filename = work_dir / input_filename
if not filename.exists():
# Falls nicht gefunden, prüfe ob es ein vollständiger Pfad ist
filename = Path(input_filename)
if not filename.exists():
print(f"Datei nicht gefunden: {input_filename}")
print(f" Geprüft in Work-Ordner: {work_dir / input_filename}")
print(f" Geprüft als vollständiger Pfad: {filename}")
sys.exit(1)
# Extrahiere Texte aus DXF
if args.extract:
print(f"Lese DXF Datei: {filename}")
if args.debug:
print("DEBUG-Modus aktiviert")
texts = extract_text_from_dxf(filename, wildcard_patterns, regex_patterns, args.debug)
# Erstelle Output-Dateinamen
if args.outname:
# Verwende benutzerdefinierten Namen
output_filename = args.outname
# Füge .xlsx Endung hinzu falls nicht vorhanden
if not output_filename.endswith('.xlsx'):
output_filename += '.xlsx'
else:
# Standardname basierend auf Eingabedatei
output_filename = filename.stem + "_texts.xlsx"
output_path = work_dir / output_filename
# Schreibe Texte in Excel
write_texts_to_excel(texts, output_path)
print("Verarbeitung erfolgreich abgeschlossen.")