← back to Hollywood Price 2026

archive_267.py

91 lines

#!/usr/bin/env python3
"""Archive the Hollywood products with no live Momentum match (Steve 2026-07-12,
'archive gone, price live' split) + reflect in dw_unified mirror.
Reads archive_final.csv. Shopify: status->ARCHIVED. dw_unified.shopify_products:
status->ARCHIVED (shopify_id = gid). Idempotent (ledger + skip already-archived).
Reversible (un-archive). $0."""
import sys
import os
import csv
import json
import glob
import re
import subprocess
import datetime
import time
sys.path.insert(0, "lib")
from shopify import gql

CSV = "archive_final.csv"
LEDGER = os.path.join("ledger", "archived.csv")
M_UPD = """mutation($input:ProductInput!){ productUpdate(input:$input){
  product{ id status } userErrors{ field message } } }"""
Q = "query($id:ID!){ product(id:$id){ status } }"


def conn():
    for f in glob.glob(os.path.expanduser("~/Projects/hollywood-import/**/.env"), recursive=True) + \
             glob.glob(os.path.expanduser("~/Projects/hollywood-import/*.env")):
        for line in open(f):
            m = re.search(r"postgresql://dw_admin:[^\s\"']+@127\.0\.0\.1:5432/dw_unified", line)
            if m:
                return m.group(0)
    return None


def log(pid, title, status):
    new = not (os.path.exists(LEDGER) and os.path.getsize(LEDGER) > 0)
    with open(LEDGER, "a", newline="") as f:
        w = csv.writer(f)
        if new:
            w.writerow(["ts", "product_id", "title", "status"])
        w.writerow([datetime.datetime.now().isoformat(timespec="seconds"), pid, title, status])


def seen():
    s = set()
    if os.path.exists(LEDGER):
        for r in csv.DictReader(open(LEDGER)):
            if r["status"].startswith("OK") and r.get("product_id"):
                s.add(r["product_id"])
    return s


def main():
    rows = list(csv.DictReader(open(CSV)))
    done = seen()
    ok = skip = fail = 0
    archived_gids = []
    for i, r in enumerate(rows):
        pid = str(r["product_id"]); gid = r["gid"]
        if pid in done:
            skip += 1; continue
        cur = gql(Q, {"id": gid})["product"]
        if not cur:
            log(pid, r["title"], "SKIP:not-found"); skip += 1; continue
        if cur["status"] == "ARCHIVED":
            log(pid, r["title"], "OK:already-archived"); archived_gids.append(gid); ok += 1; continue
        d = gql(M_UPD, {"input": {"id": gid, "status": "ARCHIVED"}})
        ue = d["productUpdate"]["userErrors"]
        if ue:
            log(pid, r["title"], "FAIL:" + json.dumps(ue)[:120]); fail += 1; continue
        log(pid, r["title"], "OK"); archived_gids.append(gid); ok += 1
        if (i + 1) % 25 == 0:
            print(f"  ...{i+1}/{len(rows)} ok={ok} skip={skip} fail={fail}")
        time.sleep(0.15)
    print(f"SHOPIFY ARCHIVE DONE: ok={ok} skip={skip} fail={fail}")

    # reflect in dw_unified mirror
    c = conn()
    if c and archived_gids:
        vals = ",".join("'" + g.replace("'", "") + "'" for g in archived_gids)
        sql = f"UPDATE shopify_products SET status='ARCHIVED' WHERE shopify_id IN ({vals});"
        r = subprocess.run(["psql", c, "-tAc", sql], capture_output=True, text=True)
        print("dw_unified update:", (r.stdout or r.stderr).strip()[:120])
    else:
        print("dw_unified: skipped (no conn or nothing archived)")


if __name__ == "__main__":
    main()