← back to Dw Product Class
push_class.py
67 lines
import os, sys, json, time, urllib.request
API = os.environ["SHOP_API"]
TOKEN = os.environ["TOKEN"]
INFILE= os.environ["INFILE"]
OKF = os.environ["OKFILE"]
ERRF = os.environ["ERRFILE"]
BATCH = int(os.environ.get("BATCH","25"))
MUT = ("mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){"
"metafields{id} userErrors{field message code}}}")
def post(payload):
req = urllib.request.Request(API, data=json.dumps(payload).encode(),
headers={"X-Shopify-Access-Token":TOKEN,"Content-Type":"application/json"})
with urllib.request.urlopen(req, timeout=60) as r:
return json.load(r)
rows=[]
with open(INFILE) as f:
for line in f:
line=line.rstrip("\n")
if not line: continue
gid,cls=line.split("\t")
rows.append((gid,cls))
ok=open(OKF,"a"); err=open(ERRF,"a")
done=0; failed=0
i=0
while i < len(rows):
chunk=rows[i:i+BATCH]
mf=[{"ownerId":g,"namespace":"custom","key":"product_class",
"type":"single_line_text_field","value":c} for g,c in chunk]
attempt=0
while True:
attempt+=1
try:
res=post({"query":MUT,"variables":{"mf":mf}})
except Exception as e:
if attempt<=5:
time.sleep(2*attempt); continue
for g,c in chunk: err.write(f"{g}\t{c}\tHTTP:{e}\n"); failed+=1
res=None; break
if "errors" in res and res["errors"]:
msg=json.dumps(res["errors"])[:200]
if "THROTTLED" in msg and attempt<=8:
time.sleep(1.5*attempt); continue
for g,c in chunk: err.write(f"{g}\t{c}\tGQL:{msg}\n"); failed+=1
break
ue=res["data"]["metafieldsSet"]["userErrors"]
if ue:
for g,c in chunk: err.write(f"{g}\t{c}\tUE:{json.dumps(ue)[:150]}\n"); failed+=1
else:
for g,c in chunk: ok.write(g+"\n"); done+=1
# throttle guard
thr=res.get("extensions",{}).get("cost",{}).get("throttleStatus",{})
avail=thr.get("currentlyAvailable",4000)
if avail<600: time.sleep(1.0)
break
i+=BATCH
if i % 500 == 0:
ok.flush(); err.flush()
print(f" ...{i}/{len(rows)} processed (ok={done} fail={failed})", flush=True)
ok.close(); err.close()
print(f"DONE processed={len(rows)} ok={done} failed={failed}", flush=True)