[object Object]

← back to Designer Wallcoverings

Daisy Bennett: material-based SKU prefixes (DWGRS/PWV/RAF/SIS/LIN/CORK/ABA), family-consistent majority-vote

4511065a2bd8fb3b41e13dd2bb086b5df301b591 · 2026-07-06 11:36:23 -0700 · Steve

Files touched

Diff

commit 4511065a2bd8fb3b41e13dd2bb086b5df301b591
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Jul 6 11:36:23 2026 -0700

    Daisy Bennett: material-based SKU prefixes (DWGRS/PWV/RAF/SIS/LIN/CORK/ABA), family-consistent majority-vote
---
 .../wallquest-refresh/remap-skus-daisy-bennett.cjs | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/scripts/wallquest-refresh/remap-skus-daisy-bennett.cjs b/scripts/wallquest-refresh/remap-skus-daisy-bennett.cjs
new file mode 100644
index 00000000..ccc7ac23
--- /dev/null
+++ b/scripts/wallquest-refresh/remap-skus-daisy-bennett.cjs
@@ -0,0 +1,63 @@
+// Re-assign Daisy Bennett dw_sku with MATERIAL-BASED prefixes (Steve's directive, 2026-07-06).
+// grasscloth→DWGRS, paperweave→DWPWV, raffia→DWRAF, sisal→DWSIS, linen→DWLIN, cork→DWCORK,
+// mica→DWMIC, abaca→DWABA. Material signal = ai_material_texture, with title/pattern keyword
+// fallback (catches abaca that AI tagged "other"); unknown "other" → DWGRS (naturals default).
+// Rewrites the enriched + normalized files in place (numeric tail = original BX number preserved).
+const fs = require('fs');
+const ENR = '/tmp/wq-daisy-bennett-enriched.json';
+const NORM = '/tmp/wq-daisy-bennett-normalized.json';
+
+const CODE = { grasscloth:'GRS', paperweave:'PWV', raffia:'RAF', sisal:'SIS', linen:'LIN', cork:'CORK', mica:'MIC', abaca:'ABA', bamboo:'BAM', netting:'NET' };
+const KW = [ // title/pattern keyword → material (fallback when ai_texture is "other"/missing)
+  [/abaca/i,'abaca'],[/\bcork\b/i,'cork'],[/mica/i,'mica'],[/raffia/i,'raffia'],
+  [/sisal/i,'sisal'],[/linen/i,'linen'],[/paperweave|paper weave/i,'paperweave'],
+  [/grass\b|grasscloth|hemp|jute/i,'grasscloth'],
+];
+function materialOf(r) {
+  const ai = (r.ai_material_texture || '').toLowerCase();
+  if (ai && ai !== 'other' && CODE[ai]) return ai;
+  const hay = `${r.pattern || ''} ${r.src_title || ''} ${r.material || ''}`;
+  for (const [re, m] of KW) if (re.test(hay)) return m;
+  return 'grasscloth'; // naturals default
+}
+// A pattern's substrate is a property of the PATTERN, not the colorway — so assign each
+// family ONE material by majority vote of its colorways (tiebreak by priority), keeping
+// every family internally consistent. Non-unanimous families are flagged for spot-check.
+const PRIORITY = ['grasscloth','paperweave','raffia','sisal','linen','cork','abaca','mica'];
+function familyMaterialMap(recs) {
+  const byFam = {};
+  for (const r of recs) { const f = (r.pattern||'').trim().toLowerCase(); (byFam[f]=byFam[f]||[]).push(materialOf(r)); }
+  const map = {}, flagged = [];
+  for (const [f, mats] of Object.entries(byFam)) {
+    const counts = {}; mats.forEach(m => counts[m] = (counts[m]||0)+1);
+    const winner = Object.entries(counts).sort((a,b) => b[1]-a[1] || PRIORITY.indexOf(a[0])-PRIORITY.indexOf(b[0]))[0][0];
+    map[f] = winner;
+    if (Object.keys(counts).length > 1) flagged.push(`${f} → ${winner}  (mixed: ${Object.entries(counts).map(([m,c])=>m+'×'+c).join(', ')})`);
+  }
+  return { map, flagged };
+}
+function recode(recs, famMap) {
+  const dist = {};
+  for (const r of recs) {
+    const mat = famMap[(r.pattern||'').trim().toLowerCase()] || materialOf(r);
+    const code = CODE[mat] || 'GRS';
+    const tail = (String(r.dw_sku).match(/\d+/) || (String(r.mfr_sku).match(/\d+/)) || ['0'])[0];
+    r.dw_sku = `DW${code}-${tail}`;
+    r._material_resolved = mat;
+    dist[code] = (dist[code] || 0) + 1;
+  }
+  return dist;
+}
+
+let dist = {}, flagged = [];
+const enr = fs.existsSync(ENR) ? JSON.parse(fs.readFileSync(ENR, 'utf8')) : null;
+const fm = familyMaterialMap(enr || JSON.parse(fs.readFileSync(NORM, 'utf8')));
+flagged = fm.flagged;
+if (enr) { dist = recode(enr, fm.map); fs.writeFileSync(ENR, JSON.stringify(enr, null, 2)); }
+if (fs.existsSync(NORM)) { const n = JSON.parse(fs.readFileSync(NORM, 'utf8')); recode(n, fm.map); fs.writeFileSync(NORM, JSON.stringify(n, null, 2)); }
+
+console.log('material-based SKU distribution (family-consistent):');
+Object.entries(dist).sort((a, b) => b[1] - a[1]).forEach(([c, v]) => console.log(`  DW${c}-  ×${v}`));
+console.log(`total=${Object.values(dist).reduce((a, b) => a + b, 0)}`);
+if (flagged.length) { console.log(`\n⚠ ${flagged.length} families were non-unanimous (AI texture split) — spot-check:`); flagged.forEach(f => console.log('  '+f)); }
+else console.log('\nall families unanimous ✓');

← ad9acbf8 Add Daisy Bennett Vol 2 internal staging viewer (sort+densit  ·  back to Designer Wallcoverings  ·  Carl Robinson: material-based SKU prefixes (DWGRS/PWV/CORK/M bccb98f8 →