← back to Sidmouth Material Fix
fix_generic.py
53 lines
#!/usr/bin/env python3
"""Generic custom.material fixer. Usage:
python3 fix_generic.py <targets.tsv> "<NEW VALUE>" [--apply]
targets.tsv rows: pid|sku|status|pattern (only first two cols used)
Snapshots prior values to rollback_<targets>.json. DRY-RUN unless --apply.
"""
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"
TARGETS_FILE = sys.argv[1]
NEW_VALUE = sys.argv[2]
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 "{}")
rows = [l.split("|") for l in (ROOT/TARGETS_FILE).read_text().splitlines() if l.strip()]
snapshot, results = [], []
print(f"{'APPLY' if APPLY else 'DRY-RUN'} — {len(rows)} products -> Material {NEW_VALUE!r}\n")
for r in rows:
pid, sku = r[0], r[1]
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("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})
if not APPLY:
print(f" {sku:22} was {old!r}"); results.append("dry"); continue
try:
if mat:
req("PUT", f"metafields/{mat['id']}.json",
{"metafield":{"id":mat["id"],"value":NEW_VALUE,"type":mat.get("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("written")
except urllib.error.HTTPError as e:
print(f" {sku:22} PUT FAIL {e.code} {e.read()[:120]}"); results.append("put_fail")
time.sleep(0.55)
rb = ROOT/f"rollback_{pathlib.Path(TARGETS_FILE).stem}.json"
rb.write_text(json.dumps(snapshot, indent=2))
from collections import Counter
print(f"\nSnapshot -> {rb.name}. Summary:", dict(Counter(results)))