← back to Rebel Walls Push

scripts/dedup-flag.js

61 lines

'use strict';
/* Tightened dedup: flag rebelwalls_catalog rows whose (pattern[+exact color]) is ALREADY
 * live in the OLDER Rebel Walls cohort. Adds reversible dedup_skip boolean. Local-PG write only.
 *   --apply  actually writes the flag (default = dry report) */
const { gqlRetry, psql } = require(process.env.HOME+'/Projects/rebel-walls-push/scripts/_shop');
const APPLY = process.argv.includes('--apply');
const norm = s => (s||'').toLowerCase().replace(/[^a-z0-9]+/g,' ').trim();

const Q=`query($cursor:String){products(first:250,query:"vendor:'Rebel Walls'",after:$cursor){pageInfo{hasNextPage endCursor}edges{node{title variants(first:5){edges{node{sku}}}}}}}`;
(async()=>{
  // ---- older-live cohort: pattern -> Set(colors) ----
  let cursor=null; const patColors=new Map(); const patAny=new Set(); let oldN=0;
  do{const r=await gqlRetry(Q,{cursor},'f');const d=r.json.data.products;
    for(const e of d.edges){
      const mains=e.node.variants.edges.map(v=>(v.node.sku||'').replace(/-Sample$/i,'')).filter(Boolean);
      const dwrw=mains.find(s=>/^DWRW-\d+$/.test(s));
      const n=dwrw?parseInt(dwrw.split('-')[1],10):null;
      const isOld = !dwrw || n<360000 || n>364132;
      if(!isOld) continue;
      oldN++;
      let t=e.node.title.replace(/\s*\|\s*Rebel Walls\s*$/i,'').replace(/wallcovering/ig,'').replace(/\s*\d+\s*cm\s*x\s*\d+\s*cm\s*/ig,' ').trim();
      const ci=t.indexOf(', ');
      let pat, col;
      if(ci>=0){ pat=norm(t.slice(0,ci)); col=norm(t.slice(ci+2)); }
      else { pat=norm(t); col=''; }
      patAny.add(pat);
      if(!patColors.has(pat)) patColors.set(pat,new Set());
      if(col) patColors.get(pat).add(col);
    }
    cursor=d.pageInfo.hasNextPage?d.pageInfo.endCursor:null;
  }while(cursor);

  // ---- classify unpushed new rows ----
  const rows = psql("SELECT dw_sku, COALESCE(pattern_name,''), COALESCE(color_name,'') FROM rebelwalls_catalog WHERE (shopify_product_id IS NULL OR shopify_product_id='') ORDER BY dw_sku;").trim().split('\n').filter(Boolean).map(l=>l.split('\t'));
  const dupSkus=[], netSkus=[];
  for(const [sku,pat,col] of rows){
    const np=norm(pat), nc=norm(col);
    let isDup=false;
    if(np && patAny.has(np)){
      if(nc){ isDup = patColors.get(np).has(nc); }   // has color: dup only if exact colorway live
      else  { isDup = true; }                         // no color: conservative -> dup if pattern live
    }
    (isDup?dupSkus:netSkus).push(sku);
  }
  console.log(`older cohort: ${oldN} products, ${patAny.size} distinct patterns`);
  console.log(`unpushed new rows: ${rows.length}`);
  console.log(`  TRUE duplicates (tightened): ${dupSkus.length}`);
  console.log(`  NET-NEW to push:             ${netSkus.length}`);

  if(!APPLY){ console.log('\nDRY — no PG writes. Re-run with --apply to flag.'); return; }

  psql("ALTER TABLE rebelwalls_catalog ADD COLUMN IF NOT EXISTS dedup_skip boolean;");
  psql("UPDATE rebelwalls_catalog SET dedup_skip=false WHERE (shopify_product_id IS NULL OR shopify_product_id='');");
  for(let i=0;i<dupSkus.length;i+=1000){
    const chunk=dupSkus.slice(i,i+1000).map(s=>`'${s}'`).join(',');
    psql(`UPDATE rebelwalls_catalog SET dedup_skip=true WHERE dw_sku IN (${chunk});`);
  }
  const after=psql("SELECT count(*) FILTER (WHERE dedup_skip=true), count(*) FILTER (WHERE dedup_skip=false AND (shopify_product_id IS NULL OR shopify_product_id='')) FROM rebelwalls_catalog;").trim();
  console.log(`\nFLAGGED. dedup_skip=true \\t pushable-net-new:  ${after}`);
})().catch(e=>{console.error('ERR',e.message);process.exit(1);});