← back to Designer Wallcoverings
onboarding/sangetsu-lilycolor/scripts/sangetsu-colorway-recover.py
84 lines
#!/usr/bin/env python3
"""Sangetsu colorway_skus recovery from the free WooCommerce Store API.
The sitemap/page scraper left 238/623 patterns with ZERO colorway_skus (overview
only) and mis-extracted ~46 thin pages (grabbed one stray cross-listed SKU). The
Store API product object carries the authoritative colorway list under
attributes[] -> (has_variations | name~="collection") -> terms[].name
Validated 3/3 against patterns whose colorway_skus were already known (exact match,
0 known-only / 0 api-only), so the API is authoritative.
Rule: overwrite colorway_skus with the API extraction ONLY when the API returns a
non-empty list (never wipe good data on an API miss). Idempotent. $0, read-only
fetch + atomic rewrite. No dw_unified / Shopify write.
"""
import json, os, sys, time
HERE = os.path.dirname(os.path.abspath(__file__))
PROJ = os.path.dirname(HERE)
STAGING = os.path.join(PROJ, "staging", "sangetsu-staging.jsonl")
sys.path.insert(0, os.path.expanduser("~/Projects/japan-enrich"))
from scrape_sangetsu_specs import get, slug_of # noqa: E402
def extract_colorways(prod):
out, seen = [], set()
for a in (prod.get("attributes") or []):
if a.get("has_variations") or "collection" in (a.get("name", "").lower()):
for t in (a.get("terms") or []):
nm = (t.get("name") or "").strip()
if nm and nm not in seen:
seen.add(nm)
out.append(nm)
return out
def main():
rows = [json.loads(l) for l in open(STAGING) if l.strip()]
limit = int(sys.argv[sys.argv.index("--limit") + 1]) if "--limit" in sys.argv else len(rows)
empty_before = sum(1 for r in rows if not (r.get("colorway_skus") or []))
total_before = sum(len(r.get("colorway_skus") or []) for r in rows)
recovered = filled = replaced = miss = 0
for i, r in enumerate(rows[:limit]):
d = get(f"/products?slug={slug_of(r.get('source_url'))}")
if not d:
miss += 1
continue
api = extract_colorways(d[0])
if not api:
continue
old = [str(x).strip() for x in (r.get("colorway_skus") or [])]
if set(x.upper() for x in api) == set(x.upper() for x in old):
pass # already correct
else:
if not old:
filled += 1
else:
replaced += 1
r["colorway_skus"] = api
r["colorway_count"] = len(api)
recovered += 1
if i % 50 == 0:
print(f"[{i+1}/{limit}] {r.get('pattern')}: api={len(api)} old={len(old)}", flush=True)
time.sleep(0.15)
tmp = STAGING + ".tmp"
with open(tmp, "w") as f:
for r in rows:
f.write(json.dumps(r, ensure_ascii=False) + "\n")
os.replace(tmp, STAGING)
empty_after = sum(1 for r in rows if not (r.get("colorway_skus") or []))
total_after = sum(len(r.get("colorway_skus") or []) for r in rows)
print(json.dumps({
"patterns": len(rows), "store_api_miss": miss,
"changed": recovered, "empty_filled": filled, "thin_replaced": replaced,
"empty_patterns": f"{empty_before} -> {empty_after}",
"total_colorway_skus": f"{total_before} -> {total_after}",
}, indent=2))
if __name__ == "__main__":
main()