← back to Tk 11331 Exec
build_d3a_manifest.py
176 lines
#!/usr/bin/env python3
"""
TK-11331 Decision-3a reprice manifest builder (READ-ONLY, deterministic, reproducible).
Rebuilds the momentum_colorways -> ACTIVE X-prefix Shopify product join that a prior
session computed but never persisted (momentum_colorways.shopify_product_id is 100% NULL).
INPUTS (all read-only):
data/candidates.tsv pid|sku|width (3,600 ACTIVE X-prefix products)
data/enum.jsonl live Shopify reads (status, variants+price, uom, width, min)
/tmp/mirror_x.tsv dw_unified mirror dump: shopify_id\tdw_sku\tmfr_sku\tpattern_name\tvendor
/tmp/momentum.tsv momentum_colorways dump: alt_sku\tmomentum_sku\tcolor_number\tpattern_number\thw_price\tpattern_name\tcolor_name
To regenerate the two /tmp dumps from the local mirror socket (host=/tmp):
psql -h /tmp -d dw_unified -F $'\t' -A -t -c \
"SELECT shopify_id, dw_sku, coalesce(mfr_sku,''), coalesce(pattern_name,''), coalesce(vendor,'') \
FROM shopify_products WHERE dw_sku LIKE 'X%'" > /tmp/mirror_x.tsv
psql -h /tmp -d dw_unified -F $'\t' -A -t -c \
"SELECT coalesce(alt_sku,''), coalesce(momentum_sku,''), coalesce(color_number,''), \
coalesce(pattern_number,''), coalesce(hw_price::text,''), pattern_name, color_name \
FROM momentum_colorways" > /tmp/momentum.tsv
SCOPE / CLASSES: reprice-needed NON-YARD classes only, by uom value in
{null, 'Full Roll', 'Sold Per None', 'Sold Per EA'}. ('Sold Per Yard*' rows are already
per-yard and are excluded.)
JOIN METHODS (first hit wins, deterministic order):
1. exact-mfr-alt mfr_sku == momentum.alt_sku
2. tail-match-alt segment after last '_' in mfr_sku == momentum.alt_sku
3. code-map-momentum_sku mfr_sku == momentum.momentum_sku
4. code-map-color_number mfr_sku == momentum.color_number
GUARDRAIL (all must hold, else EXCLUDE with reason):
- exactly ONE momentum row for the matched key (else ambiguous:N)
- hw_price non-null and > 0
- 15.0 <= hw_price <= 72.0 (per-yard retail band)
- a sellable (non "-sample") variant exists on the product
- XCD-69430 is MUST-QUOTE/MDC -> hard-excluded
OUTPUTS (FILE artifacts only -- NO writes to dw_unified or Shopify):
data/d3a-manifest.jsonl one row per confidently-mapped product (reprice + rollback source)
data/d3a-skips.jsonl excluded products + reason
"""
import json, collections, os
HERE = os.path.dirname(os.path.abspath(__file__))
DATA = os.path.join(HERE, "data")
MIRROR = "/tmp/mirror_x.tsv"
MOMENTUM = "/tmp/momentum.tsv"
NONYARD = {None, "Full Roll", "Sold Per None", "Sold Per EA"}
MUSTQUOTE = {"XCD-69430"}
BAND_LO, BAND_HI = 15.0, 72.0
# --- momentum key indexes ---
mom = []
for l in open(MOMENTUM):
p = l.rstrip("\n").split("\t")
if len(p) < 7:
continue
alt, msku, cnum, pnum, hw, pat, col = p
mom.append(dict(alt=alt.strip(), msku=msku.strip(), cnum=cnum.strip(),
hw=float(hw) if hw else None, pat=pat, col=col))
def idx(field):
d = collections.defaultdict(list)
for r in mom:
if r[field]:
d[r[field]].append(r)
return d
by_alt, by_msku, by_cnum = idx("alt"), idx("msku"), idx("cnum")
# --- mirror mfr_sku by shopify gid ---
mfr_by_gid = {}
for l in open(MIRROR):
p = l.rstrip("\n").split("\t")
if len(p) < 5:
continue
mfr_by_gid[p[0]] = p[2].strip()
# --- live Shopify enum ---
enum = {json.loads(l)["pid"]: json.loads(l) for l in open(os.path.join(DATA, "enum.jsonl"))}
cand = [l.rstrip("\n").split("|") for l in open(os.path.join(DATA, "candidates.tsv"))]
tail = lambda m: m.rsplit("_", 1)[-1] if "_" in m else m
def sellable(d):
vs = [v for v in d["variants"] if not (v["sku"] or "").lower().endswith("-sample")]
if not vs:
return None
vs.sort(key=lambda v: v.get("position", 99))
return vs[0]
man = open(os.path.join(DATA, "d3a-manifest.jsonl"), "w")
skp = open(os.path.join(DATA, "d3a-skips.jsonl"), "w")
meth = collections.Counter(); skipc = collections.Counter()
conf = 0; price_flags = []
for pid, sku, width in cand:
d = enum.get(pid)
if not d:
continue
uom = d.get("uom"); uv = (uom or {}).get("value") if uom else None
if uv not in NONYARD:
continue
gid = f"gid://shopify/Product/{pid}"
mfr = mfr_by_gid.get(gid, "")
base = sku.rsplit("-yard", 1)[0] if sku.endswith("-yard") else sku.rsplit("-", 1)[0]
sv = sellable(d)
cur = float(sv["price"]) if sv else None
rb = dict(shopify_product_id=pid, sku=sku, dw_sku=base, mfr_sku=mfr,
current_price=cur, uom=uv, width=width)
if base in MUSTQUOTE or sku in MUSTQUOTE:
skipc["must-quote-mdc"] += 1
skp.write(json.dumps({**rb, "reason": "must-quote-mdc (XCD-69430 excluded per directive)"}) + "\n")
continue
t = tail(mfr); hit = None; m = None
if mfr and mfr in by_alt:
hit, m = by_alt[mfr], "exact-mfr-alt"
elif t and t in by_alt:
hit, m = by_alt[t], "tail-match-alt"
elif mfr and mfr in by_msku:
hit, m = by_msku[mfr], "code-map-momentum_sku"
elif mfr and mfr in by_cnum:
hit, m = by_cnum[mfr], "code-map-color_number"
if not hit:
skipc["no-momentum-match"] += 1
skp.write(json.dumps({**rb, "reason": "no-momentum-match"}) + "\n")
continue
if len(hit) > 1:
skipc["ambiguous-multi-row"] += 1
skp.write(json.dumps({**rb, "reason": f"ambiguous:{len(hit)}-momentum-rows"}) + "\n")
continue
r = hit[0]; hw = r["hw"]
if hw is None or hw <= 0:
skipc["hw-null-or-zero"] += 1
skp.write(json.dumps({**rb, "reason": f"hw_price null/zero ({hw})", "source_momentum_pattern": r["pat"]}) + "\n")
continue
if not (BAND_LO <= hw <= BAND_HI):
skipc["out-of-band"] += 1
skp.write(json.dumps({**rb, "reason": f"hw_price {hw} outside ${BAND_LO:.0f}-{BAND_HI:.0f}/yd band", "source_momentum_pattern": r["pat"]}) + "\n")
continue
if sv is None:
skipc["no-sellable-variant"] += 1
skp.write(json.dumps({**rb, "reason": "no sellable variant found", "source_momentum_pattern": r["pat"]}) + "\n")
continue
conf += 1; meth[m] += 1
if cur is not None and abs(cur - 4.25) > 0.001 and abs(cur - hw) > 0.001:
price_flags.append((sku, cur, hw, r["pat"]))
man.write(json.dumps(dict(
shopify_product_id=pid,
variant_id=sv["id"],
sku=sv["sku"],
current_price=cur,
source_momentum_pattern=r["pat"],
source_momentum_color=r["col"],
source_momentum_alt_sku=r["alt"],
new_price=hw,
hw_price=hw,
join_method=m,
mfr_sku=mfr,
uom=uv,
width=width,
)) + "\n")
man.close(); skp.close()
print("confidently-mapped:", conf)
print("join-method breakdown:", dict(meth))
print("skip breakdown:", dict(skipc), "total skipped:", sum(skipc.values()))
print("price-flags (current != $4.25 and != hw_price):", len(price_flags))
for s, c, h, p in sorted(price_flags, key=lambda x: -x[1]):
print(f" {s}: current=${c} target_hw=${h} [{p}]")