← back to Wolfgordon Crawl
unmatched-analysis.js
59 lines
#!/usr/bin/env node
// Drill into the 789 unmatched legacy WG products (read-only).
// Goal: distinguish genuine discontinues from recoverable patterns.
const { pool } = require('./scraper-utils');
function norm(s){return s?String(s).toLowerCase().replace(/[®™©]/g,'').replace(/&/g,'&').replace(/[^a-z0-9]+/g,' ').replace(/\s+/g,' ').trim():'';}
function nsku(s){return s?String(s).toLowerCase().replace(/_8$/,'').replace(/[^a-z0-9]+/g,''):'';}
async function main(){
const cat = (await pool.query(`SELECT mfr_sku,pattern_name,color_name FROM wolf_gordon_catalog`)).rows;
const catNsku = new Set(cat.map(r=>nsku(r.mfr_sku)));
const catPat = new Set(cat.map(r=>norm(r.pattern_name))); // pattern-only catalog set
const catPC = new Set(cat.map(r=>`${norm(r.pattern_name)}|${norm(r.color_name)}`));
const leg = (await pool.query(`SELECT sku,mfr_sku,pattern_name,title,status FROM shopify_products WHERE vendor='Wolf Gordon'`)).rows;
const unmatched=[];
for(const r of leg){
const head=(r.title||'').split('|')[0].trim();
const ci=head.indexOf(' - ');
const color=ci===-1?null:head.slice(ci+3).trim();
if(catNsku.has(nsku(r.mfr_sku))) continue;
const pc=`${norm(r.pattern_name)}|${norm(color)}`;
if(color && catPC.has(pc)) continue;
unmatched.push({...r, color});
}
// Bucket the unmatched.
let patternStillExists=0, patternGone=0, patternIsSku=0;
const goneSamples=[], patStillSamples=[];
for(const u of unmatched){
const p=norm(u.pattern_name);
// pattern_name that is literally the SKU (weak legacy data)
if(nsku(u.pattern_name)===nsku(u.mfr_sku)){ patternIsSku++; continue; }
if(catPat.has(p)){ patternStillExists++; if(patStillSamples.length<12) patStillSamples.push(u); }
else { patternGone++; if(goneSamples.length<12) goneSamples.push(u); }
}
const byStatus={};
for(const u of unmatched) byStatus[u.status]=(byStatus[u.status]||0)+1;
console.log('UNMATCHED LEGACY ANALYSIS (total '+unmatched.length+')');
console.log('='.repeat(64));
console.log('By Shopify status:');
for(const k of Object.keys(byStatus)) console.log(' '+k.padEnd(22)+byStatus[k]);
console.log('-'.repeat(64));
console.log('pattern_name is just the SKU (weak data): '+patternIsSku);
console.log('pattern STILL EXISTS in catalog (color drifted): '+patternStillExists+' <- likely recolored/renamed, recoverable');
console.log('pattern GONE from catalog entirely: '+patternGone+' <- genuine discontinue');
console.log('='.repeat(64));
console.log('\nSAMPLE — pattern still exists but exact color/sku drifted:');
for(const u of patStillSamples) console.log(' '+u.sku.padEnd(16)+(u.pattern_name+' / '+(u.color||'')).padEnd(34)+'status='+u.status);
console.log('\nSAMPLE — pattern gone (genuine discontinue):');
for(const u of goneSamples) console.log(' '+u.sku.padEnd(16)+(u.pattern_name+' / '+(u.color||'')).padEnd(34)+'status='+u.status);
await pool.end();
}
main().catch(e=>{console.error(e);process.exit(1);});