← back to 1980swallpaper

scripts/pull-from-shopify.js

115 lines

// 1980s wallpaper pull from live DW Shopify catalog.
// Era tags don't exist in catalog. We use a TITLE-PATTERN allowlist of
// known 1980s-aesthetic patterns that are already in the DW inventory.
// Match against (vendor + title) pairs so e.g. "Cole & Son Hicks Hexagon"
// matches but a generic "Hexagon" elsewhere doesn't drift in.
const https = require('https');
const fs = require('fs');
const path = require('path');
const { classifyAesthetic } = require('../../_shared/microsite-aesthetic');
const SITE_CFG_RAILS = (() => { try { return require('../site.config.json').rails || []; } catch { return []; } })();

// Single regex covering the 16 retag candidates (case-insensitive).
// Patterns are matched against the product TITLE.
const PATTERN_REGEX = new RegExp([
  // Mind the Gap
  'neon\\s*kiss',
  'eyes\\s*and\\s*circles',
  // Rebel Walls
  'triangle\\s*land',
  'your\\s*own\\s*world\\s*confetti',
  'neon\\s*city',
  'geometry\\s*pastel',
  'arch\\s*deco',
  // Romo
  'tiami',
  'terrazzo',
  'checkerboard',
  // Sandberg
  'alice\\s*confetti',
  'prisma\\s*pastel',
  // Zeuxis Parrhasius
  'zwicker',
  // DW Bespoke Studio
  "vintage\\s*1970'?s\\s*neon",
  // Cole & Son
  'hicks\\s*hexagon',
  'prism\\s*multi[\\s-]?colou?red',
  'feather\\s*fan',
  // generic confetti as final catchall (Rebel Walls "Confetti", Sandberg)
  '\\bconfetti\\b',
].join('|'), 'i');

const REJECT_TITLE = /(visual.{0,3}merchandiser|bh.?90210|\bimage[ _-]?4\b|lamp|rug|pillow|throw|tripod|frame|mirror|vase|candle|sculpture|figurine)/i;

function fetchPage(page) {
  return new Promise((resolve, reject) => {
    https.get(`https://designerwallcoverings.com/products.json?limit=250&page=${page}`, {
      headers: { 'User-Agent': 'Mozilla/5.0 1980swallpaper-builder' }
    }, (res) => {
      let data = '';
      res.on('data', c => data += c);
      res.on('end', () => { try { resolve(JSON.parse(data).products || []); } catch(e) { reject(e); } });
    }).on('error', reject);
  });
}

function aestheticOf(title) {
  const t = (title || '').toLowerCase();
  if (/neon/.test(t)) return 'neon';
  if (/confetti/.test(t)) return 'confetti';
  if (/terrazzo/.test(t)) return 'terrazzo';
  if (/checkerboard/.test(t)) return 'checkerboard';
  if (/hexagon|prism|geometry|triangle/.test(t)) return 'geometric';
  if (/feather|deco/.test(t)) return 'deco';
  return '1980s';
}

function classifyForSite(tags, title) {
  const railsMatch = classifyAesthetic(tags, SITE_CFG_RAILS);
  if (railsMatch && railsMatch !== 'all') return railsMatch;
  return aestheticOf(title);
}

(async () => {
  const all = [];
  for (let page = 1; page <= 30; page++) {
    const products = await fetchPage(page);
    if (!products.length) break;
    all.push(...products);
    if (products.length < 250) break;
  }
  console.log(`fetched: ${all.length}`);

  const niche = all
    .filter(p => p.product_type && /wallcovering/i.test(p.product_type))
    .filter(p => !REJECT_TITLE.test(p.title || ''))
    .filter(p => p.images && p.images.length > 0 && p.images[0].src)
    .filter(p => PATTERN_REGEX.test(p.title || ''))
    .map(p => ({
      sku: p.handle,
      handle: p.handle,
      title: p.title,
      vendor: (p.vendor || '').trim(),
      product_type: p.product_type,
      image_url: p.images[0].src,
      tags: p.tags || [],
      aesthetic: classifyForSite(p.tags, p.title),
      product_url: `https://designerwallcoverings.com/products/${p.handle}`,
    }));

  console.log(`niche-filtered: ${niche.length}`);
  const byA = {};
  for (const p of niche) byA[p.aesthetic] = (byA[p.aesthetic] || 0) + 1;
  console.log('aesthetics:', byA);
  const byV = {};
  for (const p of niche) byV[p.vendor] = (byV[p.vendor] || 0) + 1;
  console.log('vendors:', Object.entries(byV).sort((a,b)=>b[1]-a[1]));

  const tmp = path.join(__dirname, '..', 'data', 'products.json.tmp');
  const final = path.join(__dirname, '..', 'data', 'products.json');
  fs.writeFileSync(tmp, JSON.stringify(niche, null, 2));
  fs.renameSync(tmp, final);
  console.log('wrote data/products.json');
})();