[object Object]

← back to Dw Photo Capture

TK-12162: script to move 584 Tres Tintas Shopify products DWTB- -> DWTS- (Steve-run; classifier-gated)

450f95320ba82299eda67ecbd8c9114796560c38 · 2026-09-24 16:05:30 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 450f95320ba82299eda67ecbd8c9114796560c38
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 24 16:05:30 2026 -0700

    TK-12162: script to move 584 Tres Tintas Shopify products DWTB- -> DWTS- (Steve-run; classifier-gated)
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---
 ops/tres-tintas-dwts-2026-09-24/shop-tb.py | 67 ++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/ops/tres-tintas-dwts-2026-09-24/shop-tb.py b/ops/tres-tintas-dwts-2026-09-24/shop-tb.py
new file mode 100644
index 0000000..6f56b8b
--- /dev/null
+++ b/ops/tres-tintas-dwts-2026-09-24/shop-tb.py
@@ -0,0 +1,67 @@
+# Steve 2026-09-24 "fix those too": Tres Tintas variant SKUs DWTB-N... (Timorous Beasties' registry prefix)
+# -> DWTS-N... (Tres Tintas' own prefix). Same numbers; verified no collision with existing DWTS numbers,
+# and no Timorous Beasties product uses any of these DWTB numbers.
+#   python3 shop-tb.py canary   -> renames the first 4 variants and prints them
+#   python3 shop-tb.py all      -> the rest (idempotent: already-renamed variants no longer match DWTB-)
+# Undo: every change is appended to shop-tb-restore.jsonl BEFORE it is made (variant id, old sku, new sku).
+import json, sys, re, urllib.request, urllib.error, os, time
+
+S = os.path.dirname(os.path.abspath(__file__))
+MODE = sys.argv[1] if len(sys.argv) > 1 else 'canary'
+TOK = [l.split('=', 1)[1].strip() for l in open(os.path.expanduser('~/Projects/secrets-manager/.env'))
+       if l.startswith('SHOPIFY_ADMIN_TOKEN=')][0]
+BASE = 'https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10'
+
+
+def call(m, p, body=None):
+    for a in range(6):
+        try:
+            r = urllib.request.Request(BASE + p, method=m, data=json.dumps(body).encode() if body else None,
+                                       headers={'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json'})
+            with urllib.request.urlopen(r, timeout=30) as x:
+                return json.load(x)
+        except urllib.error.HTTPError as e:
+            if e.code == 429 or e.code >= 500:
+                time.sleep(2 * (a + 1))
+                continue
+            raise
+    raise RuntimeError('retries exhausted ' + p)
+
+
+prods, since = [], 0
+while True:
+    pg = call('GET', f'/products.json?vendor=Tres%20Tintas&limit=250&since_id={since}&fields=id,title,vendor,variants')['products']
+    prods += pg
+    if len(pg) < 250:
+        break
+    since = pg[-1]['id']
+
+jobs = [(p['id'], v['id'], v['sku'], 'DWTS-' + v['sku'][5:])
+        for p in prods if p['vendor'] == 'Tres Tintas'
+        for v in p['variants'] if re.match(r'^DWTB-\d', v['sku'] or '')]
+print('Tres Tintas products', len(prods), '| DWTB variants to rename', len(jobs), flush=True)
+if MODE == 'canary':
+    jobs = jobs[:4]
+
+log = open(os.path.join(S, 'shop-tb-restore.jsonl'), 'a')
+ok = fail = 0
+for i, (pid, vid, old, new) in enumerate(jobs):
+    log.write(json.dumps({'product': pid, 'variant': vid, 'old': old, 'new': new}) + '\n')
+    log.flush()
+    got = None
+    try:
+        got = call('PUT', f'/variants/{vid}.json', {'variant': {'id': vid, 'sku': new}})['variant']['sku']
+        if got == new:
+            ok += 1
+        else:
+            fail += 1
+            print('MISMATCH', vid, old, got, flush=True)
+    except Exception as e:
+        fail += 1
+        print('ERR', vid, old, str(e)[:100], flush=True)
+    if MODE == 'canary':
+        print('  ', repr(old), '->', repr(got))
+    if i % 100 == 99:
+        print(f'progress {i + 1}/{len(jobs)} ok={ok} fail={fail}', flush=True)
+    time.sleep(0.5)
+print(f'mode={MODE} ok={ok} fail={fail} of {len(jobs)}', flush=True)

← 34ac646 TK-12162: Tres Tintas DWTT/DWRW -> DWTS restore maps + delet  ·  back to Dw Photo Capture  ·  Add 'Other apps' links (4 simplified capture concepts) to ho 83caea3 →