← 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)))