← back to Kravet Sheet Sync 2026 04 20

build_dwkk_variant_map.py

105 lines

#!/usr/bin/env python3
"""
Build authoritative dw_sku → (product_id, variant_id, current_price) map from Shopify.
Queries all products with SKU starting DWKK- and extracts main variant.
Persists to PG: kravet_dwkk_variant_map (dw_sku, product_gid, variant_gid, price, status, updated_at)
"""
import json, subprocess, time, urllib.request

TOKEN=os.environ.get('SHOPIFY_ADMIN_TOKEN','')
URL='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json'
SSH=['ssh','root@45.61.58.125']


def gql(q, v=None):
    body=json.dumps({"query":q,"variables":v or {}}).encode()
    req=urllib.request.Request(URL,data=body,method="POST",
        headers={"Content-Type":"application/json","X-Shopify-Access-Token":TOKEN})
    d=json.loads(urllib.request.urlopen(req,timeout=30).read())
    if "errors" in d: raise RuntimeError(d["errors"])
    return d["data"]


def pg_exec(sql):
    subprocess.run(SSH + [
        f'PGPASSWORD=DW2024! psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "{sql}"'],
        capture_output=True, text=True, check=True)


Q="""query($cursor:String){
  products(first:100, after:$cursor, query:"sku:DWKK-*") {
    pageInfo{ hasNextPage endCursor }
    nodes{
      id status
      variants(first:10){
        nodes{ id sku price inventoryItem{ id unitCost{ amount } } }
      }
    }
  }
}"""

print("fetching DWKK products from Shopify ...")
rows = []
cursor = None
pages = 0
while True:
    d = gql(Q, {"cursor": cursor})
    prods = d["products"]["nodes"]
    for p in prods:
        for v in p["variants"]["nodes"]:
            sku = v["sku"] or ""
            # Keep only exact DWKK-NNNN (not -Sample, not -Per Yard, etc.) main variants.
            # Accept both plain DWKK-NNNNN and DWKK-NNNNN (no suffix)
            if sku.startswith("DWKK-") and "-Sample" not in sku and " " not in sku and sku.count("-") == 1:
                rows.append({
                    "dw_sku":     sku,
                    "product_gid": p["id"],
                    "variant_gid": v["id"],
                    "price":       v["price"],
                    "cost":        (v["inventoryItem"].get("unitCost") or {}).get("amount"),
                    "status":      p["status"],
                })
    pages += 1
    if pages % 5 == 0:
        print(f"  page {pages}  products={sum(1 for _ in rows)}  cursor={cursor[:40] if cursor else '-'}")
    if not d["products"]["pageInfo"]["hasNextPage"]: break
    cursor = d["products"]["pageInfo"]["endCursor"]

print(f"collected {len(rows)} DWKK main variants across {pages} pages")

# Build unique map (first-wins on dupe dw_sku)
seen = {}
for r in rows:
    seen.setdefault(r["dw_sku"], r)
print(f"unique dw_skus: {len(seen)}")

# Status breakdown
from collections import Counter
print(f"statuses: {dict(Counter(r['status'] for r in seen.values()))}")

# Persist
pg_exec("""
CREATE TABLE IF NOT EXISTS kravet_dwkk_variant_map (
  dw_sku       text PRIMARY KEY,
  product_gid  text NOT NULL,
  variant_gid  text NOT NULL,
  price        numeric,
  cost         numeric,
  status       text,
  updated_at   timestamptz DEFAULT now()
);
TRUNCATE kravet_dwkk_variant_map;
""")

# Bulk insert via COPY
import tempfile, csv
with tempfile.NamedTemporaryFile("w", suffix=".csv", delete=False, newline="") as tf:
    w = csv.writer(tf)
    for r in seen.values():
        w.writerow([r["dw_sku"], r["product_gid"], r["variant_gid"],
                    r["price"] or "", r["cost"] or "", r["status"]])
    tmp = tf.name
subprocess.run(["scp", tmp, "root@45.61.58.125:/tmp/dwkk_map.csv"], check=True)
subprocess.run(SSH + ["""PGPASSWORD=DW2024! psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \"\\copy kravet_dwkk_variant_map (dw_sku,product_gid,variant_gid,price,cost,status) FROM '/tmp/dwkk_map.csv' WITH (FORMAT csv)\""""], check=True)
print(f"wrote {len(seen)} rows to kravet_dwkk_variant_map")