← back to Wolfgordon Crawl
find-draft-candidates.js
35 lines
#!/usr/bin/env node
// Read-only: among the pushed cohort, find products that would FAIL the
// activation gate after backfill (no image available AND/OR no width) — the
// ones backfill-enrichment.js will set to DRAFT.
const fs = require('fs'); const os = require('os');
const { pool } = require('./scraper-utils');
function loadToken() { const p = os.homedir() + '/Projects/secrets-manager/.env'; const line = fs.readFileSync(p, 'utf8').split('\n').find(l => l.startsWith('SHOPIFY_ADMIN_TOKEN=')); return line.slice('SHOPIFY_ADMIN_TOKEN='.length).replace(/^["']|["']$/g, '').trim(); }
const TOKEN = loadToken(); const SHOP = 'designer-laboratory-sandbox.myshopify.com'; const GQL = `https://${SHOP}/admin/api/2024-10/graphql.json`;
const wait = ms => new Promise(r => setTimeout(r, ms));
async function gql(q, v) { await wait(250); const res = await fetch(GQL, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: q, variables: v }) }); const j = await res.json(); if (j.errors) throw new Error(JSON.stringify(j.errors)); return j.data; }
function catImgs(row) { const i = []; if (row.image_url) i.push(row.image_url); try { const a = JSON.parse(row.all_images || '[]'); if (Array.isArray(a)) a.forEach(u => u && i.push(u)); } catch {} return i; }
(async () => {
const rows = (await pool.query(`SELECT dw_sku, mfr_sku, pattern_name, color_name, width, width_inches, image_url, all_images, shopify_product_id FROM wolf_gordon_catalog WHERE shopify_product_id IS NOT NULL AND shopify_product_id<>''`)).rows;
const drafts = [];
for (let i = 0; i < rows.length; i += 50) {
const batch = rows.slice(i, i + 50);
const ids = batch.map(r => `"gid://shopify/Product/${r.shopify_product_id}"`).join(',');
const d = await gql(`{ nodes(ids:[${ids}]){ ... on Product { id status mediaCount{count} metafields(first:60){edges{node{namespace key}}} } } }`);
for (let k = 0; k < d.nodes.length; k++) {
const n = d.nodes[k], r = batch[k]; if (!n) continue;
const liveImg = n.mediaCount?.count || 0;
const have = new Set(n.metafields.edges.map(m => `${m.node.namespace}.${m.node.key}`));
const willHaveImg = liveImg > 0 || catImgs(r).length > 0;
const widthDisp = r.width || (r.width_inches ? `${r.width_inches}"` : null);
const willHaveWidth = have.has('global.width') || have.has('custom.width') || !!widthDisp;
if (!willHaveImg || !willHaveWidth) drafts.push({ dw_sku: r.dw_sku, mfr_sku: r.mfr_sku, pattern: r.pattern_name, color: r.color_name, status: n.status, liveImg, catImg: catImgs(r).length, willHaveImg, willHaveWidth });
}
process.stderr.write(`\r ${Math.min(i + 50, rows.length)}/${rows.length}`);
}
process.stderr.write('\n');
console.log('DRAFT candidates (fail gate):', drafts.length);
console.log(JSON.stringify(drafts, null, 2));
await pool.end();
})().catch(e => { console.error('ERR', e.message); process.exit(1); });