← back to Dw Photo Capture
All GRS- sold PER YARD: flipped 133 products (variant Roll->Per Yard, unit_of_measure->Priced Per Yard, dwc.order_unit->Yard); now 174/174 uniform. createFromSheet defaults to per-yard for future creates. Price unchanged (already per-yard)
a4af440e2c6d51c12f9218ea2e3f130d46c64ce7 · 2026-06-25 06:30:41 -0700 · steve@designerwallcoverings.com
Files touched
M data/build.jsonA grs_per_yard.py
Diff
commit a4af440e2c6d51c12f9218ea2e3f130d46c64ce7
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Thu Jun 25 06:30:41 2026 -0700
All GRS- sold PER YARD: flipped 133 products (variant Roll->Per Yard, unit_of_measure->Priced Per Yard, dwc.order_unit->Yard); now 174/174 uniform. createFromSheet defaults to per-yard for future creates. Price unchanged (already per-yard)
---
data/build.json | 5 +++--
grs_per_yard.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/data/build.json b/data/build.json
index 847bf12..7b98532 100644
--- a/data/build.json
+++ b/data/build.json
@@ -1,5 +1,5 @@
{
- "next": 20,
+ "next": 21,
"map": {
"578af86f": 2,
"bc95fdb0": 3,
@@ -18,6 +18,7 @@
"9905b54e": 16,
"48c9e659": 17,
"779f6d1f": 18,
- "598b1ee5": 19
+ "598b1ee5": 19,
+ "c3efeca8": 20
}
}
\ No newline at end of file
diff --git a/grs_per_yard.py b/grs_per_yard.py
new file mode 100644
index 0000000..3e71ef3
--- /dev/null
+++ b/grs_per_yard.py
@@ -0,0 +1,48 @@
+#!/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")
← 3c7d0a2 Add ⭐ Favorites + 🕘 Recent views: star any card to pin SKUs
·
back to Dw Photo Capture
·
Multi-photo add: ➕ Multiple in the Photos gallery batch-appe 4e6ec3f →