← back to Designer Wallcoverings
TK-10055: canary de Gournay live (works via vendors?q surface); harden smart/custom writes + published-filter + rollback
326e44e7b599b1b0c842f7583c5874bdd9f8ac55 · 2026-07-30 11:23:36 -0700 · collection-agent
Files touched
M shopify/collection-hero-fix/tk10055-free-promote/promote-heroes.py
Diff
commit 326e44e7b599b1b0c842f7583c5874bdd9f8ac55
Author: collection-agent <steve@designerwallcoverings.com>
Date: Thu Jul 30 11:23:36 2026 -0700
TK-10055: canary de Gournay live (works via vendors?q surface); harden smart/custom writes + published-filter + rollback
---
.../tk10055-free-promote/promote-heroes.py | 81 ++++++++++++++++------
1 file changed, 59 insertions(+), 22 deletions(-)
diff --git a/shopify/collection-hero-fix/tk10055-free-promote/promote-heroes.py b/shopify/collection-hero-fix/tk10055-free-promote/promote-heroes.py
index 1c1df38c..f3c34ded 100644
--- a/shopify/collection-hero-fix/tk10055-free-promote/promote-heroes.py
+++ b/shopify/collection-hero-fix/tk10055-free-promote/promote-heroes.py
@@ -96,44 +96,81 @@ def scan():
print(f"-> {len(swatch)} have hi-res but square/portrait swatches: LEFT on mosaic (would downgrade)")
print(f"-> {len(none_)} have no >=1400px image: mosaic (or future generation)")
+EXCLUDE = {"1838"} # pre-screen rejects (paint-pail image, not a wallcovering)
+
+def _endpoint(res):
+ # res is 'custom_collections' or 'smart_collections' (from report.json)
+ return ("custom_collection", res) if res.startswith("custom") else ("smart_collection", res)
+
+def _set_image(res, cid, src):
+ key, path = _endpoint(res)
+ return req("PUT", f"{path}/{cid}.json", {key: {"id": cid, "image": {"src": src}}})
+
+def _find(handle):
+ rep = json.load(open(os.path.join(HERE, "report.json")))
+ for c in rep["hero_worthy"]:
+ if c["handle"] == handle: return c
+ sys.exit(f"{handle} not in report.json hero_worthy")
+
def canary(handle):
- c = req("GET", f"custom_collections.json?handle={handle}&fields=id,image") or {}
- cc = c.get("custom_collections") or []
- if not cc:
- cc = (req("GET", f"smart_collections.json?handle={handle}&fields=id,image") or {}).get("smart_collections", [])
- if not cc: sys.exit(f"collection {handle} not found")
- cid = cc[0]["id"]
- b = best_hero(cid)
- if not b or not b[2]: sys.exit("no hero-worthy image to canary with")
- before = req("GET", "files.json?limit=1") # existence check only
- print(f"CANARY: setting {handle} image -> {b[3][:80]} ({b[0]}px ratio {b[1]})")
- res_key = "custom_collections" if cc else "smart_collections"
- # NOTE: this is a PROD write — only runs when explicitly invoked with --canary
- req("PUT", f"custom_collections/{cid}.json", {"custom_collection": {"id": cid, "image": {"src": b[3]}}})
+ c = _find(handle)
+ print(f"CANARY: {handle} ({c['res']}) -> {c['src'][:80]} ({c['w']}px ratio {c['ratio']})")
+ key, path = _endpoint(c["res"])
+ _set_image(c["res"], c["id"], c["src"])
time.sleep(3)
- after = req("GET", f"custom_collections/{cid}.json?fields=image")
- newimg = after.get("custom_collection", {}).get("image", {})
+ after = req("GET", f"{path}/{c['id']}.json?fields=image")
+ newimg = after.get(key, {}).get("image", {})
print("new collection.image src:", newimg.get("src"))
print("new collection.image w/h:", newimg.get("width"), "x", newimg.get("height"))
- print(">> Confirm in Shopify admin Files whether a NEW /collections/ asset was created and its size.")
+ print(f">> live: https://www.designerwallcoverings.com/collections/{handle}")
def promote(limit):
rep = json.load(open(os.path.join(HERE, "report.json")))
- todo = rep["hero_worthy"][:limit] if limit else rep["hero_worthy"]
+ todo = [c for c in rep["hero_worthy"] if c["handle"] not in EXCLUDE]
+ if limit: todo = todo[:limit]
os.makedirs(os.path.join(HERE, "backups"), exist_ok=True)
+ done = 0; skipped_unpub = []
for c in todo:
- cur = req("GET", f"collections/{c['id']}.json?fields=image")
+ key, path = _endpoint(c["res"])
+ cur = req("GET", f"{path}/{c['id']}.json?fields=image,published_at")
+ col = cur.get(key, {})
+ if not col.get("published_at"):
+ skipped_unpub.append(c["handle"]); continue # no live surface -> don't waste a file
json.dump(cur, open(os.path.join(HERE, "backups", f"{c['handle']}.json"), "w"))
- req("PUT", f"custom_collections/{c['id']}.json",
- {"custom_collection": {"id": c["id"], "image": {"src": c["src"]}}})
- print(f"promoted {c['handle']} ({c['w']}px ratio {c['ratio']})")
+ _set_image(c["res"], c["id"], c["src"])
+ done += 1
+ print(f"promoted {c['handle']} ({c['res'].split('_')[0]}, {c['w']}px ratio {c['ratio']})")
time.sleep(0.6)
- print(f"done: promoted {len(todo)} collections")
+ print(f"done: promoted {done} collections")
+ print(f"excluded (pre-screen): {sorted(EXCLUDE)}")
+ print(f"skipped (unpublished, no live surface): {len(skipped_unpub)} -> {skipped_unpub}")
+
+def rollback():
+ """Restore every promoted collection.image from backups/ (instant revert)."""
+ bdir = os.path.join(HERE, "backups")
+ if not os.path.isdir(bdir): sys.exit("no backups/ dir")
+ rep = json.load(open(os.path.join(HERE, "report.json")))
+ by_handle = {c["handle"]: c for c in rep["hero_worthy"]}
+ n = 0
+ for fn in os.listdir(bdir):
+ if not fn.endswith(".json"): continue
+ handle = fn[:-5]; c = by_handle.get(handle)
+ if not c: continue
+ prev = json.load(open(os.path.join(bdir, fn)))
+ key, path = _endpoint(c["res"])
+ img = (prev.get(key) or {}).get("image")
+ # restore prior image src, or clear it if there was none
+ body = {key: {"id": c["id"], "image": ({"src": img["src"]} if img and img.get("src") else None)}}
+ req("PUT", f"{path}/{c['id']}.json", body); n += 1
+ print(f"reverted {handle}")
+ time.sleep(0.4)
+ print(f"rolled back {n} collections")
if __name__ == "__main__":
a = sys.argv[1:]
if not a or a[0] == "--scan": scan()
elif a[0] == "--canary" and len(a) > 1: canary(a[1])
+ elif a[0] == "--rollback": rollback()
elif a[0] == "--promote":
lim = int(a[a.index("--limit")+1]) if "--limit" in a else 0
promote(lim)
← d5d01e09 auto-save: 2026-07-30T11:17:19 (12 files) — shopify/scripts/
·
back to Designer Wallcoverings
·
TK-10055: promoted 39 published collections to single flagsh 61c3ea70 →