""" Common utility functions for the cable routing system. This module contains frequently used helper functions for: - File and path operations - Environment variable handling - JSON operations - DXF file handling - Dictionary operations """ import json import os import sys from pathlib import Path from typing import Any, Tuple import ezdxf from ezdxf.lldxf.const import DXFStructureError # ============================================================================ # File Operations # ============================================================================ def check_file_in_work(work_dir, filename) -> Tuple[Path, bool]: """ Check if a file exists, either at the given path or in the work directory. Args: work_dir: Working directory to check if file not found at filename path (str or Path) filename: Path to the file to check (str or Path) Returns: Tuple of (resolved_path, exists_flag) - resolved_path: Path where file was found (or would be) - exists_flag: True if file exists, False otherwise """ work_dir = Path(work_dir) filename = Path(filename) fexists = True if not filename.exists(): mypath = work_dir.joinpath(filename) if not mypath.exists(): fexists = False else: mypath = filename return mypath, fexists # ============================================================================ # Environment Variable Operations # ============================================================================ def check_environment_var(env_str: str) -> Path: """ Get and validate an environment variable as a Path. Args: env_str: Name of the environment variable Returns: Path object from the environment variable Exits: Exits the program if the environment variable is not set or empty """ 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.") exit() # ============================================================================ # JSON Operations # ============================================================================ def to_json(d: Any, pretty: bool = True) -> str: """ Convert a Python object to a JSON string. Args: d: Object to convert to JSON pretty: If True, format with indentation for readability Returns: JSON string representation of the object Note: Uses ensure_ascii=False to properly display German umlauts (ä, ö, ü) """ return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str) def load_json(jsonfilename: str) -> dict: """ Load JSON data from a file. Args: jsonfilename: Path to the JSON file Returns: Parsed JSON data as a dictionary """ with open(jsonfilename, encoding='utf-8') as fh: return json.load(fh) def write_results(jsn_results: str, out_dir: Path, filename: str) -> None: """ Write JSON results to a file. Args: jsn_results: JSON string to write out_dir: Output directory path filename: Name of the output file """ print("writing results to a json file ...") outfile = Path(out_dir) / filename with open(outfile, 'w', encoding='utf-8') as fh: fh.write(jsn_results) print("done") # ============================================================================ # DXF File Operations # ============================================================================ def dxf_is_binary(dxf_path: Path) -> bool: """ Check if a DXF file is in binary format. Args: dxf_path: Path to the DXF file Returns: True if the file is a binary DXF, False otherwise """ with open(dxf_path, 'rb') as f: header = f.read(22) return b'AutoCAD Binary DXF' in header def get_dxf_file(filepath: Path): """ Load a DXF file using ezdxf. Args: filepath: Path to the DXF file Returns: ezdxf Drawing object Exits: Exits with code 1 for I/O errors Exits with code 2 for invalid/corrupted DXF files """ try: print("reading file ..", end='') doc = ezdxf.filemanagement.readfile(filepath) print("done") except IOError: print("Not a DXF file or a generic I/O error.") sys.exit(1) except DXFStructureError: print("Invalid or corrupted DXF file.") sys.exit(2) return doc # ============================================================================ # Dictionary Operations # ============================================================================ def merge_two_dicts(x: dict, y: dict) -> dict: """ Merge two dictionaries, with values from y overwriting those in x. Args: x: First dictionary (base) y: Second dictionary (override values) Returns: New dictionary with merged values """ z = x.copy() z.update(y) return z