← back to Filemaker Mcp
Backfill blank Name/Color of Pattern on WallQuest GRS grasscloth WALLPAPER masters (joins specialty_grasscloths_stage; idempotent)
dd37d309d4f99a100f45a4660200130a4825407d · 2026-08-10 11:04:01 -0700 · Steve
Files touched
A scripts/backfill-grs-name-color.mjs
Diff
commit dd37d309d4f99a100f45a4660200130a4825407d
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Aug 10 11:04:01 2026 -0700
Backfill blank Name/Color of Pattern on WallQuest GRS grasscloth WALLPAPER masters (joins specialty_grasscloths_stage; idempotent)
---
scripts/backfill-grs-name-color.mjs | 65 +++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/scripts/backfill-grs-name-color.mjs b/scripts/backfill-grs-name-color.mjs
new file mode 100644
index 0000000..4016cd0
--- /dev/null
+++ b/scripts/backfill-grs-name-color.mjs
@@ -0,0 +1,65 @@
+// Backfill blank "Name of Pattern" / "Color of Pattern" on the recent WallQuest
+// specialty-grasscloth WALLPAPER masters (Series=GRS, vid=WQ). Today's import created
+// ~73 records with Width + Internal Description populated but Name/Color left blank
+// (the pusher never mapped those two fields). This joins each FM record to the
+// authoritative dw_unified.specialty_grasscloths_stage by mfr code (FM lowercase
+// "Mfr Pattern" == stage UPPER "mfr_sku") and writes clean pattern/color.
+//
+// Idempotent + safe: only fills fields that are currently BLANK, only via the
+// "*List Wallpapers - Full View" layout (the one that actually exposes Name/Color),
+// reports any FM record with no stage match instead of guessing.
+//
+// node backfill-grs-name-color.mjs # DRY-RUN (prints plan)
+// node backfill-grs-name-color.mjs --apply # commit
+import { execFileSync } from 'node:child_process';
+import * as fm from '../src/fm-client.js';
+
+const APPLY = process.argv.includes('--apply');
+const DB = 'WALLPAPER';
+const LAYOUT = '*List Wallpapers - Full View';
+const PSQL = process.env.PSQL_BIN || '/opt/homebrew/opt/postgresql@14/bin/psql';
+const PG = 'postgresql:///dw_unified?host=/tmp&user=stevestudio2';
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+// authoritative pattern/color keyed by UPPER(mfr_sku)
+const stageRows = JSON.parse(execFileSync(PSQL, [PG, '-tAc',
+ `select coalesce(json_agg(json_build_object('mfr',upper(mfr_sku),'pattern',pattern,'color',color))::text,'[]')
+ from specialty_grasscloths_stage`], { encoding: 'utf8', maxBuffer: 64e6 }).trim());
+const stage = new Map(stageRows.map(r => [r.mfr, r]));
+
+async function main() {
+ // the recent broken batch: GRS / WQ, Name blank, Internal Description present
+ const res = await fm.findRecords(DB, LAYOUT,
+ { Series: 'GRS', vid: 'WQ', 'Name of Pattern': '=', 'Internal Description': '*' },
+ { limit: 500 });
+ const recs = res.records || [];
+ console.log(`Found ${recs.length} GRS/WQ records with blank Name of Pattern · ${APPLY ? 'APPLY' : 'DRY-RUN'}\n`);
+
+ let ok = 0, skip = 0, nomatch = 0, fail = 0;
+ const misses = [];
+ for (const rec of recs) {
+ const fd = rec.fieldData;
+ const sku = fd['combo sku'];
+ const mfr = String(fd['Mfr Pattern'] || '').trim();
+ const s = stage.get(mfr.toUpperCase());
+ if (!s || !s.pattern) { nomatch++; misses.push(`${sku} (mfr ${mfr || '—'})`); continue; }
+
+ const patch = {};
+ if (!String(fd['Name of Pattern'] || '').trim()) patch['Name of Pattern'] = s.pattern;
+ if (!String(fd['Color of Pattern'] || '').trim() && s.color) patch['Color of Pattern'] = s.color;
+ if (!Object.keys(patch).length) { skip++; continue; }
+
+ if (!APPLY) {
+ if (ok < 80) console.log(` ${sku} ${mfr} Name="${patch['Name of Pattern'] ?? '(kept)'}" Color="${patch['Color of Pattern'] ?? '(kept)'}"`);
+ ok++; continue;
+ }
+ try {
+ await fm.updateRecord(DB, LAYOUT, rec.recordId, patch, { dryRun: false });
+ ok++; if (ok % 20 === 0) console.log(` ...updated ${ok}`);
+ await sleep(120);
+ } catch (e) { fail++; console.error(` FAIL ${sku}: ${e.fmCode || e.message}`); await sleep(400); }
+ }
+ console.log(`\n${APPLY ? 'Updated' : 'Planned'}=${ok} alreadyFilled=${skip} noStageMatch=${nomatch} fail=${fail}`);
+ if (misses.length) console.log(`No stage match (left untouched):\n ${misses.join('\n ')}`);
+}
+main().catch(e => { console.error(e); process.exit(1); });
← d32c199 wallpaper: prefix-agnostic UNIQUE-number mfr fallback so Sho
·
back to Filemaker Mcp
·
Add GRS/WQ 20% price-bump script + canonical grasscloth impo ef0b961 →