← back to Dw Photo Capture
grs_per_yard.py
49 lines
#!/usr/bin/env python3
"""All GRS- products are sold PER YARD. Update: variant option 'Roll'->'Per Yard',
global.unit_of_measure->'Priced Per Yard', dwc.order_unit->'Yard'. Price unchanged
(already per-yard = cost*0.75). DRY default; CONFIRM=1 writes."""
import os,sys,json,time,urllib.request
SHOP="designer-laboratory-sandbox.myshopify.com";API="2024-10";DRY=os.environ.get("CONFIRM","0")!="1"
TOKEN=""
for l in open(os.path.expanduser("~/Projects/secrets-manager/.env")):
if l.startswith("SHOPIFY_ADMIN_TOKEN="): TOKEN=l.strip().split("=",1)[1];break
H={"X-Shopify-Access-Token":TOKEN,"Content-Type":"application/json"}
def api(m,p,pl=None):
d=json.dumps(pl).encode() if pl is not None else None
r=urllib.request.Request(f"https://{SHOP}/admin/api/{API}"+p,data=d,headers=H,method=m)
with urllib.request.urlopen(r,timeout=60) as x: b=x.read().decode();return x.status,(json.loads(b) if b.strip() else {})
def gql(q,v=None): return api("POST","/graphql.json",{"query":q,"variables":v or {}})[1]
Q="""query($c:String){products(first:100,query:"vendor:Fentucci",after:$c){pageInfo{hasNextPage endCursor}
edges{node{legacyResourceId status uom:metafield(namespace:"global",key:"unit_of_measure"){value}
v:variants(first:3){edges{node{legacyResourceId sku title}}}}}}}"""
c=None;targets=[]
while True:
d=gql(Q,{"c":c})["data"]["products"]
for e in d["edges"]:
n=e["node"]
if n["status"]=="ARCHIVED": continue
roll=next((v["node"] for v in n["v"]["edges"] if (v["node"]["sku"] or "").upper().startswith("GRS-") and not (v["node"]["sku"] or "").upper().endswith("-SAMPLE")),None)
if not roll: continue
uom=(n.get("uom") or {}).get("value")
if roll["title"]!="Per Yard" or uom!="Priced Per Yard":
targets.append({"pid":n["legacyResourceId"],"vid":roll["legacyResourceId"],"sku":roll["sku"],"title":roll["title"],"uom":uom})
if d["pageInfo"]["hasNextPage"]: c=d["pageInfo"]["endCursor"]
else: break
print(f"GRS products to update -> Per Yard: {len(targets)}")
for t in targets[:6]: print(f" {t['sku']:12} variant '{t['title']}'->Per Yard | uom '{t['uom']}'->Priced Per Yard")
if DRY: print("\n[DRY] CONFIRM=1 to apply."); sys.exit()
n=0
for t in targets:
try:
if t["title"]!="Per Yard":
api("PUT",f"/variants/{t['vid']}.json",{"variant":{"id":t["vid"],"option1":"Per Yard"}})
gql("""mutation($id:ID!){metafieldsSet(metafields:[
{ownerId:$id,namespace:"global",key:"unit_of_measure",type:"single_line_text_field",value:"Priced Per Yard"},
{ownerId:$id,namespace:"dwc",key:"order_unit",type:"single_line_text_field",value:"Yard"}]){userErrors{message}}}""",
{"id":f"gid://shopify/Product/{t['pid']}"})
n+=1
if n%25==0: print(f" ...{n}")
time.sleep(0.4)
except Exception as e: print(f" ERR {t['sku']}: {e}")
print(f"\nDONE — set {n} GRS products to Per Yard")