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/estudio/personas.mjs

183 lines
7.0 KiB

import { randomUUID } from 'node:crypto';
/** Unifica únicamente identidades personales enlazadas de forma explícita y unívoca. */
export async function migrarPersonas(c) {
await c.query(`CREATE TABLE IF NOT EXISTS estudio_alias (id text PRIMARY KEY,documento_id text NOT NULL REFERENCES estudio_documento(id) ON DELETE RESTRICT);
CREATE TABLE IF NOT EXISTS estudio_migracion (clave text PRIMARY KEY,datos jsonb NOT NULL,fecha timestamptz NOT NULL DEFAULT now());`);
await c.query("SELECT pg_advisory_xact_lock(hashtext('estudio:publicar'))");
const { rows: docs } = await c.query('SELECT * FROM estudio_documento FOR UPDATE');
const antiguas = docs.filter((d) => ['artistas', 'colaboradores'].includes(d.tipo));
if (!antiguas.length) return { convertidas: 0, unificadas: 0 };
const respaldo = { documentos: docs };
for (const tabla of ['estudio_vinculo', 'estudio_historial', 'estudio_trabajo'])
respaldo[tabla] = (await c.query(`SELECT * FROM ${tabla}`)).rows;
await c.query('INSERT INTO estudio_migracion(clave,datos) VALUES($1,$2)', [
'personas-' + randomUUID(),
respaldo
]);
const { rows: identidades } = await c.query(
"SELECT ap.* FROM artista_persona ap JOIN artista a ON a.id=ap.artista_id WHERE ap.relacion='identidad' AND a.tipo='persona'"
);
const { rows: artistas } = await c.query('SELECT id,slug,tipo,es_virtual FROM artista');
const { rows: personas } = await c.query(
'SELECT id,slug,nombre_legal,ipi,nacimiento,defuncion FROM persona'
);
const { rows: creditos } = await c.query(
"SELECT persona_id,CASE rol WHEN 'letra' THEN 'letrista' WHEN 'musica' THEN 'compositor' WHEN 'produccion' THEN 'productor' END AS tipo FROM cancion_persona UNION SELECT persona_id,CASE papel WHEN 'letrista' THEN 'letrista' WHEN 'compositor' THEN 'compositor' WHEN 'interprete' THEN 'artista' END FROM genero_persona"
);
const aliases = new Map();
for (const r of identidades) {
if (
identidades.filter((x) => x.persona_id === r.persona_id).length !== 1 ||
identidades.filter((x) => x.artista_id === r.artista_id).length !== 1
)
continue;
if (
docs.some((d) => d.id === r.artista_id && d.tipo === 'artistas') &&
docs.some((d) => d.id === r.persona_id && d.tipo === 'colaboradores')
)
aliases.set(r.artista_id, r.persona_id);
}
const grupos = new Map();
for (const d of antiguas) {
const id = aliases.get(d.id) ?? d.id;
grupos.set(id, [...(grupos.get(id) ?? []), d]);
}
const reservados = new Set(docs.filter((d) => d.tipo === 'personas').map((d) => d.borrador.slug));
const nuevos = [];
for (const [id, grupo] of grupos) {
const a = grupo.find((d) => d.tipo === 'artistas'),
p = grupo.find((d) => d.tipo === 'colaboradores');
const identidad = artistas.find((x) => x.id === a?.id),
fisica = personas.find((x) => x.id === p?.id);
const convertir = (edicion) => {
const x = a?.[edicion],
y = p?.[edicion];
if (!x && !y) return null;
const f = { ...(y?.campos ?? {}), ...(x?.campos ?? {}) };
const roles = new Set([
...(f.tiposPersona ?? []),
...(a
? [identidad?.es_virtual || identidad?.tipo === 'virtual' ? 'artista_virtual' : 'artista']
: []),
...creditos.filter((r) => r.persona_id === p?.id && r.tipo).map((r) => r.tipo)
]);
f.tiposPersona = [...roles];
f.naturaleza = identidad?.tipo === 'virtual' ? 'proyecto' : (identidad?.tipo ?? 'persona');
f.biografia = String(x?.campos.biografia || y?.campos.semblanza || '');
f.secciones =
x?.campos.secciones ??
(f.biografia ? [{ id: 'biografia-principal', tipo: 'texto', texto: f.biografia }] : []);
f.resumen = String(x?.campos.resumen ?? '');
f.nombreLegal = y?.campos.nombreLegal ?? fisica?.nombre_legal ?? '';
f.ipi = y?.campos.ipi ?? fisica?.ipi ?? '';
f.nacimiento = fisica?.nacimiento ?? null;
f.defuncion = fisica?.defuncion ?? null;
f._artistaId = identidad?.id ?? '';
f._personaId = fisica?.id ?? '';
f._personaSlug = fisica?.slug ?? '';
f._importado = true;
delete f.tipo;
delete f.semblanza;
return {
titulo: (x ?? y).titulo,
slug: (x ?? y).slug,
campos: f,
vinculos: [...(x?.vinculos ?? []), ...(y?.vinculos ?? [])]
};
};
const borrador = convertir('borrador'),
publicado = convertir('publicado');
if (reservados.has(borrador.slug)) {
borrador.slug += '-' + id.slice(0, 8);
}
reservados.add(borrador.slug);
const base = a ?? p;
nuevos.push({
...base,
id,
tipo: 'personas',
estado: grupo.some((d) => d.estado === 'publicado') ? 'publicado' : base.estado,
borrador,
publicado,
revision: Math.max(...grupo.map((d) => d.revision)) + 1
});
}
// Los enlaces apuntan a la ficha unificada; cada crédito conserva su papel.
const remap = (b) => {
if (!b) return b;
const vistos = new Set();
b.vinculos = (b.vinculos ?? [])
.map((v) => ({ ...v, destino: aliases.get(v.destino) ?? v.destino }))
.filter((v) => {
const k = v.papel + ':' + v.destino;
if (vistos.has(k)) return false;
vistos.add(k);
return true;
});
return b;
};
const todas = docs.filter((d) => !antiguas.some((x) => x.id === d.id)).concat(nuevos);
for (const d of todas) {
remap(d.borrador);
remap(d.publicado);
}
await c.query('DELETE FROM estudio_vinculo');
for (const d of nuevos) {
await c.query(
'UPDATE estudio_documento SET tipo=$2,borrador=$3,publicado=$4,revision=$5,estado=$6,actualizado=now(),autor=$7 WHERE id=$1',
[d.id, d.tipo, d.borrador, d.publicado, d.revision, d.estado, 'Migración a Personas']
);
await c.query(
'INSERT INTO estudio_historial(documento_id,revision,accion,autor,contenido) VALUES($1,$2,$3,$4,$5)',
[d.id, d.revision, 'unificar persona', 'Migración a Personas', d.borrador]
);
}
for (const d of todas.filter((d) => d.tipo !== 'personas'))
await c.query('UPDATE estudio_documento SET borrador=$2,publicado=$3 WHERE id=$1', [
d.id,
d.borrador,
d.publicado
]);
for (const [antes, despues] of aliases) {
await c.query('UPDATE estudio_historial SET documento_id=$2 WHERE documento_id=$1', [
antes,
despues
]);
await c.query('UPDATE estudio_trabajo SET documento_id=$2 WHERE documento_id=$1', [
antes,
despues
]);
await c.query(
'INSERT INTO estudio_alias(id,documento_id) VALUES($1,$2) ON CONFLICT(id) DO UPDATE SET documento_id=EXCLUDED.documento_id',
[antes, despues]
);
await c.query('DELETE FROM estudio_documento WHERE id=$1', [antes]);
}
const referencias = [
'principalId',
'cancionId',
'estiloId',
'generoId',
'modeloId',
'grabacionId'
];
for (const d of todas)
for (const edicion of ['borrador', 'publicado']) {
const b = d[edicion];
if (!b) continue;
const vinculos = [
...b.vinculos,
...referencias
.filter((k) => b.campos[k])
.map((k) => ({ destino: b.campos[k], papel: k, detalle: '', orden: 0 }))
];
for (const v of vinculos)
if (v.destino !== d.id)
await c.query(
'INSERT INTO estudio_vinculo(origen,destino,edicion,papel,orden,detalle) VALUES($1,$2,$3,$4,$5,$6) ON CONFLICT DO NOTHING',
[d.id, v.destino, edicion, v.papel, v.orden, v.detalle ?? '']
);
}
return { convertidas: nuevos.length, unificadas: aliases.size };
}

Powered by TurnKey Linux.