← back to Designer Wallcoverings
add color/motif Wallcovering-collection rename script (DTD verdict A)
904dc48f1c1e448bcd5529b067c777b52abc0217 · 2026-07-07 13:03:48 -0700 · Steve
Files touched
A scripts/rename-wc-color-collections.py
Diff
commit 904dc48f1c1e448bcd5529b067c777b52abc0217
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 7 13:03:48 2026 -0700
add color/motif Wallcovering-collection rename script (DTD verdict A)
---
scripts/rename-wc-color-collections.py | 70 ++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/scripts/rename-wc-color-collections.py b/scripts/rename-wc-color-collections.py
new file mode 100644
index 00000000..4942b457
--- /dev/null
+++ b/scripts/rename-wc-color-collections.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+"""
+Rename color/motif "…Wallcovering…" smart collections to drop "Wallcovering".
+DTD verdict A (2026-07-07, unanimous 3/3) + Steve scope choice "Color + motif (39)".
+
+These collections key on a COLOR or cross-type MOTIF tag (ANY mode, no product_type
+rule), so they legitimately contain both wallcoverings AND fabrics — the "Wallcovering"
+in the title is a mislabel. Rename is TITLE-ONLY: rulesets + handles/URLs untouched,
+so membership + SEO are preserved. Vendor/material/style collections are NOT in scope.
+
+Usage: python3 rename-wc-color-collections.py [--apply] (default = dry run)
+Targets come from /tmp/wc-rename-targets.json ({color:[...], motif:[...]}).
+"""
+import os, sys, re, json, time, urllib.request, urllib.error
+
+STORE = os.environ["SHOPIFY_STORE_DOMAIN"]; TOK = os.environ["SHOPIFY_ADMIN_TOKEN"]; VER = "2024-10"
+APPLY = "--apply" in sys.argv
+
+def gql(q, v=None):
+ for a in range(6):
+ req = urllib.request.Request(f"https://{STORE}/admin/api/{VER}/graphql.json",
+ data=json.dumps({"query": q, "variables": v or {}}).encode(),
+ headers={"X-Shopify-Access-Token": TOK, "Content-Type": "application/json"})
+ try: d = json.load(urllib.request.urlopen(req))
+ except urllib.error.HTTPError as e:
+ if e.code == 429: time.sleep(2*(a+1)); continue
+ raise
+ if d.get("errors") and any("THROTTLED" in json.dumps(x) for x in d["errors"]):
+ time.sleep(2*(a+1)); continue
+ return d
+ raise RuntimeError("throttled")
+
+def new_title(t):
+ s = t
+ if "and Fabric" in s: # "Purple Wallcovering and Fabric Patterns" -> "Purple Patterns"
+ s = re.sub(r"\bWallcovering and Fabric\b", "", s)
+ s = re.sub(r"\bWallcovering Collections\b", "Collections", s)
+ s = re.sub(r"\bWallcovering Collection\b", "Collection", s)
+ s = re.sub(r"\bWallcoverings\b", "", s)
+ s = re.sub(r"\bWallcovering\b", "", s)
+ s = re.sub(r"\s+", " ", s).strip()
+ if not re.search(r"\b(Collection|Collections|Patterns|Murals)\b", s):
+ s = s + " Collection"
+ return re.sub(r"\s+", " ", s).strip()
+
+tg = json.load(open("/tmp/wc-rename-targets.json"))
+targets = tg["color"] + tg["motif"]
+M = "mutation($input:CollectionInput!){ collectionUpdate(input:$input){ collection{ id title } userErrors{ field message } } }"
+
+print(f"{'OLD TITLE':<48} -> NEW TITLE")
+plan = []
+for c in targets:
+ nt = new_title(c["title"])
+ if nt != c["title"]:
+ plan.append((c, nt))
+ print(f" {c['title']:<46} -> {nt}")
+print(f"\n{len(plan)} collections to rename (of {len(targets)} targets).")
+
+if not APPLY:
+ print("DRY RUN. Re-run with --apply to write to the LIVE store.")
+ sys.exit(0)
+
+ok = err = 0
+for c, nt in plan:
+ r = gql(M, {"input": {"id": c["id"], "title": nt}})
+ ue = r.get("data", {}).get("collectionUpdate", {}).get("userErrors") if r.get("data") else r.get("errors")
+ if ue: err += 1; print(f" ERR {c['title']}: {ue}")
+ else: ok += 1
+ time.sleep(0.3)
+print(f"\nDONE. renamed ok={ok} err={err} of {len(plan)}")
← 5cdb9b92 auto-save: 2026-07-07T13:00:27 (6 files) — pending-approval/
·
back to Designer Wallcoverings
·
add collection dedup script (backup+delete+301 redirect+reti 3e87194c →