← back to Dwjs Consolidation 2026 04 23
dwqw_survey.js
69 lines
#!/usr/bin/env node
// Survey DWQW active products for natural collection groupings
const https = require('https');
const STORE='designer-laboratory-sandbox.myshopify.com';
const TOKEN=(process.env.SHOPIFY_ADMIN_TOKEN || '');
function gql(b){return new Promise((res,rej)=>{const d=JSON.stringify(b);const r=https.request({hostname:STORE,path:'/admin/api/2024-10/graphql.json',method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json','Content-Length':Buffer.byteLength(d)}},R=>{let c='';R.on('data',x=>c+=x);R.on('end',()=>{try{res(JSON.parse(c))}catch{res({})}})});r.on('error',rej);r.setTimeout(60000,()=>{r.destroy();rej(new Error('t'))});r.write(d);r.end();});}
(async()=>{
const q = `{
products(query:"sku:DWQW* AND status:active") {
edges { node {
id title vendor tags productType
style: metafield(namespace:"specs",key:"style") { value }
pattern_mf: metafield(namespace:"specs",key:"pattern") { value }
color_family: metafield(namespace:"custom",key:"color_family") { value }
} }
}
}`;
for (let i=0;i<10;i++){
const r=await gql({query:'{ currentBulkOperation { status } }'});
const st=r?.data?.currentBulkOperation?.status;
if (!st || ['COMPLETED','FAILED','CANCELED','EXPIRED'].includes(st)) break;
await new Promise(r=>setTimeout(r,3000));
}
await gql({query:`mutation{ bulkOperationRunQuery(query: """${q}""") { bulkOperation{id} userErrors{message} } }`});
let url=null;
for(;;){
await new Promise(r=>setTimeout(r,4000));
const p=await gql({query:'{ currentBulkOperation { status objectCount url } }'});
const op=p?.data?.currentBulkOperation;
if (op?.status==='COMPLETED'){ url=op.url; break; }
if (['FAILED','CANCELED','EXPIRED'].includes(op?.status)) throw new Error(op.status);
}
const chunks=[]; await new Promise((r,j)=>{https.get(url,res=>{res.on('data',d=>chunks.push(d));res.on('end',r)}).on('error',j)});
const fs=require('fs');
const data = Buffer.concat(chunks).toString();
fs.writeFileSync('/Users/macstudio3/Projects/dwjs-consolidation-2026-04-23/dwqw_active.jsonl', data);
const products = [];
for (const l of data.split('\n').filter(Boolean)){
const o=JSON.parse(l);
if (o.id?.includes('/Product/')) products.push({
title:o.title, vendor:o.vendor, type:o.productType,
style:o.style?.value||'', pattern:o.pattern_mf?.value||'',
color_family:o.color_family?.value||'',
tags: o.tags||[]
});
}
console.log(`active DWQW: ${products.length}`);
// vendor distribution
const C = require('util');
const count = (arr, fn) => {
const m = {};
for (const x of arr) { const k=fn(x); if (!k) continue; m[k]=(m[k]||0)+1; }
return Object.entries(m).sort((a,b)=>b[1]-a[1]);
};
console.log('\nvendors:'); count(products,p=>p.vendor).slice(0,10).forEach(([k,v])=>console.log(` ${v} ${k}`));
console.log('\nproduct types:'); count(products,p=>p.type).slice(0,10).forEach(([k,v])=>console.log(` ${v} ${k}`));
console.log('\nstyle (top 20):'); count(products,p=>p.style).slice(0,20).forEach(([k,v])=>console.log(` ${v} ${k}`));
console.log('\npattern mf (top 15):'); count(products,p=>p.pattern).slice(0,15).forEach(([k,v])=>console.log(` ${v} ${k}`));
console.log('\ncolor_family (top 15):'); count(products,p=>p.color_family).slice(0,15).forEach(([k,v])=>console.log(` ${v} ${k}`));
// Tag analysis - candidate collection-worthy tags
const tagCount = {};
for (const p of products) for (const t of p.tags) tagCount[t]=(tagCount[t]||0)+1;
const topTags = Object.entries(tagCount).sort((a,b)=>b[1]-a[1]).slice(0,50);
console.log('\ntop 50 tags:');
topTags.forEach(([k,v])=>console.log(` ${String(v).padStart(5)} ${k}`));
})().catch(e=>{console.error(e); process.exit(1);});