← back to Dw Catalog 404 Audit

scripts/schumacher-265-live-check.py

68 lines

#!/usr/bin/env python3
"""READ-ONLY live-Shopify completeness check for the Schumacher-265 unpublished-ACTIVE cohort.
GET only. No writes. Paces requests to respect the 2 req/s REST bucket.
Usage: python3 schumacher-265-live-check.py [limit]
Outputs CSV to stdout."""
import os, sys, time, json, urllib.request, subprocess

LIMIT = int(sys.argv[1]) if len(sys.argv) > 1 else 0
STORE = "designer-laboratory-sandbox.myshopify.com"
VER = "2024-10"

def token():
    with open(os.path.expanduser("~/Projects/secrets-manager/.env")) as f:
        for ln in f:
            if ln.startswith("SHOPIFY_ADMIN_TOKEN="):
                return ln.split("=",1)[1].strip().strip('"').strip("'")
    raise SystemExit("no token")

TOK = token()

def get_ids():
    q = ("SELECT regexp_replace(shopify_id,'\\D','','g') FROM shopify_products "
         "WHERE vendor='Schumacher' AND status='ACTIVE' AND online_store_published IS NOT TRUE "
         "ORDER BY shopify_id")
    out = subprocess.check_output(["psql","-d","dw_unified","-At","-c",q]).decode()
    ids = [x for x in out.splitlines() if x.strip()]
    return ids[:LIMIT] if LIMIT else ids

def fetch(pid, tries=4):
    url = f"https://{STORE}/admin/api/{VER}/products/{pid}.json"
    req = urllib.request.Request(url, headers={"X-Shopify-Access-Token": TOK})
    for t in range(tries):
        try:
            with urllib.request.urlopen(req, timeout=30) as r:
                return json.loads(r.read().decode())
        except urllib.error.HTTPError as e:
            if e.code == 429:
                time.sleep(2*(t+1)); continue
            if e.code == 404:
                return {"_404": True}
            time.sleep(1)
        except Exception:
            time.sleep(1)
    return None

print("id,sku,title,published_at,images,body_len,nvariants,prod_price,sample_price,has_sample,inv_tracked")
ids = get_ids()
for i, pid in enumerate(ids):
    d = fetch(pid)
    time.sleep(0.55)  # ~1.8 req/s
    if not d or "_404" in d:
        print(f"{pid},,,FETCH_FAIL,,,,,,,"); continue
    p = d.get("product", {})
    vs = p.get("variants") or []
    prod = [v for v in vs if not (v.get("sku") or "").endswith("-Sample")]
    samp = [v for v in vs if (v.get("sku") or "").endswith("-Sample")]
    pv = prod[0] if prod else {}
    sv = samp[0] if samp else {}
    title = (p.get("title") or "").replace(",", " ")
    print(",".join(str(x) for x in [
        pid, pv.get("sku") or "", title, p.get("published_at"),
        len(p.get("images") or []), len(p.get("body_html") or ""),
        len(vs), pv.get("price"), sv.get("price"),
        "Y" if samp else "N", pv.get("inventory_management") or "none",
    ]))
    sys.stderr.write(f"\r{i+1}/{len(ids)}"); sys.stderr.flush()
sys.stderr.write("\n")