← back to Novasuede Onboard

tweak_two.py

56 lines

#!/usr/bin/env python3
"""Micro-tweak two colorway names that the swatch verification refined:
   Terracotta: Oatmeal -> Camel   (swatch reads warmer than oatmeal)
   Olive:      Mocha   -> Loden   (dark drab green-brown; keep the olive cast)
Changes the `Color:` tag, the bare colorway tag, AND the body text together so
tag and prose never disagree (the 2026-06-24 QA lesson). Backs up to the same
rollback.ndjson FIRST. Dry-run unless --commit.
"""
import sys, json, re, time, urllib.request, pathlib
ROOT = pathlib.Path(__file__).resolve().parent; DATA = ROOT/"data"
env = pathlib.Path.home()/"Projects/secrets-manager/.env"
TOKEN = next(l.split("=",1)[1].strip().strip('"') for l in env.read_text().splitlines()
             if l.startswith("SHOPIFY_ADMIN_TOKEN="))
API = "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01"
COMMIT = "--commit" in sys.argv

TWEAKS = {7821431046195: ("Oatmeal","Camel"),     # Terracotta
          7692852887603: ("Mocha","Loden")}       # Olive

def req(method, path, body=None):
    data = json.dumps(body).encode() if body is not None else None
    r = urllib.request.Request(API+path, data=data, method=method,
        headers={"X-Shopify-Access-Token":TOKEN,"Content-Type":"application/json"})
    for _ in range(6):
        try:
            with urllib.request.urlopen(r, timeout=30) as resp: return json.load(resp)
        except urllib.error.HTTPError as e:
            if e.code==429: time.sleep(2.5); continue
            raise
    raise RuntimeError("retries exhausted")

def retag(tags, old, new):
    out=[]
    for t in (x.strip() for x in tags.split(",")):
        if t == old: out.append(new)                         # bare colorway tag
        elif t == f"Color: {old}": out.append(f"Color: {new}")  # primary color tag
        else: out.append(t)
    return ", ".join(out)

rb = (DATA/"rollback.ndjson").open("a") if COMMIT else None
for pid,(old,new) in TWEAKS.items():
    cur = req("GET", f"/products/{pid}.json?fields=id,title,tags,body_html")["product"]
    new_tags = retag(cur["tags"], old, new)
    # whole-word, case-sensitive body swap (handles description + the "Color: X" trailer)
    new_body = re.sub(rf"\b{re.escape(old)}\b", new, cur["body_html"])
    print(f"\n# {cur['title']}  ({old} -> {new})")
    print("  tags Color: ->", [t for t in new_tags.split(", ") if t.startswith("Color:")])
    print("  body[:140] ->", re.sub('<[^>]+>',' ',new_body).strip()[:140])
    if COMMIT:
        rb.write(json.dumps({"id":cur["id"],"tags":cur["tags"],"body_html":cur["body_html"]})+"\n"); rb.flush()
        req("PUT", f"/products/{pid}.json",
            {"product":{"id":pid,"tags":new_tags,"body_html":new_body}})
        print("  ✓ written"); time.sleep(0.6)
if rb: rb.close()
print(f"\n{'COMMITTED' if COMMIT else 'DRY RUN — no writes'}")