← back to Wallco Ai
scripts/backfill_prompt_match.js
56 lines
#!/usr/bin/env node
/**
* Backfill prompt-match ratings from the append-only JSONL ledger
* (data/prompt-match-ratings.jsonl) into the PG columns promoted in
* migrations/20260601_prompt_match_columns.sql.
*
* Last-write-wins per id (ledger is chronological). Idempotent: re-running just
* re-applies the same final state. ADDITIVE only — never touches is_published.
*
* Usage: node scripts/backfill_prompt_match.js
* Prints a JSON summary { ledger_lines, distinct_ids, applied, skipped }.
*/
'use strict';
const fs = require('fs');
const path = require('path');
const { psqlExecLocal, pgEsc } = require('../lib/db');
const LEDGER = path.join(__dirname, '..', 'data', 'prompt-match-ratings.jsonl');
if (!fs.existsSync(LEDGER)) {
console.log(JSON.stringify({ ledger_lines: 0, distinct_ids: 0, applied: 0, skipped: 0, note: 'no ledger file' }));
process.exit(0);
}
const lines = fs.readFileSync(LEDGER, 'utf8').split('\n').filter(Boolean);
const latest = new Map(); // id -> { match, verdict, ts }
for (const ln of lines) {
try {
const r = JSON.parse(ln);
if (Number.isInteger(r.id)) latest.set(r.id, { match: r.match ?? null, verdict: r.verdict ?? null, ts: r.ts || null });
} catch {}
}
let applied = 0, skipped = 0;
for (const [id, r] of latest) {
const match = Number.isInteger(r.match) && r.match >= 1 && r.match <= 5 ? r.match : null;
const verdict = (r.verdict === 'approve' || r.verdict === 'reject') ? r.verdict : null;
if (match === null && verdict === null) { skipped++; continue; }
const ts = r.ts || new Date().toISOString();
try {
psqlExecLocal(
'UPDATE spoon_all_designs SET ' +
'user_prompt_match=' + pgEsc(match) + ', ' +
'user_prompt_verdict=' + pgEsc(verdict) + ', ' +
'user_prompt_rated_at=' + pgEsc(ts) + ' ' +
'WHERE id=' + id + ';'
);
applied++;
} catch (e) {
skipped++;
process.stderr.write(`id=${id} failed: ${e.message}\n`);
}
}
console.log(JSON.stringify({ ledger_lines: lines.length, distinct_ids: latest.size, applied, skipped }));