"""Synthetic example only: transform the documented local catalogue schema.""" import argparse import csv from decimal import Decimal, InvalidOperation import json from pathlib import Path def convert(source, destination, report_path): accepted = [] rejected = [] seen = set() with source.open(encoding="utf-8-sig", newline="") as handle: reader = csv.DictReader(handle, delimiter=";") if reader.fieldnames != ["Artikelnummer", "Titel", "Preis", "Bestand"]: raise ValueError("Input headers differ from the documented example schema") rows = list(reader) for index, row in enumerate(rows, start=2): sku = (row.get("Artikelnummer") or "").strip() title = (row.get("Titel") or "").strip() reason = None if None in row or any(value is None for value in row.values()): reason = "column_count" elif not sku or not title: reason = "required_field" elif sku in seen: reason = "duplicate_sku" else: try: price = Decimal(row["Preis"].strip().replace(",", ".")) stock_text = row["Bestand"].strip() if not price.is_finite() or price < 0 or price != price.quantize(Decimal("0.01")): raise ValueError("Price must be nonnegative with at most two decimal places") if not stock_text.isascii() or not stock_text.isdigit(): raise ValueError("Stock must be a nonnegative integer") stock = int(stock_text) except (InvalidOperation, ValueError): reason = "invalid_scalar" if reason: rejected.append({"source_line": index, "sku": sku, "reason": reason}) else: seen.add(sku) accepted.append({"sku": sku, "title": title, "price_eur": format(price, ".2f"), "stock": stock}) with destination.open("w", encoding="utf-8", newline="") as handle: writer = csv.DictWriter(handle, fieldnames=["sku", "title", "price_eur", "stock"], lineterminator="\n") writer.writeheader() writer.writerows(accepted) report = {"synthetic_example": True, "input_records": len(rows), "accepted_records": len(accepted), "rejected_records": len(rejected), "counts_reconcile": len(rows) == len(accepted) + len(rejected), "duplicate_policy": "Reject subsequent duplicate of an accepted SKU; do not infer or merge", "rejections": rejected} report_path.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") return accepted, report if __name__ == "__main__": parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("source", type=Path) parser.add_argument("destination", type=Path) parser.add_argument("report", type=Path) options = parser.parse_args() convert(options.source, options.destination, options.report)