← back to Dw Cadence Next2

scripts/build-data.js

88 lines

'use strict';
/*
 * build-data.js — project the NEXT 2 go-live days out of the activation-calendar
 * cadence and enrich each SKU with its image + handle from the dw_unified mirror.
 *
 * Reads the SAME pruned snapshot the cadence viewer serves
 * (~/Projects/dw-activation-calendar/data/activation-schedule.json — DRAFT-only,
 * already-active items pruned per DTD-C), takes the 2 earliest upcoming go-live
 * DATES, joins shopify_products for image_url + handle, and writes
 * data/next2days.json. Read-only vs dw_unified; re-run any time.
 */
const fs = require('fs');
const os = require('os');
const path = require('path');
const { execFileSync } = require('child_process');

const SCHED = path.join(os.homedir(), 'Projects', 'dw-activation-calendar', 'data', 'activation-schedule.json');
const OUT = path.join(__dirname, '..', 'data', 'next2days.json');

function psql(sql) {
  return execFileSync('psql', ['-At', '-F', '\t', '-d', 'dw_unified', '-c', sql],
    { encoding: 'utf8', maxBuffer: 1 << 28 }).trim();
}

const doc = JSON.parse(fs.readFileSync(SCHED, 'utf8'));
const skus = Array.isArray(doc.skus) ? doc.skus : [];

// The 2 earliest upcoming go-live dates in the cadence.
const dates = [...new Set(skus.map(s => s.go_live_date))].sort().slice(0, 2);
const picked = skus.filter(s => dates.includes(s.go_live_date));

// Join image_url + handle from the mirror for the picked ids.
const ids = [...new Set(picked.map(s => String(s.shopify_id)))];
const tmp = path.join(os.tmpdir(), `next2_ids_${process.pid}.txt`);
fs.writeFileSync(tmp, ids.join('\n'));
let rows;
try {
  rows = psql(
    `WITH q(id) AS (SELECT unnest(string_to_array(pg_read_file('${tmp}'), E'\\n')))
     SELECT sp.shopify_id::text, coalesce(sp.image_url,''), coalesce(sp.handle,''), sp.status
     FROM shopify_products sp JOIN q ON sp.shopify_id::text = q.id`);
} finally {
  try { fs.unlinkSync(tmp); } catch { /* best-effort */ }
}

const meta = {};
for (const line of rows.split('\n')) {
  if (!line) continue;
  const [id, image_url, handle, status] = line.split('\t');
  meta[id] = { image_url, handle, status };
}

const STORE = 'https://designer-laboratory-sandbox.myshopify.com';
const items = picked.map(s => {
  const m = meta[String(s.shopify_id)] || {};
  return {
    shopify_id: s.shopify_id,
    dw_sku: s.dw_sku || '',
    title: s.title || '',
    vendor: s.vendor || '',
    product_type: s.product_type || '',
    mat_tier: Number(s.mat_tier),
    go_live_date: s.go_live_date,
    go_live_at: s.go_live_at,
    slot_hour: s.slot_hour,
    image_url: m.image_url || '',
    handle: m.handle || '',
    product_url: m.handle ? `${STORE}/products/${m.handle}` : '',
    still_draft: m.status === 'DRAFT',
  };
}).sort((a, b) => a.go_live_at < b.go_live_at ? -1 : a.go_live_at > b.go_live_at ? 1 : 0);

const out = {
  meta: {
    generated_at: new Date().toISOString(),
    schedule_generated_at: doc.meta && doc.meta.generated_at,
    dates,
    total: items.length,
    by_date: dates.map(d => ({ date: d, count: items.filter(i => i.go_live_date === d).length })),
    with_image: items.filter(i => i.image_url).length,
    still_draft: items.filter(i => i.still_draft).length,
  },
  items,
};
fs.writeFileSync(OUT, JSON.stringify(out));
console.log(`next2days: ${items.length} items across ${dates.join(', ')} ` +
  `(${out.meta.with_image} with image, ${out.meta.still_draft} still DRAFT) -> ${OUT}`);