← back to Elitis Price 2026
exec/ws2_archive.py
88 lines
#!/usr/bin/env python3
"""WS2 — archive the 20 off-list disco Elitis products (VP-1048 + VP-1050).
Discontinued = ARCHIVE (never draft, never delete). Idempotent + resumable.
$0 (Shopify Admin API is free)."""
import csv, sys, os, json, datetime, time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "lib"))
from shopify import gql, find_product_by_title
LEDGER = os.path.join(os.path.dirname(__file__), "ledger", "ws2_archived.csv")
PLAN = os.path.join(os.path.dirname(__file__), "..", "PLAN_price_update.csv")
M_ARCHIVE = """mutation($input:ProductInput!){
productUpdate(input:$input){ userErrors{ field message }
product{ id status title } } }"""
def append_ledger(row):
exists = os.path.exists(LEDGER) and os.path.getsize(LEDGER) > 0
with open(LEDGER, "a", newline="") as f:
w = csv.writer(f)
if not exists:
w.writerow(["ts", "title", "product_id", "prev_status", "new_status"])
w.writerow(row)
def seen():
s = set()
if os.path.exists(LEDGER):
for r in csv.DictReader(open(LEDGER)):
if r.get("new_status") in ("ARCHIVED", "already-ARCHIVED"):
s.add(r["title"])
return s
def process(r):
ts = datetime.datetime.now().isoformat(timespec="seconds")
title = r["title"]
node = find_product_by_title(title)
if not node:
append_ledger([ts, title, "", "", "NOT-FOUND"])
return "NOT-FOUND"
pid = str(node["legacyResourceId"]); prev = node["status"]
if prev == "ARCHIVED":
append_ledger([ts, title, pid, prev, "already-ARCHIVED"])
return "already-ARCHIVED"
d = gql(M_ARCHIVE, {"input": {"id": node["id"], "status": "ARCHIVED"}})
ue = d["productUpdate"]["userErrors"]
if ue:
append_ledger([ts, title, pid, prev, "FAIL:" + json.dumps(ue)[:120]])
return "FAIL"
new = d["productUpdate"]["product"]["status"]
append_ledger([ts, title, pid, prev, new])
return new
if __name__ == "__main__":
plan = list(csv.DictReader(open(PLAN)))
disco = [r for r in plan if r["action"].startswith("DISCONTINUE")]
done = seen()
canary = "--canary" in sys.argv
if canary:
r = disco[0]
print("=== WS2 CANARY archive:", r["title"], "===")
# verify before
n = find_product_by_title(r["title"])
print("BEFORE status:", n["status"] if n else "NOT-FOUND", "| id:",
n["legacyResourceId"] if n else None)
st = process(r)
n2 = find_product_by_title(r["title"])
print("AFTER status:", n2["status"] if n2 else "NOT-FOUND")
print("RESULT:", st)
sys.exit(0)
n_arch = n_already = n_fail = n_missing = 0
for r in disco:
if r["title"] in done:
n_already += 1; continue
st = process(r)
if st == "ARCHIVED":
n_arch += 1
elif st == "already-ARCHIVED":
n_already += 1
elif st == "NOT-FOUND":
n_missing += 1
else:
n_fail += 1
time.sleep(0.15)
print(f"WS2 DONE: archived={n_arch} already={n_already} missing={n_missing} fail={n_fail} total={len(disco)}")