Files
kabellaengen/lib/doc/UTILS_API.md
T

15 KiB

utils.py API Documentation

Comprehensive documentation for the utils.py module containing helper functions for file operations, DXF manipulation, and data processing.

Table of Contents

  1. File Operations
  2. Environment Variables
  3. JSON Operations
  4. DXF File Operations
  5. ezdxf Operations (High-Level DXF Helpers)
  6. Dictionary Operations

File Operations

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 (str | Path): Working directory to check if file not found at filename path
  • filename (str | Path): Path to the file to check

Returns:

  • Tuple[Path, bool]: (resolved_path, exists_flag)
    • resolved_path: Path where file was found (or would be)
    • exists_flag: True if file exists, False otherwise

Example:

from utils import check_file_in_work
work_dir = Path("c:/work")
filepath, exists = check_file_in_work(work_dir, "test.dxf")
if exists:
    print(f"File found at: {filepath}")

Environment Variables

check_environment_var(env_str: str) → Path

Get and validate an environment variable as a Path.

Args:

  • env_str (str): Name of the environment variable

Returns:

  • Path: Path object from the environment variable

Exits:

  • Exits the program if the environment variable is not set or empty

Example:

from utils import check_environment_var
work_dir = check_environment_var('PROJECT_WORK')
config_dir = check_environment_var('PROJECT_CFG')

JSON Operations

to_json(d: Any, pretty: bool = True) → str

Convert a Python object to a JSON string.

Args:

  • d (Any): Object to convert to JSON
  • pretty (bool): If True, format with indentation for readability (default: True)

Returns:

  • str: JSON string representation of the object

Note: Uses ensure_ascii=False to properly display German umlauts (ä, ö, ü)

Example:

from utils import to_json
data = {"name": "Test", "wert": 42}
json_str = to_json(data)
print(json_str)

load_json(jsonfilename: str) → dict

Load JSON data from a file.

Args:

  • jsonfilename (str): Path to the JSON file

Returns:

  • dict: Parsed JSON data as a dictionary

Example:

from utils import load_json
config = load_json("config.json")

write_results(jsn_results: str, out_dir: Path, filename: str) → None

Write JSON results to a file.

Args:

  • jsn_results (str): JSON string to write
  • out_dir (Path): Output directory path
  • filename (str): Name of the output file

Example:

from utils import write_results, to_json
from pathlib import Path

data = {"results": [1, 2, 3]}
write_results(to_json(data), Path("output"), "results.json")

DXF File Operations

dxf_is_binary(dxf_path: Path) → bool

Check if a DXF file is in binary format.

Args:

  • dxf_path (Path): Path to the DXF file

Returns:

  • bool: True if the file is a binary DXF, False otherwise

Example:

from utils import dxf_is_binary
from pathlib import Path

if dxf_is_binary(Path("drawing.dxf")):
    print("Binary DXF file detected")
else:
    print("ASCII DXF file")

get_dxf_file(filepath: Path) → ezdxf.Drawing

Load a DXF file using ezdxf.

Args:

  • filepath (Path): Path to the DXF file

Returns:

  • ezdxf.Drawing: ezdxf Drawing object

Exits:

  • Exit code 1: I/O errors
  • Exit code 2: Invalid/corrupted DXF files

Example:

from utils import get_dxf_file
from pathlib import Path

doc = get_dxf_file(Path("drawing.dxf"))
msp = doc.modelspace()

ezdxf Operations

High-level helper functions for working with ezdxf objects.

Attribute Extraction

extract_insert_attributes(insert, error_collector=None) → dict

Extrahiert alle Attribute aus einem INSERT-Block. Unterstützt zweistufige Blockstruktur.

Args:

  • insert: INSERT-Entity des Blocks
  • error_collector (optional): ErrorCollector für Warnungen

Returns:

  • dict: Dictionary mit allen gefundenen Attributen {tag: text}

Note:

  • Funktioniert ohne explizites doc-Objekt wenn insert.doc verfügbar
  • Für neue Code: Verwenden Sie extract_insert_attributes_with_doc()

Example:

from utils import extract_insert_attributes

for entity in msp.query('INSERT'):
    attributes = extract_insert_attributes(entity)
    if 'IO' in attributes:
        print(f"IO: {attributes['IO']}")

extract_insert_attributes_with_doc(doc, insert, error_collector=None) → dict

Extrahiert alle Attribute aus einem INSERT-Block mit explizitem doc-Parameter. Empfohlen für neuen Code - bessere Unterstützung für zweistufige Blockstrukturen.

Args:

  • doc: DXF-Dokument
  • insert: INSERT-Entity des Blocks
  • error_collector (optional): ErrorCollector für Warnungen

Returns:

  • dict: Dictionary mit allen gefundenen Attributen {tag: text}

Example:

from utils import extract_insert_attributes_with_doc

doc = get_dxf_file(Path("drawing.dxf"))
msp = doc.modelspace()

for entity in msp.query('INSERT'):
    attributes = extract_insert_attributes_with_doc(doc, entity)
    print(f"Block: {entity.dxf.name}, Attributes: {attributes}")

extract_attributes_with_positions(insert_iterable, round_decimals=1) → Tuple[list, list]

Wandelt eine Iterable von INSERT-Objekten in zwei Listen um. Kompatibilität mit getpositions.py attribs_to_dicts().

Args:

  • insert_iterable: Iterable von INSERT-Entities (z.B. msp oder iterdxf.modelspace())
  • round_decimals (int): Anzahl Nachkommastellen für Positions-Rundung (default: 1)

Returns:

  • Tuple[list, list]: (all_inserts, all_positions)
    • all_inserts: Liste von Dicts mit Attribut-Tags und deren Textwerten
    • all_positions: Liste von Dicts mit Attribut-Tags und deren (x, y, z)-Positionen

Note:

  • Blöcke ohne Attribute werden übersprungen
  • Ersetzt die alte attribs_to_dicts() aus getpositions.py

Example:

from utils import extract_attributes_with_positions

all_inserts, all_positions = extract_attributes_with_positions(msp)
for insert_data, positions in zip(all_inserts, all_positions):
    print(f"Attributes: {insert_data}")
    print(f"Positions: {positions}")

Layer Operations

ensure_layer_exists(doc, layer_name, color=None, linetype=None, lineweight=None, description=None) → Layer

Stellt sicher, dass ein Layer existiert und erstellt ihn falls nötig.

Args:

  • doc: DXF-Dokument
  • layer_name (str): Name des Layers
  • color (int, optional): ACI Color Index (1-255)
  • linetype (str, optional): Name des Linientyps (z.B. 'CONTINUOUS', 'DASHED')
  • lineweight (int, optional): Linienbreite in mm*100 (z.B. 25 für 0.25mm)
  • description (str, optional): Layer-Beschreibung

Returns:

  • Layer: Layer-Objekt (existierend oder neu erstellt)

Example:

from utils import ensure_layer_exists

# Einfacher Layer
ensure_layer_exists(doc, 'ILS_MOTOR')

# Layer mit Farbe und Beschreibung
ensure_layer_exists(doc, 'ILS_CABLE', color=5, description='Cable routing layer')

# Layer mit allen Optionen
ensure_layer_exists(doc, 'ILS_SPECIAL',
                   color=3, linetype='DASHED',
                   lineweight=25, description='Special equipment')

create_layers_from_config(doc, layer_definitions) → None

Erstellt mehrere Layer aus einer Konfiguration.

Args:

  • doc: DXF-Dokument
  • layer_definitions (dict): Dictionary mit Layer-Definitionen

Format:

{
    'layer_name': {
        'color': int (optional),
        'linetype': str (optional),
        'lineweight': int (optional),
        'description': str (optional)
    },
    ...
}

Example:

from utils import create_layers_from_config

layer_config = {
    'ILS_MOTOR': {'color': 1, 'description': 'Motor layer'},
    'ILS_SENSOR': {'color': 3, 'lineweight': 25},
    'ILS_CABLE': {'color': 5, 'linetype': 'DASHED'}
}
create_layers_from_config(doc, layer_config)

Geometry Creation

add_rectangle_lwpolyline(msp, x, y, width, height, layer='0', color=None, rgb=None, closed=True) → LWPolyline

Fügt eine rechteckige LWPOLYLINE hinzu.

Args:

  • msp: Modelspace des DXF-Dokuments
  • x (float): X-Koordinate der unteren linken Ecke
  • y (float): Y-Koordinate der unteren linken Ecke
  • width (float): Breite des Rechtecks
  • height (float): Höhe des Rechtecks
  • layer (str): Layer-Name (default: '0')
  • color (int, optional): ACI Color Index (1-255)
  • rgb (tuple, optional): RGB-Tuple (r, g, b) mit Werten 0-255
  • closed (bool): Ob die Polylinie geschlossen sein soll (default: True)

Returns:

  • LWPolyline: LWPOLYLINE-Entity

Example:

from utils import add_rectangle_lwpolyline

# Einfaches Rechteck
add_rectangle_lwpolyline(msp, 0, 0, 100, 50, layer='ILS_FRAME')

# Rechteck mit Farbe
add_rectangle_lwpolyline(msp, 10, 10, 200, 100, color=5)

# Rechteck mit RGB-Farbe
add_rectangle_lwpolyline(msp, 20, 20, 150, 75, rgb=(255, 0, 0))

add_text_with_alignment(msp, text, x, y, height=50, layer='0', color=None, halign=0, valign=0, rotation=0.0, style='Standard') → Text

Fügt einen Text mit Ausrichtung hinzu.

Args:

  • msp: Modelspace des DXF-Dokuments
  • text (str): Textinhalt
  • x (float): X-Koordinate
  • y (float): Y-Koordinate
  • height (float): Texthöhe (default: 50)
  • layer (str): Layer-Name (default: '0')
  • color (int, optional): ACI Color Index (1-255)
  • halign (int): Horizontale Ausrichtung (0=links, 1=mitte, 2=rechts)
  • valign (int): Vertikale Ausrichtung (0=basis, 1=unten, 2=mitte, 3=oben)
  • rotation (float): Rotation in Grad (default: 0.0)
  • style (str): Textstil-Name (default: 'Standard')

Returns:

  • Text: TEXT-Entity

Example:

from utils import add_text_with_alignment

# Einfacher Text links unten
add_text_with_alignment(msp, "Test", 100, 100, layer='ILS_TEXT')

# Zentrierter Text
add_text_with_alignment(msp, "Centered", 200, 200, halign=1, valign=2)

# Rotierter Text mit Farbe
add_text_with_alignment(msp, "Rotated", 300, 300,
                       color=3, rotation=45.0, height=75)

Block Operations

add_blockref_with_attributes(msp, block_name, insert_point, attributes=None, layer='0', scale=1.0, rotation=0.0) → Insert

Fügt eine Block-Referenz (INSERT) mit Attributen hinzu.

Args:

  • msp: Modelspace des DXF-Dokuments
  • block_name (str): Name des Blocks
  • insert_point (tuple): Einfügepunkt als (x, y) oder (x, y, z) Tuple
  • attributes (dict, optional): Dictionary mit Attribut-Namen und -Werten
  • layer (str): Layer-Name (default: '0')
  • scale (float): Skalierungsfaktor (default: 1.0)
  • rotation (float): Rotation in Grad (default: 0.0)

Returns:

  • Insert: INSERT-Entity mit gesetzten Attributen

Example:

from utils import add_blockref_with_attributes

# Block ohne Attribute
add_blockref_with_attributes(msp, "MOTOR_SYMBOL", (100, 100))

# Block mit Attributen
attrs = {"IO": "MA0001", "KENNZEICHNUNG": "=A01+UH00"}
add_blockref_with_attributes(msp, "io", (200, 200), attributes=attrs)

# Block mit Skalierung und Rotation
add_blockref_with_attributes(msp, "SENSOR", (300, 300),
                            scale=0.5, rotation=90.0, layer='ILS_SENSOR')

Entity Queries

query_entities_by_layer(msp, entity_type=None, layers=None, exclude_layers=None) → list

Filtert Entities nach Typ und Layer.

Args:

  • msp: Modelspace des DXF-Dokuments
  • entity_type (str, optional): Entity-Typ (z.B. 'INSERT', 'LINE', 'TEXT'), None für alle
  • layers (list, optional): Liste der zu inkludierenden Layer (None = alle außer exclude_layers)
  • exclude_layers (list, optional): Liste der zu exkludierenden Layer (None = keine Exklusion)

Returns:

  • list: Liste der gefilterten Entities

Examples:

from utils import query_entities_by_layer

# Alle INSERTs auf Layer 'ILS_MOTOR'
motors = query_entities_by_layer(msp, 'INSERT', layers=['ILS_MOTOR'])

# Alle Entities außer auf Layer '0'
entities = query_entities_by_layer(msp, exclude_layers=['0'])

# Alle LINEs auf ILS_* Layern (mit zusätzlichem Filtern)
all_lines = query_entities_by_layer(msp, 'LINE')
ils_lines = [e for e in all_lines if e.dxf.layer.startswith('ILS_')]

# Alle INSERTs auf mehreren Layern
inserts = query_entities_by_layer(msp, 'INSERT',
                                 layers=['ILS_MOTOR', 'ILS_SENSOR'])

Dictionary Operations

merge_two_dicts(x, y) → dict

Merge two dictionaries, with values from y overwriting those in x.

Args:

  • x (dict): First dictionary (base)
  • y (dict): Second dictionary (override values)

Returns:

  • dict: New dictionary with merged values

Example:

from utils import merge_two_dicts

defaults = {"color": 1, "layer": "0", "scale": 1.0}
overrides = {"color": 5, "scale": 2.0}
result = merge_two_dicts(defaults, overrides)
# Result: {"color": 5, "layer": "0", "scale": 2.0}

Migration Guide

From old attribs_to_dicts() to new API

Old code (getpositions.py):

from getpositions import attribs_to_dicts
all_inserts, all_positions = attribs_to_dicts(msp)

New code:

from utils import extract_attributes_with_positions
all_inserts, all_positions = extract_attributes_with_positions(msp)

From old extract_block_attributes() to new API

Old code (create_numbers.py):

def extract_block_attributes(doc, insert, error_collector=None):
    # 48 lines of code...
    return attributes

New code:

from utils import extract_insert_attributes_with_doc as extract_block_attributes
# Direct usage - no local function needed

Performance Notes

  1. extract_attributes_with_positions: O(n) where n = number of INSERT entities
  2. query_entities_by_layer: O(n) where n = total entities in msp
  3. ensure_layer_exists: O(1) - uses dict lookup
  4. create_layers_from_config: O(m) where m = number of layers to create

Error Handling

Most functions follow these error handling patterns:

  1. Silent fallback: Functions like extract_insert_attributes() return empty dict on error
  2. ErrorCollector integration: Functions accepting error_collector parameter log warnings
  3. System exit: check_environment_var() and get_dxf_file() exit on critical errors
  4. Exception propagation: JSON and file operations raise native Python exceptions

Best Practices

  1. Always use extract_insert_attributes_with_doc() for new code (better error handling)
  2. Use ensure_layer_exists() before adding entities to avoid missing layer errors
  3. Prefer query_entities_by_layer() over manual filtering for better readability
  4. Use create_layers_from_config() when setting up multiple layers from configuration files
  5. Pass error_collector when available for comprehensive error tracking

Version History

  • v1.2.0 (2026-01-30): Added 8 new ezdxf helper functions
    • extract_insert_attributes_with_doc()
    • extract_attributes_with_positions()
    • ensure_layer_exists()
    • add_rectangle_lwpolyline()
    • add_text_with_alignment()
    • add_blockref_with_attributes()
    • query_entities_by_layer()
    • create_layers_from_config()
  • v1.1.0: Refactored extract_insert_attributes() to support two-level blocks
  • v1.0.0: Initial release with file, JSON, and DXF operations