← back to Sidmouth Material Fix

fix_material.py

62 lines

#!/usr/bin/env python3
"""Fix custom.material on the Sidmouth Acoustical (Momentum 'Silence') line.
'Type II Vinyl Wallcovering' -> '50% Polyester / 50% Polypropylene'.
DRY-RUN by default; pass --apply to write to live Shopify.
Snapshots every prior value to rollback.json for reversibility.
"""
import json, sys, time, urllib.request, urllib.error, pathlib

ROOT = pathlib.Path(__file__).parent
TOKEN = (ROOT/".token").read_text().strip()
STORE = (ROOT/".store").read_text().strip()
API = "2024-10"
NEW_VALUE = "50% Polyester / 50% Polypropylene"
WRONG = "Type II Vinyl Wallcovering"
APPLY = "--apply" in sys.argv

def req(method, path, body=None):
    url = f"https://{STORE}/admin/api/{API}/{path}"
    data = json.dumps(body).encode() if body else None
    r = urllib.request.Request(url, data=data, method=method,
        headers={"X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json"})
    with urllib.request.urlopen(r) as resp:
        return json.loads(resp.read() or "{}")

targets = [l.split("|") for l in (ROOT/"targets.tsv").read_text().splitlines() if l.strip()]
snapshot, results = [], []
print(f"{'APPLY' if APPLY else 'DRY-RUN'} — {len(targets)} products, target Material = {NEW_VALUE!r}\n")

for pid, sku in targets:
    try:
        mfs = req("GET", f"products/{pid}/metafields.json").get("metafields", [])
    except urllib.error.HTTPError as e:
        print(f"  {sku:22} GET FAIL {e.code}"); results.append((sku,"get_fail")); continue
    mat = next((m for m in mfs if m["namespace"]=="custom" and m["key"]=="material"), None)
    old = mat["value"] if mat else None
    snapshot.append({"pid":pid,"sku":sku,"metafield_id":mat["id"] if mat else None,"old_value":old})
    tag = "WRONG" if old==WRONG else ("already-ok" if old==NEW_VALUE else f"other:{old!r}")
    if not APPLY:
        print(f"  {sku:22} custom.material={old!r}  [{tag}]")
        results.append((sku,tag)); continue
    # APPLY
    try:
        if mat:
            req("PUT", f"metafields/{mat['id']}.json",
                {"metafield":{"id":mat["id"],"value":NEW_VALUE,"type":"multi_line_text_field"}})
        else:
            req("POST", f"products/{pid}/metafields.json",
                {"metafield":{"namespace":"custom","key":"material","value":NEW_VALUE,"type":"multi_line_text_field"}})
        print(f"  {sku:22} {old!r} -> {NEW_VALUE!r}  ✓")
        results.append((sku,"written"))
    except urllib.error.HTTPError as e:
        print(f"  {sku:22} PUT FAIL {e.code} {e.read()[:120]}"); results.append((sku,"put_fail"))
    time.sleep(0.55)  # Shopify REST ~2/sec

# always write/update the rollback snapshot on dry-run (captures pre-change state)
if not APPLY:
    (ROOT/"rollback.json").write_text(json.dumps(snapshot, indent=2))
    print(f"\nSnapshot -> rollback.json ({len(snapshot)} rows)")

from collections import Counter
print("\nSummary:", dict(Counter(r[1] for r in results)))