← back to Eur Recrawl

build_reonboard.py

97 lines

#!/usr/bin/env python3
"""
Build the EUR- reonboard dry-run from the parsed trade price list.

For each ACTIVE live EUR- product, resolve its trade price from prices.csv
(exact base+colorway, else the pattern's uniform single price), compute
retail = trade x1.810, and stage it. Uses the KNOWN series — the existing EUR-
SKU — adding a sellable roll variant later via productVariantsBulkCreate
(sample preserved). No writes here; emits reonboard.csv + reonboard_held.csv.
"""
import csv, os, re, subprocess, collections

HERE = os.path.dirname(os.path.abspath(__file__))
MARKUP = 1 / 0.65 / 0.85

def norm_cw(code):
    if "-" in code:
        m = re.match(r'^(\d+)', code.split("-", 1)[1])
        return m.group(1).zfill(2) if m else None
    return None

def main():
    rows = list(csv.DictReader(open(os.path.join(HERE, "prices.csv"))))
    priced = {}                                   # (base, cw2) -> row
    by_base = collections.defaultdict(list)       # base -> rows
    base_prices = collections.defaultdict(set)    # base -> distinct retail
    for r in rows:
        cw2 = r["colorway"].zfill(2)
        priced[(r["code"], cw2)] = r
        by_base[r["code"]].append(r)
        base_prices[r["code"]].add(r["retail_x181"])

    q = ("SELECT sku, vendor, mfr_sku, "
         "substring(mfr_sku from '^[A-Za-z]+[0-9]+(?:-[0-9]+)?'), title "
         "FROM shopify_products WHERE sku ILIKE 'EUR-%' AND status='ACTIVE' AND mfr_sku<>''")
    out = subprocess.run(["psql", "host=/tmp dbname=dw_unified", "-tA", "-F", "\t", "-c", q],
                         capture_output=True, text=True)

    # Cody-gate fixes: hold Panel/Mural products (sold as sets, not rolls) and
    # hold absent-colorway rows whose pattern is in the Discontinued/Limited list
    # (that colorway may have priced differently when it existed).
    disco = set()
    dpath = os.path.join(HERE, "disco_codes.txt")
    if os.path.exists(dpath):
        disco = {l.strip() for l in open(dpath) if l.strip()}
    is_panel = re.compile(r'\b(panel|mural)\b', re.I)

    priceable, held = [], []
    for line in out.stdout.strip().splitlines():
        p = line.split("\t")
        if len(p) < 5:
            continue
        sku, vendor, mfr, code, title = p[0], p[1], p[2], p[3], p[4]
        ttl = title.split(" | ")[0]
        base = code.split("-")[0]
        cw = norm_cw(code)
        trade = retail = basis = None
        if cw and (base, cw) in priced:
            pr = priced[(base, cw)]; trade, retail, basis = pr["trade_price"], pr["retail_x181"], "exact"
        elif base in by_base and len(base_prices[base]) == 1:
            pr = by_base[base][0]; trade, retail = pr["trade_price"], pr["retail_x181"]
            cw_absent = cw is not None and cw not in by_base[base]
            basis = "pattern-single-cw-absent" if cw_absent else "pattern-single"
        rec = {
            "sku": sku, "roll_sku": re.sub(r'-Sample$', '', sku),
            "vendor": vendor, "mfr_code": code, "title": ttl,
            "trade_price": trade or "", "retail": retail or "",
            "basis": basis or ("multi-price-ambiguous" if base in by_base else "discontinued-absent"),
        }
        if not retail:
            held.append(rec)
        elif is_panel.search(ttl):
            rec["basis"] = "HELD-panel-not-roll"; held.append(rec)          # fix #2
        elif rec["basis"] == "pattern-single-cw-absent" and base in disco:
            rec["basis"] = "HELD-cw-absent-in-disco-list"; held.append(rec)  # fix #1
        else:
            priceable.append(rec)

    def wcsv(fn, recs):
        cols = ["sku", "roll_sku", "vendor", "mfr_code", "title", "trade_price", "retail", "basis"]
        with open(os.path.join(HERE, fn), "w", newline="") as f:
            w = csv.DictWriter(f, fieldnames=cols); w.writeheader(); w.writerows(recs)
    wcsv("reonboard.csv", priceable)
    wcsv("reonboard_held.csv", held)

    byv = collections.Counter(r["vendor"] for r in priceable)
    basis = collections.Counter(r["basis"] for r in priceable)
    canary = [r for r in priceable if r["roll_sku"] == "EUR-71216"]
    print(f"priceable: {len(priceable)} | held: {len(held)}")
    print("  basis:", dict(basis))
    print("  by vendor:", dict(byv))
    print("  CANARY EUR-71216:", canary[0] if canary else "not in active set (already priced)")
    print("  held reasons:", dict(collections.Counter(r["basis"] for r in held)))

if __name__ == "__main__":
    main()