87 lines
3.3 KiB
JavaScript
87 lines
3.3 KiB
JavaScript
/**
|
||||
|
|
* D0 requirement 3 — review report for names that cannot be split safely.
|
|||
|
|
*
|
|||
|
|
* The migration backfills firstName = first token, lastName = the rest. That is
|
|||
|
|
* correct for two-token names and a guess for anything longer: "سید محمد
|
|||
|
|
* حسینی" should almost certainly be firstName="سید محمد", and the migration
|
|||
|
|
* writes firstName="سید" instead. Those rows need a human.
|
|||
|
|
*
|
|||
|
|
* READ-ONLY. It changes nothing; it prints what a person has to check.
|
|||
|
|
*
|
|||
|
|
* node scripts/report-name-split.cjs # print to stdout
|
|||
|
|
* node scripts/report-name-split.cjs --csv out.csv
|
|||
|
|
*
|
|||
|
|
* Run it AFTER the 20260922120000_customer_name_parts migration.
|
|||
|
|
*/
|
|||
|
|
const { PrismaClient } = require('@prisma/client');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
|
|||
|
|
const prisma = new PrismaClient();
|
|||
|
|
|
|||
|
|
/** Same normalisation the migration's SQL uses. */
|
|||
|
|
const tokens = (s) => String(s ?? '').trim().replace(/\s+/g, ' ').split(' ').filter(Boolean);
|
|||
|
|
|
|||
|
|
(async () => {
|
|||
|
|
const customers = await prisma.$queryRawUnsafe(`
|
|||
|
|
SELECT id, "fullName", "firstName", "lastName", mobile, "memberCode"
|
|||
|
|
FROM "Customer"
|
|||
|
|
ORDER BY "createdAt"
|
|||
|
|
`);
|
|||
|
|
|
|||
|
|
const multi = [];
|
|||
|
|
const empty = [];
|
|||
|
|
const single = [];
|
|||
|
|
|
|||
|
|
for (const c of customers) {
|
|||
|
|
const t = tokens(c.fullName);
|
|||
|
|
if (t.length === 0) empty.push(c);
|
|||
|
|
else if (t.length === 1) single.push(c);
|
|||
|
|
else if (t.length > 2) multi.push({ ...c, tokenCount: t.length });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`Customers scanned: ${customers.length}`);
|
|||
|
|
console.log(` two tokens (split is reliable): ${customers.length - multi.length - empty.length - single.length}`);
|
|||
|
|
console.log(` one token (lastName is NULL): ${single.length}`);
|
|||
|
|
console.log(` empty name (nothing to split): ${empty.length}`);
|
|||
|
|
console.log(` MORE THAN TWO TOKENS — NEEDS REVIEW: ${multi.length}`);
|
|||
|
|
|
|||
|
|
if (single.length) {
|
|||
|
|
console.log('\n--- single-token names (no surname recorded) ---');
|
|||
|
|
for (const c of single) console.log(` ${c.id} ${c.mobile} "${c.fullName}"`);
|
|||
|
|
}
|
|||
|
|
if (empty.length) {
|
|||
|
|
console.log('\n--- empty names ---');
|
|||
|
|
for (const c of empty) console.log(` ${c.id} ${c.mobile}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (multi.length) {
|
|||
|
|
console.log('\n--- NEEDS REVIEW: more than two tokens ---');
|
|||
|
|
console.log('The migration guessed first-token / rest. Verify each one.\n');
|
|||
|
|
for (const c of multi) {
|
|||
|
|
console.log(` ${c.id} ${c.mobile} (${c.tokenCount} tokens)`);
|
|||
|
|
console.log(` fullName : "${c.fullName}"`);
|
|||
|
|
console.log(` guessed : firstName="${c.firstName}" lastName="${c.lastName}"`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const csvArg = process.argv.indexOf('--csv');
|
|||
|
|
if (csvArg !== -1 && process.argv[csvArg + 1]) {
|
|||
|
|
const esc = (v) => `"${String(v ?? '').replace(/"/g, '""')}"`;
|
|||
|
|
const rows = [
|
|||
|
|
['id', 'mobile', 'memberCode', 'fullName', 'guessedFirstName', 'guessedLastName', 'tokenCount'].join(','),
|
|||
|
|
...multi.map((c) =>
|
|||
|
|
[c.id, c.mobile, c.memberCode, c.fullName, c.firstName, c.lastName, c.tokenCount].map(esc).join(','),
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
// BOM so Excel opens the Persian text in the right encoding.
|
|||
|
|
fs.writeFileSync(process.argv[csvArg + 1], '' + rows.join('\n'), 'utf8');
|
|||
|
|
console.log(`\nCSV written: ${process.argv[csvArg + 1]} (${multi.length} rows)`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await prisma.$disconnect();
|
|||
|
|
})().catch(async (e) => {
|
|||
|
|
console.error(e);
|
|||
|
|
await prisma.$disconnect();
|
|||
|
|
process.exit(1);
|
|||
|
|
});
|