← back to Hollywood Import

match-momentum-col3.mjs

50 lines

// Price the Tri-Kes/Momentum (vid=mom) unpriced Hollywood rows by matching the FileMaker
// mfr_sku (col3 = original Momentum slug/pattern ref) against the live crawl. Pattern price
// (×1.448 retail). Stages results; reports live vs discontinued. READ-ONLY (writes local files).
import fs from 'node:fs';
const slug = s => (s || '').toLowerCase().replace(/[’'.]/g, '').replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
const MULT = 1.448, r2 = n => Math.round(n * 100) / 100;

// crawl keys -> price
const crawl = fs.readFileSync(new URL('momentum-crawl.jsonl', import.meta.url), 'utf8').trim().split('\n').map(JSON.parse).filter(x => x.slug);
const K = {};
for (const x of crawl) { for (const k of [x.slug.split('-')[0], slug(x.pattern)]) if (k && !K[k]) K[k] = { price: x.price, unit: x.unit, pattern: x.pattern }; }

const GRADE = /[_-](t2|l2|r2|lsl|lv|uhn|2v[a-z]{0,3}|a\d{2,4}|asl|sg|bx|col|glo|nfr|kma|sf|mi|sx|ia|re|tr|vsx)([-_]|\d|$).*$/i;
function patKey(ref) {
  if (!ref) return null;
  let s = String(ref).replace(/\.jpg.*$/i, '').replace(/\s*\(\d+\)\s*$/, '').trim();   // drop .jpg + "(1)"
  if (GRADE.test(s)) s = s.replace(GRADE, '');
  else if (/^[a-z0-9]+(-[a-z0-9]+){2,}$/i.test(s)) { const p = s.split('-'); p.pop(); s = p.join('_'); }
  else s = s.replace(/[-_]\d.*$/, '');
  const k = slug(s);
  return k.length >= 3 ? k : null;
}

// FMP: dw_sku(col2) -> {mfrsku col3, vid}
const recs = fs.readFileSync('/Users/macstudio3/Projects/Designer-Wallcoverings/shopify/scripts/fmpro-master.csv', 'utf8').split('\r');
const fmp = new Map();
for (const r of recs) { if (!r) continue; const f = r.replace(/^"/, '').replace(/"$/, '').split('","'); if (f.length < 4) continue;
  fmp.set((f[1] || '').trim().toUpperCase(), { mfrsku: (f[2] || '').trim(), vid: (f[3] || '').trim().toLowerCase() }); }

const rows = fs.readFileSync('/tmp/hw_full.tsv', 'utf8').trim().split('\n').map(l => l.split('\t'));
const staged = [], absent = [];
for (const [mfr, pat, color, priced] of rows) {
  const h = fmp.get((mfr || '').toUpperCase());
  if (!h || h.vid !== 'mom' || priced !== '0') continue;
  const k = patKey(h.mfrsku);
  const hit = k && K[k];
  if (hit) staged.push({ mfr, col3: h.mfrsku, key: k, momPattern: hit.pattern, list: hit.price, hw: r2(hit.price * MULT), unit: hit.unit, dwPattern: pat, color });
  else absent.push({ mfr, col3: h.mfrsku, key: k, dwPattern: pat });
}
fs.writeFileSync(new URL('mom-col3-staged.json', import.meta.url), JSON.stringify(staged, null, 2));
fs.writeFileSync(new URL('mom-col3-absent.json', import.meta.url), JSON.stringify(absent, null, 2));
const total = staged.length + absent.length;
console.log(`Tri-Kes/Momentum unpriced: ${total}`);
console.log(`  matched to LIVE Momentum pattern → priceable: ${staged.length} (${Math.round(staged.length/total*100)}%)`);
console.log(`  not in live crawl (discontinued / unparseable): ${absent.length}`);
console.log('\n--- priced samples ---');
staged.slice(0, 12).forEach(s => console.log(`  ${s.mfr} [${s.col3}] → ${s.momPattern} → list ${s.list} hw ${s.hw}/${s.unit}`));
console.log('\n--- absent samples ---');
absent.slice(0, 10).forEach(s => console.log(`  ${s.mfr} [${s.col3}] key=${s.key}`));