""" create_examples.py - Liest Stuecklisten-Daten aus Excel-Dateien im Examples-Ordner. Verwendung: python create_examples.py [--example-dir ] """ from __future__ import annotations import argparse import logging import sys from dataclasses import dataclass from pathlib import Path from typing import Any log = logging.getLogger(__name__) @dataclass class StkItem: position: str teilenummer: str menge: Any def _cell_str(value: Any) -> str: return str(value).strip() if value is not None else "" def _find_col_index(header_row: tuple[Any, ...], candidates: list[str]) -> int | None: normalized = [_cell_str(v).lower() for v in header_row] for candidate in candidates: candidate_norm = candidate.lower() if candidate_norm in normalized: return normalized.index(candidate_norm) return None def _extract_stk_liste_ist(xlsx_path: Path) -> list[StkItem]: import openpyxl wb = openpyxl.load_workbook(str(xlsx_path), read_only=True, data_only=True) if "Stk.-Liste IST" not in wb.sheetnames: wb.close() raise ValueError("Reiter 'Stk.-Liste IST' nicht gefunden") ws = wb["Stk.-Liste IST"] header_idx: tuple[int | None, int | None, int | None] | None = None items: list[StkItem] = [] for row in ws.iter_rows(values_only=True): if header_idx is None: pos_idx = _find_col_index(list(row), ["position", "pos", "pos."]) part_idx = _find_col_index( list(row), ["teilenummer", "teilnummer", "article", "artikelnummer"], ) qty_idx = _find_col_index(list(row), ["menge", "anzahl", "qty", "quantity"]) if pos_idx is not None and part_idx is not None and qty_idx is not None: header_idx = (pos_idx, part_idx, qty_idx) continue pos_idx, part_idx, qty_idx = header_idx assert pos_idx is not None and part_idx is not None and qty_idx is not None position = _cell_str(row[pos_idx]) if pos_idx < len(row) else "" teilenummer = _cell_str(row[part_idx]) if part_idx < len(row) else "" menge = row[qty_idx] if qty_idx < len(row) else None # Leere Zeilen ueberspringen if not position and not teilenummer and menge in ("", None): continue if not position or not teilenummer: continue items.append(StkItem(position=position, teilenummer=teilenummer, menge=menge)) wb.close() return items def process_all(example_dir: Path) -> dict[str, list[dict[str, Any]]]: """ Durchsucht rekursiv alle Unterverzeichnisse in example_dir. Pro Unterverzeichnis wird die erste gefundene .xlsx-Datei gelesen und aus dem Reiter "Stk.-Liste IST" werden Position, Teilenummer, Menge extrahiert. """ if not example_dir.exists(): log.error("Beispiel-Verzeichnis existiert nicht: %s", example_dir) sys.exit(1) subdirs = sorted([p for p in example_dir.rglob("*") if p.is_dir()]) if not subdirs: log.error("Keine Unterverzeichnisse in %s gefunden", example_dir) sys.exit(1) log.info("Gefunden: %d Unterverzeichnisse in %s", len(subdirs), example_dir) result: dict[str, list[dict[str, Any]]] = {} for folder in subdirs: xlsx_files = sorted(folder.glob("*.xlsx")) if not xlsx_files: continue xlsx_path = xlsx_files[0] rel_folder = str(folder.relative_to(example_dir)) log.info("Verarbeite %s: %s", rel_folder, xlsx_path.name) try: items = _extract_stk_liste_ist(xlsx_path) except ValueError as exc: log.warning("%s/%s uebersprungen: %s", rel_folder, xlsx_path.name, exc) continue result[rel_folder] = [ { "position": item.position, "teilenummer": item.teilenummer, "menge": item.menge, } for item in items ] log.info("Fertig. Gelesene Verzeichnisse mit Daten: %d", len(result)) return result def main() -> None: parser = argparse.ArgumentParser( description="Liest Position/Teilenummer/Menge aus Example-Exceldateien" ) parser.add_argument("--example-dir", type=Path, default=None, help="example/ Verzeichnis (default: aus PV_EXAMPLES oder ./examples)") parser.add_argument("--verbose", "-v", action="store_true", help="Ausfuehrliche Ausgabe") args = parser.parse_args() logging.basicConfig( level=logging.DEBUG if args.verbose else logging.INFO, format="%(levelname)-7s %(message)s", ) import os if args.example_dir: example_dir = args.example_dir.resolve() elif os.environ.get("PV_EXAMPLES"): example_dir = Path(os.environ["PV_EXAMPLES"]).resolve() else: example_dir = Path("example").resolve() log.info("Beispiel-Verzeichnis: %s", example_dir) data = process_all(example_dir) log.info("Objekt erzeugt mit %d Schluesseln", len(data)) if __name__ == "__main__": main()