← back to Vahallan Onboard

import-drafts.py

59 lines

#!/usr/bin/env python3
"""Create Shopify DRAFT products for vahallan_catalog rows not yet on Shopify.
Template = existing Vahallan products (sample-only $4.25, Size=Sample, display_variant).
Resumable: skips rows with shopify_product_id set or already_on_shopify. 429-aware.
Steve-approved 2026-07-23 (AskUserQuestion: 'Yes — create the drafts')."""
import json, subprocess, time, urllib.request, sys

TOKEN = next(l.split('=',1)[1].strip() for l in open('/Users/macstudio3/Projects/secrets-manager/.env')
             if l.startswith('SHOPIFY_ADMIN_TOKEN='))
API = 'https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/products.json'

def q(sql):
    r = subprocess.run(['psql','-h','/tmp','-d','dw_unified','-tAF','\x1f','-c',sql],capture_output=True,text=True)
    if r.returncode: sys.exit('psql error: '+r.stderr[:300])
    return [ln.split('\x1f') for ln in r.stdout.strip().split('\n') if ln]

raw = subprocess.run(['psql','-h','/tmp','-d','dw_unified','-tA','-c',
    """SELECT coalesce(json_agg(json_build_array(id,mfr_sku,handle,pattern,title,description_html,tags,image_url,dw_sku) ORDER BY dw_sku),'[]')
       FROM vahallan_catalog WHERE NOT already_on_shopify AND shopify_product_id IS NULL"""],
    capture_output=True,text=True)
if raw.returncode: sys.exit('psql error: '+raw.stderr[:300])
rows = json.loads(raw.stdout)
print(f'to import: {len(rows)}', flush=True)
done=0; errs=0
for rid,mfr,handle,pattern,title,desc,tags,img,dw in rows:
    tagset = {t.strip() for t in tags.split(',') if t.strip()}
    tagset |= {'vahallan','wallcovering','Vahallan '+pattern, title, 'display_variant','hand-crafted','Textured'}
    payload = {'product':{
        'title': f'{title} Wallcovering | Vahallan',
        'body_html': desc,
        'vendor': 'Vahallan',
        'product_type': 'Wallcovering',
        'status': 'draft',
        'tags': ', '.join(sorted(tagset)),
        'options': [{'name':'Size','values':['Sample']}],
        'variants': [{'option1':'Sample','price':'4.25','sku':f'{dw}-Sample',
                      'inventory_management':'shopify','inventory_policy':'continue',
                      'requires_shipping':True,'taxable':True,'grams':0}],
        'images': [{'src':img,'alt':title}] if img else [],
    }}
    for attempt in range(4):
        req = urllib.request.Request(API, data=json.dumps(payload).encode(),
              headers={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'})
        try:
            resp = urllib.request.urlopen(req, timeout=40)
            pid = json.loads(resp.read())['product']['id']
            q(f"UPDATE vahallan_catalog SET shopify_product_id={pid}, imported_at=now() WHERE id={rid}")
            done+=1
            if done % 25 == 0: print(f'{done}/{len(rows)} created', flush=True)
            break
        except urllib.error.HTTPError as e:
            if e.code == 429:
                time.sleep(float(e.headers.get('Retry-After','3'))+1); continue
            print(f'ERR {dw} {handle}: {e.code} {e.read()[:200]}', flush=True); errs+=1; break
        except Exception as e:
            print(f'ERR {dw} {handle}: {e}', flush=True); time.sleep(5)
    time.sleep(0.55)
print(f'DONE created={done} errors={errs}', flush=True)