113 lines
3.4 KiB
JavaScript
113 lines
3.4 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
/**
|
||
|
|
* TICKET-012: Migrar glosarios del frontend al backend.
|
||
|
|
* Lee backend/scripts/fixtures/glossary-migration.json e inserta en tes_content.glossary_terms.
|
||
|
|
*
|
||
|
|
* Requisitos: tabla glossary_terms creada (npm run migrate:glossary).
|
||
|
|
* Opcional: generar fixture antes con npx tsx backend/scripts/generate-glossary-fixture.ts (desde raíz del repo).
|
||
|
|
*
|
||
|
|
* Uso: cd backend && node scripts/migrate-glossary-from-frontend.js
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { readFile } from 'fs/promises';
|
||
|
|
import { join, dirname } from 'path';
|
||
|
|
import { fileURLToPath } from 'url';
|
||
|
|
import { query } from '../config/database.js';
|
||
|
|
import 'dotenv/config';
|
||
|
|
|
||
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
|
|
const fixturePath = join(__dirname, 'fixtures', 'glossary-migration.json');
|
||
|
|
|
||
|
|
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000001';
|
||
|
|
|
||
|
|
async function getFirstUserId() {
|
||
|
|
const tables = ['tes_content.users', 'emerges_content.users'];
|
||
|
|
for (const table of tables) {
|
||
|
|
try {
|
||
|
|
const result = await query(
|
||
|
|
`SELECT id FROM ${table} WHERE is_active = true LIMIT 1`
|
||
|
|
);
|
||
|
|
if (result.rows.length > 0) {
|
||
|
|
return result.rows[0].id;
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return SYSTEM_USER_ID;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function termExists(term, category) {
|
||
|
|
const result = await query(
|
||
|
|
`SELECT 1 FROM tes_content.glossary_terms WHERE LOWER(term) = LOWER($1) AND category = $2 LIMIT 1`,
|
||
|
|
[term, category]
|
||
|
|
);
|
||
|
|
return result.rows.length > 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
console.log('🔧 TICKET-012: Migración de glosarios frontend → backend\n');
|
||
|
|
|
||
|
|
let data;
|
||
|
|
try {
|
||
|
|
const raw = await readFile(fixturePath, 'utf-8');
|
||
|
|
data = JSON.parse(raw);
|
||
|
|
} catch (err) {
|
||
|
|
console.error('❌ No se encontró o no se pudo leer', fixturePath);
|
||
|
|
console.error(' Genera el fixture desde la raíz del repo:');
|
||
|
|
console.error(' npx tsx backend/scripts/generate-glossary-fixture.ts\n');
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
const terms = data.terms || [];
|
||
|
|
if (terms.length === 0) {
|
||
|
|
console.log('⚠️ El fixture no contiene términos. Nada que migrar.');
|
||
|
|
process.exit(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
const userId = await getFirstUserId();
|
||
|
|
if (userId === SYSTEM_USER_ID) {
|
||
|
|
console.log('⚠️ No se encontró usuario en BD; usando ID de sistema para created_by.\n');
|
||
|
|
}
|
||
|
|
|
||
|
|
let inserted = 0;
|
||
|
|
let skipped = 0;
|
||
|
|
|
||
|
|
for (const row of terms) {
|
||
|
|
const { term, abbreviation, category, definition, context, source } = row;
|
||
|
|
if (!term || !definition || !category) {
|
||
|
|
skipped++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (!['pharmaceutical', 'anatomical', 'clinical', 'procedural'].includes(category)) {
|
||
|
|
skipped++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
const exists = await termExists(term, category);
|
||
|
|
if (exists) {
|
||
|
|
skipped++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
await query(
|
||
|
|
`INSERT INTO tes_content.glossary_terms (
|
||
|
|
id, term, abbreviation, category, definition, context, source, status,
|
||
|
|
created_at, updated_at, created_by, updated_by
|
||
|
|
) VALUES (
|
||
|
|
uuid_generate_v4(), $1, $2, $3, $4, $5, $6, 'published'::tes_content.content_status,
|
||
|
|
NOW(), NOW(), $7, $7
|
||
|
|
)`,
|
||
|
|
[term, abbreviation || null, category, definition, context || null, source || null, userId]
|
||
|
|
);
|
||
|
|
inserted++;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`✅ Migración completada: ${inserted} insertados, ${skipped} omitidos (ya existían o inválidos).\n`);
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch((err) => {
|
||
|
|
console.error('❌ Error:', err.message);
|
||
|
|
process.exit(1);
|
||
|
|
});
|