#!/usr/bin/env python3 """Import a full catalog.json into the admin SQLite DB (no HTTP / no size limit). Usage: python3 import_catalog.py /path/to/catalog.json python3 import_catalog.py /path/to/catalog.json --export """ from __future__ import annotations import argparse import json import sys from pathlib import Path # Reuse server helpers sys.path.insert(0, str(Path(__file__).resolve().parent)) import server # noqa: E402 def map_export_category(raw: dict) -> dict: """Map export {id,parent_id,name,full_path} -> admin category row fields.""" name = str(raw.get("name") or raw.get("full_path") or "").strip() if not name: raise ValueError("category missing name") slug = str(raw.get("slug") or server._slugify(name)) parent_slug = None parent_id = raw.get("parent_id") or raw.get("parentSlug") or raw.get("parent_slug") if parent_id: parent_name = str(parent_id).split(" / ")[-1].strip() parent_slug = server._slugify(parent_name) return { "slug": slug, "name": name.title() if name == name.upper() else name, "name_fa": str(raw.get("name_fa") or raw.get("nameFa") or ""), "parent_slug": parent_slug, "sort_order": int(raw.get("sort_order") or raw.get("sortOrder") or 0), "active": raw.get("active", True), } def import_categories(payload: dict) -> int: cats = payload.get("categories") or [] if not isinstance(cats, list): return 0 # parents first (shorter paths) cats = sorted(cats, key=lambda c: str(c.get("full_path") or c.get("id") or "").count("/")) count = 0 with server.db() as conn: for raw in cats: if not isinstance(raw, dict): continue try: server.upsert_category(conn, map_export_category(raw)) count += 1 except Exception as exc: print(f" category skip: {exc}", file=sys.stderr) return count def main(): parser = argparse.ArgumentParser(description="Import catalog.json into admin DB") parser.add_argument("catalog", type=Path, help="Path to catalog.json") parser.add_argument( "--export", action="store_true", help="Also export active products to storefront catalog.dedupe2.json", ) parser.add_argument( "--products-only", action="store_true", help="Skip categories", ) args = parser.parse_args() path = args.catalog.expanduser().resolve() if not path.is_file(): print(f"File not found: {path}", file=sys.stderr) sys.exit(1) print(f"Loading {path} ({path.stat().st_size // 1_000_000} MB)…") payload = json.loads(path.read_text(encoding="utf-8")) if not isinstance(payload, (dict, list)): print("Invalid JSON root", file=sys.stderr) sys.exit(1) server.init_db() if isinstance(payload, dict) and not args.products_only: n = import_categories(payload) print(f"Categories imported/updated: {n}") print("Importing products…") result = server.import_products_payload(payload) print( f"Products created={result['created']} updated={result['updated']} " f"skipped={result['skipped']} errors={len(result['errors'])}" ) if result["errors"]: print("First errors:", file=sys.stderr) for err in result["errors"][:10]: print(f" {err}", file=sys.stderr) if args.export: out = server.export_catalog() print(f"Exported {out['count']} products → {out['path']}") with server.db() as conn: pc = conn.execute("SELECT COUNT(*) AS c FROM products").fetchone()["c"] cc = conn.execute("SELECT COUNT(*) AS c FROM categories").fetchone()["c"] print(f"DB totals: products={pc} categories={cc}") if __name__ == "__main__": main()