You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
senzapaura_es/scripts/db/sembrar-glosario.mjs

103 lines
2.9 KiB

El glosario del taller, con su globo en el texto Lo que se pidió: «cada vez que aparezca un término del glosario en el texto, estará marcado para visualizarse con un popup con la explicación». Veintinueve términos con acepciones numeradas —«clave» es el patrón rítmico y el instrumento de percusión, y con una sola definición una de las dos sería falsa—, más las formas con las que cada palabra aparece de verdad: plurales, femeninos, con tilde y sin ella. El marcado va sobre el HTML ya compuesto y no sobre el Markdown: antes habría que esquivar la sintaxis, y un término dentro de un enlace o de un bloque de código es justo donde un reemplazo de texto se equivoca en silencio. Se marca una vez por término y por apartado, no en cada aparición: nueve «compás» subrayados no hay quien los lea. La pieza tiene más casos raros que cualquier otra del sitio y ninguno se ve mirando la página —el fallo es que un globo no salga, y nadie echa en falta lo que no sabe que existía—, así que va separada de la base y con doce pruebas. Dos las escribí creyendo que ya pasaban: - Marcaba «sinalefas» del final del párrafo en vez de la «sinalefa» de la primera línea, porque probaba forma por forma en orden de longitud. - Con las formas sin ordenar marcaba «rima» y no «rima asonante». Salía bien solo porque la consulta las devolvía ordenadas; ahora el desempate está en la función, que no puede depender de cómo se la llame. El globo es un botón y no un `title`: un tooltip nativo no se abre con teclado y en un móvil no existe. Se cierra con Escape devolviendo el foco, y se coloca debajo del término o encima si no cabe, porque tapar lo que se acaba de pulsar es lo peor que podría hacer. El glosario entero tiene además su página, donde sí cabe la ampliación que en un globo taparía el texto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
/**
* Carga el glosario del taller.
*
* npm run db:glosario
*
* Veintiocho términos con sus acepciones numeradas y las formas con las que
* aparecen de verdad en el texto. Es lo que hace posible el marcado: sin las
* variantes, «sinalefas» en plural no se reconocería y nadie echaría en falta
* el globo que no salió.
*
* Idempotente por slug: reimportar una definición corregida actualiza. Las
* acepciones se rehacen enteras —se borran y se vuelven a poner— porque su
* número es su identidad y renumerar a mano dejaría huecos.
*/
import fs from 'node:fs';
import pg from 'pg';
const SEMILLA = JSON.parse(fs.readFileSync('scripts/db/semilla-glosario.json', 'utf8'));
const slugificar = (t) =>
t
.toLowerCase()
.normalize('NFD')
.replace(/[̀-ͯ]/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
const c = new pg.Client({ connectionString: process.env.DATABASE_URL, query_timeout: 60_000 });
await c.connect();
await c.query('BEGIN');
const contado = { termino: 0, acepcion: 0, variante: 0 };
const avisos = [];
try {
const vistos = new Set();
for (const t of SEMILLA.terminos) {
const slug = slugificar(t.termino);
if (vistos.has(slug)) {
avisos.push(`«${t.termino}» sale dos veces en la semilla`);
continue;
}
vistos.add(slug);
const { rows } = await c.query(
`INSERT INTO termino (id, slug, termino) VALUES ($1,$2,$3)
ON CONFLICT (slug) DO UPDATE SET termino = EXCLUDED.termino
RETURNING id`,
[crypto.randomUUID(), slug, t.termino]
);
const terminoId = rows[0].id;
contado.termino++;
await c.query('DELETE FROM acepcion WHERE termino_id = $1', [terminoId]);
for (const [i, a] of t.acepciones.entries()) {
await c.query(
`INSERT INTO acepcion (id, termino_id, numero, categoria, definicion, ampliacion, ejemplo)
VALUES ($1,$2,$3,$4,$5,$6,$7)`,
[
crypto.randomUUID(),
terminoId,
i + 1,
a.categoria,
a.definicion,
a.ampliacion ?? null,
a.ejemplo ?? null
]
);
contado.acepcion++;
}
/*
* El propio término también es una forma buscable, así no hay que
* repetirlo en la lista de variantes de los veintiocho.
*/
await c.query('DELETE FROM termino_variante WHERE termino_id = $1', [terminoId]);
for (const forma of new Set([t.termino, ...(t.variantes ?? [])])) {
await c.query(
'INSERT INTO termino_variante (termino_id, forma) VALUES ($1,$2) ON CONFLICT DO NOTHING',
[terminoId, forma.toLowerCase()]
);
contado.variante++;
}
}
await c.query('COMMIT');
} catch (fallo) {
await c.query('ROLLBACK').catch(() => {});
console.error('Falló, no se ha escrito nada:');
console.error(' ' + fallo.message);
await c.end();
process.exit(1);
}
await c.end();
for (const [que, n] of Object.entries(contado)) console.log(` ${que.padEnd(10)} ${n}`);
for (const aviso of avisos) console.log(` aviso: ${aviso}`);
console.log('');
console.log('Glosario cargado.');

Powered by TurnKey Linux.