[object Object]

← back to Designer Wallcoverings

auto-save: 2026-07-10T12:08:12 (6 files) — DW-Programming/schu_pillow_apisniff.mjs pending-approval/boost-filter-consolidation-2026-06-25 scripts/pr-contact-for-price shopify/scripts/cadence/data/upload-restore-2026-07-10.jsonl vendor-scrapers/china-seas-refresh

45d52c9333ac7b0ff417d21515f25c3c8b6cb3f4 · 2026-07-10 12:08:18 -0700 · Steve Abrams

Files touched

Diff

commit 45d52c9333ac7b0ff417d21515f25c3c8b6cb3f4
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Jul 10 12:08:18 2026 -0700

    auto-save: 2026-07-10T12:08:12 (6 files) — DW-Programming/schu_pillow_apisniff.mjs pending-approval/boost-filter-consolidation-2026-06-25 scripts/pr-contact-for-price shopify/scripts/cadence/data/upload-restore-2026-07-10.jsonl vendor-scrapers/china-seas-refresh
---
 DW-Programming/schu-enrich-local.mjs               | 70 ++++++++++++++++++++++
 DW-Programming/schu_pillow_apisniff.mjs            | 32 ----------
 .../cadence/data/upload-restore-2026-07-10.jsonl   |  3 +
 3 files changed, 73 insertions(+), 32 deletions(-)

diff --git a/DW-Programming/schu-enrich-local.mjs b/DW-Programming/schu-enrich-local.mjs
new file mode 100644
index 00000000..e886186f
--- /dev/null
+++ b/DW-Programming/schu-enrich-local.mjs
@@ -0,0 +1,70 @@
+// LOCAL enrichment ($0) for new Schumacher pillow rows.
+// qwen2.5vl:7b -> colors + background from image; qwen3:14b -> polished 1-line description.
+// Writes ONLY ai_colors, ai_background_color, ai_description on schumacher_catalog pillow rows.
+import pg from 'pg';
+
+const OLLAMA = 'http://127.0.0.1:11434';
+const LIMIT = parseInt(process.argv[2] || '40');
+const sleep = (ms) => new Promise(r => setTimeout(r, ms));
+
+async function fetchImgB64(url) {
+  const r = await fetch(url);
+  if (!r.ok) return null;
+  const buf = Buffer.from(await r.arrayBuffer());
+  return buf.toString('base64');
+}
+async function ollama(model, prompt, images) {
+  const body = { model, prompt, stream: false, options: { temperature: 0.2 } };
+  if (images) body.images = images;
+  const r = await fetch(`${OLLAMA}/api/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
+  if (!r.ok) return null;
+  const d = await r.json();
+  return d.response;
+}
+function extractJSON(s) {
+  if (!s) return null;
+  const m = s.match(/\{[\s\S]*\}/);
+  if (!m) return null;
+  try { return JSON.parse(m[0]); } catch { return null; }
+}
+
+const client = new pg.Client({ database: 'dw_unified' });
+await client.connect();
+const { rows } = await client.query(
+  `SELECT mfr_sku, pattern_name, color_name, image_url, description
+   FROM schumacher_catalog
+   WHERE product_type='pillow' AND image_url IS NOT NULL
+     AND (ai_colors IS NULL OR ai_background_color IS NULL)
+   ORDER BY crawled_at DESC LIMIT $1`, [LIMIT]);
+console.log(`enriching ${rows.length} pillow rows (local, $0)`);
+
+let done = 0;
+for (const r of rows) {
+  try {
+    const b64 = await fetchImgB64(r.image_url);
+    let colors = null, bg = null;
+    if (b64) {
+      const vprompt = `This is a decorative throw pillow. Return ONLY JSON: {"colors":["name1","name2","name3"],"background":"dominant background color name"}. Use plain color names (e.g. Navy, Cream, Sage). Max 3 colors.`;
+      const vout = await ollama('qwen2.5vl:7b', vprompt, [b64]);
+      const j = extractJSON(vout);
+      if (j) { colors = j.colors || null; bg = j.background || null; }
+    }
+    // polished description via qwen3 (text only)
+    const tprompt = `Write ONE concise retail sentence (max 22 words) for a luxury Schumacher throw pillow named "${r.pattern_name}" in color "${r.color_name}". No fluff, no quotes. Description context: ${(r.description || '').slice(0, 160)}`;
+    let desc = await ollama('qwen3:14b', tprompt, null);
+    if (desc) desc = desc.replace(/<think>[\s\S]*?<\/think>/gi, '').trim().replace(/^["']|["']$/g, '').split('\n').filter(Boolean).pop();
+
+    await client.query(
+      `UPDATE schumacher_catalog SET
+         ai_colors = COALESCE($2::jsonb, ai_colors),
+         ai_background_color = COALESCE($3, ai_background_color),
+         ai_description = COALESCE($4, ai_description),
+         ai_accepted_at = now()
+       WHERE mfr_sku = $1`,
+      [r.mfr_sku, colors ? JSON.stringify(colors) : null, bg, desc || null]);
+    done++;
+    if (done % 5 === 0) process.stdout.write(`  ${done}/${rows.length}\r`);
+  } catch (e) { console.error('enrich fail', r.mfr_sku, e.message); }
+}
+await client.end();
+console.log(`\nlocal enrichment done: ${done} rows`);
diff --git a/DW-Programming/schu_pillow_apisniff.mjs b/DW-Programming/schu_pillow_apisniff.mjs
deleted file mode 100644
index 57491f60..00000000
--- a/DW-Programming/schu_pillow_apisniff.mjs
+++ /dev/null
@@ -1,32 +0,0 @@
-// ANONYMOUS (no login) — load pillows category, capture the client JSON API that returns variations,
-// then replay it page-by-page to harvest all 782 pillow variations into a JSONL.
-import { chromium } from 'playwright';
-import fs from 'fs';
-
-const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36';
-const browser = await chromium.launch({ headless: true });
-const ctx = await browser.newContext({ userAgent: UA });
-const page = await ctx.newPage();
-
-let apiUrl = null;
-let apiHeaders = null;
-const seen = [];
-page.on('request', (r) => {
-  const u = r.url();
-  if (/variation|products\/search|catalog.*product|api/i.test(u) && r.resourceType() === 'fetch') {
-    seen.push(r.method() + ' ' + u.slice(0, 160));
-    if (/variation|search/i.test(u) && !apiUrl) { apiUrl = u; apiHeaders = r.headers(); }
-  }
-});
-
-await page.goto('https://schumacher.com/catalog/7', { waitUntil: 'domcontentloaded', timeout: 40000 });
-await page.waitForTimeout(6000);
-// try to trigger pagination / more loads by scrolling
-for (let i = 0; i < 3; i++) { await page.mouse.wheel(0, 4000); await page.waitForTimeout(1500); }
-
-console.log('=== fetch requests seen ===');
-console.log(seen.slice(0, 20).join('\n'));
-console.log('=== candidate apiUrl ===', apiUrl);
-if (apiHeaders) console.log('=== apiHeaders ===', JSON.stringify({ authorization: apiHeaders.authorization, 'x-api-key': apiHeaders['x-api-key'], origin: apiHeaders.origin, referer: apiHeaders.referer }));
-
-await browser.close();
diff --git a/shopify/scripts/cadence/data/upload-restore-2026-07-10.jsonl b/shopify/scripts/cadence/data/upload-restore-2026-07-10.jsonl
index 48f814dd..8074778e 100644
--- a/shopify/scripts/cadence/data/upload-restore-2026-07-10.jsonl
+++ b/shopify/scripts/cadence/data/upload-restore-2026-07-10.jsonl
@@ -168,3 +168,6 @@
 {"table":"thibaut_catalog","mfr_sku":"T24128","dw_sku":"DWTT-73944","shopify_product_id":"7880634957875","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-10T17-40-05-236Z","ts":"2026-07-10T17:40:11.113Z"}
 {"table":"thibaut_catalog","mfr_sku":"T24124","dw_sku":"DWTT-73945","shopify_product_id":"7880634990643","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-10T17-40-05-236Z","ts":"2026-07-10T17:40:14.441Z"}
 {"table":"thibaut_catalog","mfr_sku":"T24129","dw_sku":"DWTT-73946","shopify_product_id":"7880635023411","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-10T17-40-05-236Z","ts":"2026-07-10T17:40:17.807Z"}
+{"table":"thibaut_catalog","mfr_sku":"T24130","dw_sku":"DWTT-73947","shopify_product_id":"7880662745139","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-10T18-40-00-247Z","ts":"2026-07-10T18:40:05.619Z"}
+{"table":"thibaut_catalog","mfr_sku":"T24131","dw_sku":"DWTT-73948","shopify_product_id":"7880662843443","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-10T18-40-00-247Z","ts":"2026-07-10T18:40:09.143Z"}
+{"table":"thibaut_catalog","mfr_sku":"T24134","dw_sku":"DWTT-73949","shopify_product_id":"7880662876211","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-10T18-40-00-247Z","ts":"2026-07-10T18:40:12.866Z"}

← 4bfcd4e0 auto-save: 2026-07-10T11:38:04 (7 files) — pending-approval/  ·  back to Designer Wallcoverings  ·  auto-save: 2026-07-10T12:38:21 (4 files) — _dw-login-record. 0b3ab024 →