← back to Vahallan Onboard

fix-drafts-quoteonly.py

49 lines

#!/usr/bin/env python3
"""Make Vahallan quote-only drafts gate-passable: add global.width metafield (real
Vahallan standard 30" panel) + the `quotes` tag. Operates on DRAFT products only
(not customer-facing). Resumable. --limit N to test. Steve-approved 2026-07-23
('extend gate exemption + tag quotes')."""
import json, subprocess, sys, time, urllib.request

TOKEN = next(l.split('=',1)[1].strip() for l in open('/Users/macstudio3/Projects/secrets-manager/.env')
             if l.startswith('SHOPIFY_ADMIN_TOKEN='))
SHOP = 'designer-laboratory-sandbox.myshopify.com'
API = f'https://{SHOP}/admin/api/2024-10'
WIDTH = '30 Inches'   # Vahallan documented panel width (30" untrimmed, 28" usable)
LIMIT = int(sys.argv[sys.argv.index('--limit')+1]) if '--limit' in sys.argv else 10**9

def req(method, path, body=None):
    r = urllib.request.Request(API+path, method=method,
        data=json.dumps(body).encode() if body else None,
        headers={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'})
    for attempt in range(4):
        try: return json.loads(urllib.request.urlopen(r, timeout=40).read())
        except urllib.error.HTTPError as e:
            if e.code==429: time.sleep(float(e.headers.get('Retry-After','3'))+1); continue
            raise
    raise RuntimeError('retries exhausted')

raw = subprocess.run(['psql','-h','/tmp','-d','dw_unified','-tA','-c',
    "SELECT coalesce(json_agg(json_build_array(id,shopify_product_id,handle) ORDER BY dw_sku),'[]') "
    "FROM vahallan_catalog WHERE NOT already_on_shopify AND shopify_product_id IS NOT NULL"],
    capture_output=True,text=True)
rows = json.loads(raw.stdout)[:LIMIT]
print(f'fixing {len(rows)} drafts (width + quotes tag)', flush=True)
done=0
for rid, pid, handle in rows:
    p = req('GET', f'/products/{pid}.json?fields=id,tags,status')['product']
    if p['status'] != 'draft':
        print(f'  SKIP {handle}: status={p["status"]} (not draft)'); continue
    tags = [t.strip() for t in (p['tags'] or '').split(',') if t.strip()]
    if 'quotes' not in tags: tags.append('quotes')
    req('PUT', f'/products/{pid}.json', {'product':{'id':pid,'tags':', '.join(tags)}})
    # width metafield (global.width) — idempotent: skip if present
    mfs = req('GET', f'/products/{pid}/metafields.json?namespace=global')['metafields']
    if not any(m['key']=='width' and (m['value'] or '').strip() for m in mfs):
        req('POST', f'/products/{pid}/metafields.json', {'metafield':{
            'namespace':'global','key':'width','value':WIDTH,'type':'single_line_text_field'}})
    done+=1
    if done%25==0: print(f'  {done}/{len(rows)}', flush=True)
    time.sleep(0.4)
print(f'DONE fixed={done}', flush=True)