296 lines
12 KiB
JavaScript
Executable file
296 lines
12 KiB
JavaScript
Executable file
/**
|
|
* Script para crear contenido de ejemplo (seed data)
|
|
*/
|
|
|
|
import { query } from '../config/database.js';
|
|
import 'dotenv/config';
|
|
import { randomUUID as uuidv4 } from 'crypto';
|
|
|
|
// Obtener ID del admin (asumiendo que existe)
|
|
async function getAdminId() {
|
|
const result = await query(
|
|
`SELECT id FROM emerges_content.users WHERE role = 'super_admin' LIMIT 1`
|
|
);
|
|
return result.rows[0]?.id || uuidv4();
|
|
}
|
|
|
|
async function seedContent() {
|
|
try {
|
|
console.log('🌱 Creando contenido de ejemplo...');
|
|
|
|
const adminId = await getAdminId();
|
|
|
|
// 1. Checklist: Electrodos/Parches DESA
|
|
const checklistElectrodos = {
|
|
id: 'checklist-electrodos-desa',
|
|
type: 'checklist',
|
|
level: 'operativo',
|
|
title: 'Colocación de Electrodos/Parches DESA',
|
|
shortTitle: 'Electrodos DESA',
|
|
description: 'Checklist para la correcta colocación de electrodos en desfibrilación',
|
|
content: {
|
|
items: [
|
|
{ id: '1', text: 'Verificar que el paciente está en superficie seca y no metálica', order: 1, critical: true },
|
|
{ id: '2', text: 'Secar el tórax si está húmedo', order: 2, critical: true },
|
|
{ id: '3', text: 'Rasurar vello excesivo si es necesario', order: 3 },
|
|
{ id: '4', text: 'Colocar parche derecho: debajo de la clavícula derecha', order: 4, critical: true },
|
|
{ id: '5', text: 'Colocar parche izquierdo: línea axilar media izquierda', order: 5, critical: true },
|
|
{ id: '6', text: 'Verificar que los parches están bien adheridos', order: 6, critical: true },
|
|
{ id: '7', text: 'Conectar cables al DESA', order: 7, critical: true },
|
|
{ id: '8', text: 'Asegurar que nadie toca al paciente durante análisis', order: 8, critical: true },
|
|
],
|
|
description: 'Secuencia de pasos para colocación correcta de electrodos en desfibrilación',
|
|
estimatedTime: '30-60 segundos',
|
|
applicableProtocols: ['rcp-adulto-svb', 'rcp-adulto-sva'],
|
|
tags: ['DESA', 'desfibrilación', 'electrodos'],
|
|
},
|
|
};
|
|
|
|
await insertContentItem(checklistElectrodos, adminId);
|
|
console.log(' ✅ Checklist: Electrodos DESA');
|
|
|
|
// 2. Checklist: Preparación Intubación
|
|
const checklistIntubacion = {
|
|
id: 'checklist-preparacion-intubacion',
|
|
type: 'checklist',
|
|
level: 'operativo',
|
|
title: 'Preparación para Intubación Orotraqueal',
|
|
shortTitle: 'Preparación IOT',
|
|
description: 'Checklist completo de preparación antes de intubación',
|
|
content: {
|
|
items: [
|
|
{ id: '1', text: 'Verificar material: laringoscopio, tubos, estilete', order: 1, critical: true, category: 'Material' },
|
|
{ id: '2', text: 'Comprobar fuente de luz del laringoscopio', order: 2, critical: true, category: 'Material' },
|
|
{ id: '3', text: 'Preparar tubo endotraqueal (talla adecuada)', order: 3, critical: true, category: 'Material' },
|
|
{ id: '4', text: 'Preparar estilete (si se usa)', order: 4, category: 'Material' },
|
|
{ id: '5', text: 'Preparar jeringa para balón', order: 5, category: 'Material' },
|
|
{ id: '6', text: 'Verificar aspiración funcionando', order: 6, critical: true, category: 'Verificación' },
|
|
{ id: '7', text: 'Preparar fármacos: sedación y relajante', order: 7, critical: true, category: 'Fármacos' },
|
|
{ id: '8', text: 'Verificar monitorización: SpO₂, ECG, capnografía', order: 8, critical: true, category: 'Monitorización' },
|
|
{ id: '9', text: 'Posicionar paciente: alineación cabeza-cuello-tórax', order: 9, critical: true, category: 'Posicionamiento' },
|
|
{ id: '10', text: 'Preoxigenación: 3-5 minutos con mascarilla con reservorio', order: 10, critical: true, category: 'Preoxigenación' },
|
|
],
|
|
description: 'Checklist completo para preparación segura de intubación orotraqueal',
|
|
estimatedTime: '5-10 minutos',
|
|
applicableProtocols: ['via-aerea', 'rcp-adulto-sva'],
|
|
tags: ['IOT', 'intubación', 'vía aérea avanzada'],
|
|
},
|
|
};
|
|
|
|
await insertContentItem(checklistIntubacion, adminId);
|
|
console.log(' ✅ Checklist: Preparación Intubación');
|
|
|
|
// 3. Checklist: RCP Checklist (versión mejorada)
|
|
const checklistRCP = {
|
|
id: 'checklist-rcp-adulto-svb',
|
|
type: 'checklist',
|
|
level: 'operativo',
|
|
title: 'Checklist RCP Adulto SVB',
|
|
shortTitle: 'RCP SVB Checklist',
|
|
description: 'Checklist interactivo para RCP básico en adultos',
|
|
content: {
|
|
items: [
|
|
{ id: '1', text: 'Seguridad de la escena', order: 1, critical: true },
|
|
{ id: '2', text: 'Comprobación de respuesta', order: 2, critical: true },
|
|
{ id: '3', text: 'Apertura de vía aérea', order: 3, critical: true },
|
|
{ id: '4', text: 'Comprobación de respiración (<10s)', order: 4, critical: true },
|
|
{ id: '5', text: 'Activación de emergencias', order: 5, critical: true },
|
|
{ id: '6', text: 'Inicio de compresiones', order: 6, critical: true },
|
|
{ id: '7', text: 'Colocación del DEA', order: 7, critical: true },
|
|
{ id: '8', text: 'Análisis y descarga si indicada', order: 8, critical: true },
|
|
{ id: '9', text: 'RCP de alta calidad continua', order: 9, critical: true },
|
|
{ id: '10', text: 'Reevaluación cada 2 minutos', order: 10, critical: true },
|
|
],
|
|
description: 'Checklist esencial para RCP básico en adultos',
|
|
estimatedTime: 'Continuo',
|
|
applicableProtocols: ['rcp-adulto-svb'],
|
|
tags: ['RCP', 'SVB', 'reanimación'],
|
|
},
|
|
};
|
|
|
|
await insertContentItem(checklistRCP, adminId);
|
|
console.log(' ✅ Checklist: RCP Adulto SVB');
|
|
|
|
// 4. Protocolo de ejemplo: RCP Adulto SVB (extendido)
|
|
const protocolRCP = {
|
|
id: 'rcp-adulto-svb-extended',
|
|
type: 'protocol',
|
|
level: 'operativo',
|
|
title: 'RCP Adulto SVB - Versión Extendida',
|
|
shortTitle: 'RCP SVB Ext',
|
|
description: 'Protocolo RCP SVB con checklist integrado y dosis inline',
|
|
category: 'soporte_vital',
|
|
subcategory: 'rcp',
|
|
priority: 'critico',
|
|
ageGroup: 'adulto',
|
|
content: {
|
|
pasosRapidos: [
|
|
{ order: 1, text: 'Garantizar seguridad de la escena', critical: true, timeEstimate: '5-10s' },
|
|
{ order: 2, text: 'Comprobar consciencia: estimular y preguntar "¿Se encuentra bien?"', critical: true, timeEstimate: '5s' },
|
|
{ order: 3, text: 'Si no responde, llamar inmediatamente al 112', critical: true, timeEstimate: '10s' },
|
|
{ order: 4, text: 'Abrir vía aérea: maniobra frente-mentón', critical: true, timeEstimate: '5s' },
|
|
{ order: 5, text: 'Comprobar respiración: VER-OÍR-SENTIR (máx. 10 segundos)', critical: true, timeEstimate: '10s' },
|
|
],
|
|
checklist: {
|
|
enabled: true,
|
|
items: [
|
|
{ id: '1', text: 'Seguridad de la escena', order: 1, reusableChecklistId: null },
|
|
{ id: '2', text: 'Comprobación de respuesta', order: 2, reusableChecklistId: null },
|
|
{ id: '3', text: 'Apertura de vía aérea', order: 3, reusableChecklistId: null },
|
|
],
|
|
title: 'Checklist RCP SVB',
|
|
},
|
|
dosisInline: [
|
|
{
|
|
drugId: 'adrenalina',
|
|
drugName: 'Adrenalina',
|
|
adultDose: '1 mg IV/IO cada 3-5 min',
|
|
route: 'IV',
|
|
timing: 'Cada 3-5 minutos en PCR',
|
|
context: 'En PCR, una vez establecida vía IV/IO',
|
|
},
|
|
],
|
|
herramientasContexto: [
|
|
{
|
|
id: 'calc-dosis-pediatrica',
|
|
name: 'Calculadora de Dosis Pediátrica',
|
|
type: 'calculator',
|
|
description: 'Calcula dosis por peso para pacientes pediátricos',
|
|
},
|
|
],
|
|
fuentes: [
|
|
{
|
|
organization: 'ERC',
|
|
guideline: 'European Resuscitation Council Guidelines 2021',
|
|
year: 2021,
|
|
section: 'Adult Basic Life Support',
|
|
},
|
|
],
|
|
warnings: [
|
|
'Profundidad compresiones: 5-6 cm',
|
|
'Frecuencia: 100-120 compresiones/min',
|
|
],
|
|
keyPoints: [
|
|
'Compresiones de calidad salvan vidas',
|
|
'No interrumpir para pulso hasta que haya signos de vida',
|
|
],
|
|
equipment: ['DEA', 'Bolsa-mascarilla'],
|
|
drugs: ['Adrenalina'],
|
|
version: 1,
|
|
lastUpdated: new Date().toISOString(),
|
|
},
|
|
};
|
|
|
|
await insertContentItem(protocolRCP, adminId);
|
|
console.log(' ✅ Protocolo: RCP Adulto SVB Extendido');
|
|
|
|
// 5. Protocolo de ejemplo: Shock Hemorrágico (extendido)
|
|
const protocolShock = {
|
|
id: 'shock-hemorragico-extended',
|
|
type: 'protocol',
|
|
level: 'operativo',
|
|
title: 'Shock Hemorrágico - Versión Extendida',
|
|
shortTitle: 'Shock Hemorrágico Ext',
|
|
description: 'Protocolo de shock hemorrágico con checklist y dosis inline',
|
|
category: 'soporte_vital',
|
|
subcategory: 'shock',
|
|
priority: 'critico',
|
|
ageGroup: 'adulto',
|
|
content: {
|
|
pasosRapidos: [
|
|
{ order: 1, text: 'Control de hemorragia externa: presión directa', critical: true, timeEstimate: 'Inmediato' },
|
|
{ order: 2, text: 'Torniquete si hemorragia en extremidad no controlable', critical: true, timeEstimate: '30-60s' },
|
|
{ order: 3, text: 'Oxigenoterapia alto flujo', critical: true, timeEstimate: '30s' },
|
|
],
|
|
dosisInline: [
|
|
{
|
|
drugId: 'acido-tranexamico',
|
|
drugName: 'Ácido Tranexámico',
|
|
adultDose: '1g IV en bolo lento (10 min)',
|
|
route: 'IV',
|
|
timing: 'Lo antes posible en hemorragia grave',
|
|
context: 'En hemorragia traumática grave',
|
|
},
|
|
],
|
|
fuentes: [
|
|
{
|
|
organization: 'SEMES',
|
|
guideline: 'Protocolo de Shock Hemorrágico',
|
|
year: 2023,
|
|
},
|
|
],
|
|
warnings: [
|
|
'Hipotensión permisiva: TAS 80-90 mmHg',
|
|
'Excepto en TCE: mantener TAS >90 mmHg',
|
|
],
|
|
equipment: ['Torniquete', 'Agentes hemostáticos'],
|
|
drugs: ['Ácido tranexámico'],
|
|
version: 1,
|
|
lastUpdated: new Date().toISOString(),
|
|
},
|
|
};
|
|
|
|
await insertContentItem(protocolShock, adminId);
|
|
console.log(' ✅ Protocolo: Shock Hemorrágico Extendido');
|
|
|
|
console.log('\n✅ Contenido de ejemplo creado exitosamente');
|
|
} catch (error) {
|
|
console.error('❌ Error creando contenido de ejemplo:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async function insertContentItem(item, userId) {
|
|
// Verificar si ya existe
|
|
const existing = await query(
|
|
`SELECT id FROM emerges_content.content_items WHERE id = $1`,
|
|
[item.id]
|
|
);
|
|
|
|
if (existing.rows.length > 0) {
|
|
console.log(` ⚠️ ${item.id} ya existe, saltando...`);
|
|
return;
|
|
}
|
|
|
|
// Insertar item
|
|
await query(
|
|
`INSERT INTO emerges_content.content_items
|
|
(id, type, level, title, short_title, description, category, subcategory,
|
|
priority, age_group, status, version, latest_version, created_by, updated_by)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'draft', 1, 1, $11, $11)`,
|
|
[
|
|
item.id,
|
|
item.type,
|
|
item.level,
|
|
item.title,
|
|
item.shortTitle,
|
|
item.description,
|
|
item.category,
|
|
item.subcategory,
|
|
item.priority,
|
|
item.ageGroup,
|
|
userId,
|
|
]
|
|
);
|
|
|
|
// Insertar versión
|
|
const versionId = uuidv4();
|
|
await query(
|
|
`INSERT INTO emerges_content.content_versions
|
|
(version_id, content_item_id, version_number, json_content, created_by)
|
|
VALUES ($1, $2, 1, $3, $4)`,
|
|
[versionId, item.id, 1, JSON.stringify(item.content), userId]
|
|
);
|
|
|
|
// Actualizar current_version_id
|
|
await query(
|
|
`UPDATE emerges_content.content_items
|
|
SET current_version_id = $1
|
|
WHERE id = $2`,
|
|
[versionId, item.id]
|
|
);
|
|
}
|
|
|
|
seedContent();
|
|
|