← 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}`);