← back to Dw Yolo Loop
scripts/cole-son-apply-pattern-map.js
54 lines
#!/usr/bin/env node
/* Price the remaining Cole & Son products (no SKU/pattern metafields) by parsing the PATTERN
from the title and looking up its uniform MAP (248/249 Cole & Son patterns have one MAP).
Reads /tmp/cs_recon.json (noMatch list) + /tmp/cs_pattern_map.tsv. DRY RUN unless --apply. */
const fs = require('fs');
const STORE = 'designer-laboratory-sandbox.myshopify.com';
const TOKEN = process.env.T;
const APPLY = process.argv.includes('--apply');
const sleep = ms => new Promise(r => setTimeout(r, ms));
const PMAP = {};
fs.readFileSync('/tmp/cs_pattern_map.tsv', 'utf8').trim().split('\n').forEach(l => { const [p, m] = l.split('\t'); if (p) PMAP[p.trim().toUpperCase()] = parseFloat(m); });
// "Acacia - Blue & Green Multi | Cole & Son" → pattern "ACACIA"
function patternOf(title) {
let t = title.split('|')[0].trim(); // drop "| Cole & Son ..."
t = t.split(/\s+-\s+/)[0].trim(); // pattern is before " - colorway"
return t.toUpperCase().replace(/\s+/g, ' ').trim();
}
async function api(p, opts = {}, tries = 5) {
for (let i = 0; i < tries; i++) {
const r = await fetch(`https://${STORE}/admin/api/2024-10${p}`, { ...opts, headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json', ...(opts.headers || {}) } });
if (r.status === 429 || r.status >= 500) { await sleep(1500 * (i + 1)); continue; }
await sleep(95); return r;
}
throw new Error('fail ' + p);
}
(async () => {
const recon = JSON.parse(fs.readFileSync('/tmp/cs_recon.json', 'utf8'));
const cand = recon.noMatch;
const matched = [], unmatched = [];
for (const x of cand) {
const pat = patternOf(x.title);
const map = PMAP[pat];
if (map && Math.abs(x.cur - map) > 0.5) matched.push({ ...x, pat, map });
else if (map) { /* already at MAP */ }
else unmatched.push({ ...x, pat });
}
console.log(`no-match pool: ${cand.length} | pattern-matched & need reprice: ${matched.length} | still unmatched: ${unmatched.length} | mode: ${APPLY ? 'APPLY' : 'DRY RUN'}\n`);
matched.slice(0, 10).forEach(x => console.log(` [${x.status}] $${x.cur} → $${x.map} (${x.pat}) ${x.title.slice(0,40)}`));
console.log('\nstill-unmatched patterns (sample):', [...new Set(unmatched.map(u => u.pat))].slice(0, 15).join(' | '));
if (!APPLY) { console.log(`\nDRY RUN — re-run with --apply to reprice ${matched.length}.`); return; }
let ok = 0, fail = 0;
for (const x of matched) {
try {
const r = await api(`/variants/${x.vid}.json`, { method: 'PUT', body: JSON.stringify({ variant: { id: x.vid, price: String(x.map) } }) });
if (r.ok) { ok++; if (ok % 50 === 0) console.log(` …${ok}/${matched.length}`); } else { fail++; console.log(` FAIL ${x.id} ${r.status}`); }
} catch (e) { fail++; }
}
console.log(`\nDONE — ${ok} repriced, ${fail} failed.`);
})().catch(e => { console.error(e); process.exit(1); });