← back to Pj Image Repair

rollback_images.py

64 lines

#!/usr/bin/env python3
"""
TK-10467 — rollback for the PJ logo-hero image canary.
Reads data/pj_write_journal.jsonl 'post' records (product_id + added_image_id) and DELETEs
each image we added. Because the canary left the logo in place, deleting the one added
webdamdb image returns the PhillipJeffriesLogo_*.png to position 1 — a clean, complete undo.

DRY-RUN by default; requires --apply AND env CONFIRM_PJ_WRITE=1 (same gate as the writer).
"""
import os, sys, json, time, urllib.request, urllib.error

HERE = os.path.dirname(os.path.abspath(__file__))
JRN  = os.path.join(HERE, "data", "pj_write_journal.jsonl")
SHOP = "designer-laboratory-sandbox.myshopify.com"
API  = "2024-10"
APPLY = "--apply" in sys.argv

def token():
    envp = os.path.expanduser("~/Projects/secrets-manager/.env")
    vals = {}
    with open(envp) as f:
        for line in f:
            for k in ("SHOPIFY_ADMIN_TOKEN", "SHOPIFY_FULL_ACCESS_TOKEN"):
                if line.startswith(k + "="):
                    vals[k] = line.split("=", 1)[1].strip().strip('"').strip("'")
    tok = vals.get("SHOPIFY_ADMIN_TOKEN") or vals.get("SHOPIFY_FULL_ACCESS_TOKEN")
    if not tok:
        raise SystemExit("no SHOPIFY_ADMIN_TOKEN / SHOPIFY_FULL_ACCESS_TOKEN")
    return tok

TOK = token()

def api(method, path):
    url = f"https://{SHOP}/admin/api/{API}/{path}"
    req = urllib.request.Request(url, method=method,
        headers={"X-Shopify-Access-Token": TOK, "Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=60) as r:
        return r.getcode()

def main():
    if APPLY and os.environ.get("CONFIRM_PJ_WRITE") != "1":
        print("REFUSING: --apply requires env CONFIRM_PJ_WRITE=1. Aborting.")
        sys.exit(2)
    posts = [json.loads(l) for l in open(JRN) if '"added_image_id"' in l]
    print(f"mode={'APPLY' if APPLY else 'DRY-RUN'} rollback_records={len(posts)}")
    ok = err = 0
    for i, d in enumerate(posts, 1):
        pid, iid = d["product_id"], d["added_image_id"]
        if not APPLY:
            print(f"[{i}] DRY would DELETE products/{pid}/images/{iid}.json ({d.get('dw_sku')})")
            continue
        try:
            api("DELETE", f"products/{pid}/images/{iid}.json")
            ok += 1
            print(f"[{i}] OK deleted image {iid} on {pid} ({d.get('dw_sku')})")
        except Exception as e:
            err += 1
            print(f"[{i}] ERR {pid}/{iid}: {e}")
        time.sleep(0.4)
    print(f"done ok={ok} err={err}" + ("" if APPLY else " (DRY-RUN)"))

if __name__ == "__main__":
    main()