← back to Dw Collection Banner Audit
land_renders.py
73 lines
#!/usr/bin/env python3
"""
Upload the 29 room-render PNGs to Shopify + set each as its collection banner.
Uses REST image.attachment (base64) so Shopify hosts the image (no staged-upload).
Auto-detects custom vs smart collection (tries custom, falls back to smart).
Records old->new in rollback.jsonl. Detects storage-cap failures and stops.
"""
import json, os, base64, time, urllib.request, glob
HERE = os.path.dirname(os.path.abspath(__file__))
STORE = "designer-laboratory-sandbox.myshopify.com"; API = "2024-10"
TOKEN = os.environ["SHOPIFY_ADMIN_TOKEN"]
cols = {c["handle"]: c for c in json.load(open(os.path.join(HERE, "dw_collections.json")))}
prop = {r["handle"]: r for r in json.load(open(os.path.join(HERE, "proposal.json")))}
def rest_put(kind, cid, payload):
url = f"https://{STORE}/admin/api/{API}/{kind}/{cid}.json"
req = urllib.request.Request(url, data=json.dumps(payload).encode(),
headers={"X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json"}, method="PUT")
return urllib.request.urlopen(req, timeout=60)
# handles already landed as a room-render (skip to avoid re-upload)
landed = set()
rbp = os.path.join(HERE, "rollback.jsonl")
if os.path.exists(rbp):
for line in open(rbp):
try:
d = json.loads(line)
if d.get("ok") and d.get("room_render"): landed.add(d["handle"])
except: pass
log = open(rbp, "a")
files = [f for f in sorted(glob.glob(os.path.join(HERE, "room_renders", "*.png")))
if os.path.basename(f)[:-4] not in landed]
print(f"already-landed room-renders skipped: {len(landed)}; new to land: {len(files)}")
ok = fail = 0
for i, f in enumerate(files, 1):
handle = os.path.basename(f)[:-4]
c = cols.get(handle)
if not c:
print(f" SKIP {handle} (no collection)"); continue
cid = c["id"]; title = c["title"]
b64 = base64.b64encode(open(f, "rb").read()).decode()
old_src = (prop.get(handle) or {}).get("current_src")
done = False
for kind, key in [("custom_collections", "custom_collection"), ("smart_collections", "smart_collection")]:
try:
resp = rest_put(kind, cid, {key: {"id": cid, "image": {"attachment": b64, "alt": title}}})
data = json.load(resp)
img = data.get(key, {}).get("image")
new_src = img.get("src") if img else None
print(f" OK {handle} ({kind.split('_')[0]}) -> {new_src.rsplit('/',1)[-1][:40] if new_src else '?'}")
log.write(json.dumps({"handle": handle, "ok": True, "old_src": old_src,
"new_src": new_src, "room_render": True}) + "\n")
ok += 1; done = True; break
except urllib.error.HTTPError as e:
body = e.read().decode()[:200]
if e.code == 404:
continue # wrong collection type, try the other
if "STORAGE" in body.upper() or "storage" in body.lower():
print(f" STORAGE-CAP HIT on {handle}: {body}"); log.close()
print("STOPPING — Shopify file storage cap reached."); raise SystemExit(2)
print(f" FAIL {handle}: HTTP {e.code} {body}")
log.write(json.dumps({"handle": handle, "ok": False, "err": f"{e.code} {body}"}) + "\n")
fail += 1; done = True; break
except Exception as e:
print(f" ERR {handle}: {str(e)[:120]}"); fail += 1; done = True; break
if not done:
print(f" FAIL {handle}: neither custom nor smart matched"); fail += 1
time.sleep(0.7)
log.close()
print(f"\nlanded ok={ok} fail={fail}")