537 lines
15 KiB
Markdown
537 lines
15 KiB
Markdown
# 📐 MODELO DE DATOS CANÓNICO - SISTEMA DE CONTENIDO
|
|
|
|
**Versión:** 1.0.0
|
|
**Fecha:** 2025-01-06
|
|
**Estado:** FASE 4 - Base de Contenido
|
|
|
|
---
|
|
|
|
## 🎯 PROPÓSITO
|
|
|
|
Este documento define el **modelo de datos canónico** para el sistema de gestión de contenido externo de la app TES.
|
|
|
|
**Características:**
|
|
- ✅ Completamente desacoplado del código de la app
|
|
- ✅ No modifica `procedures.ts`, `drugs.ts` ni componentes existentes
|
|
- ✅ Diseñado para durabilidad (10+ años)
|
|
- ✅ Optimizado para uso real de TES en guardia
|
|
- ✅ Soporta formación continua y referencia profesional
|
|
|
|
---
|
|
|
|
## 🏗️ ARQUITECTURA DEL MODELO
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ CONTENT ITEM (Base) │
|
|
│ ┌─────────┬─────────┬──────────────┐ │
|
|
│ │Protocol │ Guide │ Manual │ │
|
|
│ │ Drug │ Checklist│ │ │
|
|
│ └─────────┴─────────┴──────────────┘ │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
│ (asociación)
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ MEDIA RESOURCE │
|
|
│ ┌─────────┬─────────┐ │
|
|
│ │ Image │ Video │ │
|
|
│ └─────────┴─────────┘ │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
│ (versión)
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ CONTENT VERSION │
|
|
│ (Historial y rollback) │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
│ (auditoría)
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ AUDIT LOG │
|
|
│ (Trazabilidad completa) │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 ENTIDADES PRINCIPALES
|
|
|
|
### 1. ContentItem
|
|
|
|
**Descripción:** Entidad base que representa todo el contenido del sistema.
|
|
|
|
**Tipos:**
|
|
- `protocol`: Protocolos operativos (RCP, OVACE, ABCDE, etc.)
|
|
- `guide`: Guías formativas (8 secciones)
|
|
- `manual`: Capítulos del manual completo
|
|
- `drug`: Fármacos del vademécum
|
|
- `checklist`: Checklists reutilizables
|
|
|
|
**Campos Clave:**
|
|
|
|
| Campo | Tipo | Descripción | Ejemplo |
|
|
|-------|------|-------------|---------|
|
|
| `id` | UUID | Identificador único | `550e8400-e29b-41d4-a716-446655440000` |
|
|
| `type` | enum | Tipo de contenido | `protocol` |
|
|
| `slug` | string | Slug para URLs | `rcp-adulto-svb` |
|
|
| `title` | string | Título completo | `RCP Adulto - Soporte Vital Básico` |
|
|
| `clinical_context` | enum | Contexto clínico | `RCP` |
|
|
| `usage_type` | enum | Tipo de uso | `operativo` |
|
|
| `priority` | enum | Prioridad clínica | `critica` |
|
|
| `status` | enum | Estado | `published` |
|
|
| `source_guideline` | enum | Fuente clínica | `ERC` |
|
|
| `version` | string | Versión semántica | `1.0.0` |
|
|
| `content` | JSONB | Contenido específico | Ver secciones siguientes |
|
|
|
|
**Ejemplo Real: RCP Adulto SVB**
|
|
|
|
```typescript
|
|
{
|
|
id: "550e8400-e29b-41d4-a716-446655440000",
|
|
type: "protocol",
|
|
slug: "rcp-adulto-svb",
|
|
title: "RCP Adulto - Soporte Vital Básico",
|
|
short_title: "RCP Adulto SVB",
|
|
description: "Protocolo de reanimación cardiopulmonar básica en adultos",
|
|
clinical_context: "RCP",
|
|
usage_type: "operativo",
|
|
priority: "critica",
|
|
status: "published",
|
|
source_guideline: "ERC",
|
|
source_year: 2021,
|
|
source_url: "https://www.erc.edu/...",
|
|
version: "1.0.0",
|
|
latest_version: "1.0.0",
|
|
content: {
|
|
// Ver ProtocolContent
|
|
},
|
|
tags: ["rcp", "svb", "adulto", "emergencia", "critica"],
|
|
category: "soporte_vital",
|
|
created_at: "2025-01-01T00:00:00Z",
|
|
updated_at: "2025-01-06T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. ProtocolContent
|
|
|
|
**Descripción:** Contenido específico para protocolos operativos.
|
|
|
|
**Estructura:**
|
|
|
|
```typescript
|
|
interface ProtocolContent {
|
|
steps: ProtocolStep[]; // Pasos operativos ordenados
|
|
checklist?: { // Checklist integrado
|
|
enabled: boolean;
|
|
title?: string;
|
|
items: ChecklistItem[];
|
|
};
|
|
inline_doses?: InlineDose[]; // Dosis inline
|
|
context_tools?: ContextTool[]; // Herramientas de contexto
|
|
clinical_sources?: ClinicalSource[]; // Fuentes clínicas
|
|
warnings?: string[]; // Advertencias críticas
|
|
key_points?: string[]; // Puntos clave
|
|
equipment?: string[]; // Equipamiento necesario
|
|
drugs?: string[]; // Referencias a fármacos
|
|
age_group?: 'adulto' | 'pediatrico' | 'neonatal' | 'todos';
|
|
estimated_duration?: string; // Duración estimada
|
|
}
|
|
```
|
|
|
|
**Ejemplo: Paso de Protocolo**
|
|
|
|
```typescript
|
|
{
|
|
order: 7,
|
|
text: "Iniciar compresiones torácicas: 30 compresiones",
|
|
critical: true,
|
|
equipment: ["DEA"],
|
|
time_estimate: "20-30s",
|
|
notes: "Profundidad 5-6 cm, frecuencia 100-120/min"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. GuideContent
|
|
|
|
**Descripción:** Contenido específico para guías formativas (8 secciones).
|
|
|
|
**Estructura:**
|
|
|
|
```typescript
|
|
interface GuideContent {
|
|
sections: GuideSection[]; // Siempre 8 secciones
|
|
related_protocol_id?: string; // Protocolo operativo relacionado
|
|
related_manual_ids?: string[]; // Capítulos de manual relacionados
|
|
learning_objectives?: string[]; // Objetivos de aprendizaje
|
|
prerequisites?: string[]; // Prerrequisitos
|
|
target_audience?: string[]; // Audiencia objetivo
|
|
estimated_time?: string; // Tiempo total estimado
|
|
}
|
|
```
|
|
|
|
**Ejemplo: Sección de Guía**
|
|
|
|
```typescript
|
|
{
|
|
numero: 4,
|
|
titulo: "Medios Visuales y Demostración",
|
|
markdown: "# Medios Visuales...",
|
|
estimated_time: "10 min",
|
|
resources: {
|
|
images: ["660e8400-..."],
|
|
videos: ["660e8400-..."],
|
|
links: [
|
|
{ title: "ERC Guidelines", url: "https://..." }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. MediaResource
|
|
|
|
**Descripción:** Recurso multimedia (imagen o vídeo).
|
|
|
|
**Tipos:**
|
|
- `image`: Infografías, diagramas, fotografías
|
|
- `video`: Vídeos demostrativos, formativos
|
|
|
|
**Campos Clave:**
|
|
|
|
| Campo | Tipo | Descripción | Ejemplo |
|
|
|-------|------|-------------|---------|
|
|
| `id` | UUID | Identificador único | `660e8400-...` |
|
|
| `type` | enum | Tipo de recurso | `image` |
|
|
| `file_url` | string | URL completa | `https://...supabase.co/...` |
|
|
| `title` | string | Título | `Posición de Manos - RCP Adulto` |
|
|
| `alt_text` | string | Texto alternativo | `Posición correcta de manos...` |
|
|
| `priority` | enum | Prioridad | `critica` |
|
|
| `usage_type` | array | Tipos de uso | `["operativo", "formativo"]` |
|
|
| `tags` | array | Tags | `["rcp", "adulto", "compresiones"]` |
|
|
|
|
**Ejemplo: Imagen**
|
|
|
|
```typescript
|
|
{
|
|
id: "660e8400-e29b-41d4-a716-446655440000",
|
|
type: "image",
|
|
file_url: "https://[project].supabase.co/storage/v1/object/public/infografias/rcp/rcp_posicion_manos_adulto.png",
|
|
filename: "rcp_posicion_manos_adulto.png",
|
|
path: "/assets/infografias/rcp/rcp_posicion_manos_adulto.png",
|
|
title: "Posición de Manos - RCP Adulto",
|
|
alt_text: "Posición correcta de manos para compresiones torácicas RCP adulto",
|
|
caption: "Posición correcta de manos para compresiones torácicas",
|
|
tags: ["rcp", "adulto", "compresiones", "posicion", "operativo"],
|
|
block: "bloque-4-soporte-vital",
|
|
priority: "critica",
|
|
usage_type: ["operativo"],
|
|
width: 1200,
|
|
height: 800,
|
|
format: "png",
|
|
file_size: 245678,
|
|
status: "published"
|
|
}
|
|
```
|
|
|
|
**Ejemplo: Vídeo**
|
|
|
|
```typescript
|
|
{
|
|
id: "660e8400-e29b-41d4-a716-446655440002",
|
|
type: "video",
|
|
file_url: "https://[project].supabase.co/storage/v1/object/public/videos/rcp/rcp_adulto_svb.mp4",
|
|
thumbnail_url: "https://[project].supabase.co/storage/v1/object/public/videos/rcp/rcp_adulto_svb_thumb.jpg",
|
|
filename: "rcp_adulto_svb.mp4",
|
|
path: "/assets/videos/rcp/rcp_adulto_svb.mp4",
|
|
title: "RCP Adulto SVB - Técnica Completa",
|
|
alt_text: "Vídeo demostrativo RCP Adulto SVB",
|
|
caption: "Técnica completa de RCP Adulto SVB",
|
|
tags: ["rcp", "adulto", "svb", "video", "operativo"],
|
|
block: "bloque-4-soporte-vital",
|
|
priority: "critica",
|
|
usage_type: ["operativo"],
|
|
duration_seconds: 45,
|
|
video_format: "mp4",
|
|
file_size: 5242880,
|
|
status: "published"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. ContentResourceAssociation
|
|
|
|
**Descripción:** Asociación entre contenido y recursos multimedia.
|
|
|
|
**Propósito:**
|
|
- Define dónde se muestra un recurso en un contenido
|
|
- Permite múltiples asociaciones (mismo recurso en diferentes secciones)
|
|
- Define prioridad y criticidad
|
|
|
|
**Ejemplo:**
|
|
|
|
```typescript
|
|
{
|
|
id: "770e8400-e29b-41d4-a716-446655440000",
|
|
content_item_id: "550e8400-e29b-41d4-a716-446655440000", // RCP Adulto SVB
|
|
media_resource_id: "660e8400-e29b-41d4-a716-446655440000", // Imagen posición manos
|
|
section: "pasos",
|
|
position: 7,
|
|
placement: "inline",
|
|
caption: "Posición correcta de manos para compresiones",
|
|
is_critical: true,
|
|
priority: "critica"
|
|
}
|
|
```
|
|
|
|
**Secciones Comunes:**
|
|
- `pasos`: En pasos de protocolo
|
|
- `checklist`: En checklist integrado
|
|
- `guia_seccion_1` a `guia_seccion_8`: En secciones de guía
|
|
- `manual_intro`: En introducción de manual
|
|
- `manual_contenido`: En contenido principal de manual
|
|
|
|
---
|
|
|
|
### 6. ContentVersion
|
|
|
|
**Descripción:** Versión histórica de un ContentItem.
|
|
|
|
**Propósito:**
|
|
- Versionado semántico (1.2.3)
|
|
- Rollback a versiones anteriores
|
|
- Historial de cambios
|
|
|
|
**Ejemplo:**
|
|
|
|
```typescript
|
|
{
|
|
id: "880e8400-e29b-41d4-a716-446655440000",
|
|
content_item_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
version: "1.1.0",
|
|
content: {
|
|
// Contenido de esta versión
|
|
},
|
|
change_summary: "Añadido paso de verificación de seguridad, actualizado checklist",
|
|
is_breaking: false,
|
|
created_by: "editor-123",
|
|
created_at: "2025-01-10T10:00:00Z",
|
|
is_active: false
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 7. AuditLog
|
|
|
|
**Descripción:** Registro de auditoría de todas las acciones.
|
|
|
|
**Propósito:**
|
|
- Trazabilidad completa
|
|
- Cumplimiento normativo
|
|
- Análisis de uso
|
|
|
|
**Ejemplo:**
|
|
|
|
```typescript
|
|
{
|
|
id: "990e8400-e29b-41d4-a716-446655440000",
|
|
entity_type: "content_item",
|
|
entity_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
action: "publish",
|
|
user_id: "user-456",
|
|
user_role: "editor",
|
|
metadata: {
|
|
previous_status: "approved",
|
|
new_status: "published",
|
|
version: "1.0.0"
|
|
},
|
|
timestamp: "2025-01-06T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔗 RELACIONES ENTRE ENTIDADES
|
|
|
|
### Diagrama de Relaciones
|
|
|
|
```
|
|
ContentItem (1) ──< (N) ContentResourceAssociation (N) >── (1) MediaResource
|
|
│
|
|
│ (1)
|
|
│
|
|
└──< (N) ContentVersion
|
|
|
|
ContentItem (1) ──< (N) AuditLog
|
|
MediaResource (1) ──< (N) AuditLog
|
|
ContentVersion (1) ──< (N) AuditLog
|
|
```
|
|
|
|
### Relaciones Bidireccionales
|
|
|
|
**Protocolo ↔ Guía:**
|
|
- Protocolo tiene `related_guide_ids[]`
|
|
- Guía tiene `related_protocol_id`
|
|
|
|
**Manual ↔ Protocolo/Guía:**
|
|
- Manual tiene `related_protocol_ids[]` y `related_guide_ids[]`
|
|
- Protocolo/Guía tienen `related_manual_ids[]`
|
|
|
|
---
|
|
|
|
## 📊 EJEMPLOS REALES COMPLETOS
|
|
|
|
### Ejemplo 1: Protocolo RCP Adulto SVB
|
|
|
|
Ver `docs/CONTENT_PACK_SPEC.md` sección "Ejemplo Completo"
|
|
|
|
### Ejemplo 2: Guía ABCDE Formativa
|
|
|
|
```typescript
|
|
{
|
|
id: "550e8400-e29b-41d4-a716-446655440010",
|
|
type: "guide",
|
|
slug: "abcde-operativo",
|
|
title: "ABCDE Operativo",
|
|
description: "Guía de refuerzo para comprender el enfoque ABCDE",
|
|
clinical_context: "ABCDE",
|
|
usage_type: "formativo",
|
|
priority: "alta",
|
|
status: "published",
|
|
source_guideline: "MANUAL_TES_DIGITAL",
|
|
version: "1.0.0",
|
|
content: {
|
|
sections: [
|
|
{
|
|
numero: 1,
|
|
titulo: "Introducción y Contexto",
|
|
markdown: "# Introducción...",
|
|
resources: {
|
|
images: ["660e8400-..."] // Infografía introducción
|
|
}
|
|
},
|
|
// ... 7 secciones más
|
|
],
|
|
related_protocol_id: "550e8400-...", // ABCDE Operativo
|
|
learning_objectives: [
|
|
"Comprender ABCDE como estructura mental",
|
|
"Aplicar ABCDE en todas las emergencias"
|
|
]
|
|
},
|
|
related_protocol_ids: ["550e8400-..."],
|
|
tags: ["abcde", "evaluacion", "formacion"]
|
|
}
|
|
```
|
|
|
|
### Ejemplo 3: Fármaco Adrenalina
|
|
|
|
```typescript
|
|
{
|
|
id: "550e8400-e29b-41d4-a716-446655440020",
|
|
type: "drug",
|
|
slug: "adrenalina",
|
|
title: "Adrenalina (Epinefrina)",
|
|
description: "Fármaco de primera línea en PCR y anafilaxia",
|
|
clinical_context: "FARMACOLOGIA",
|
|
usage_type: "referencia",
|
|
priority: "critica",
|
|
status: "published",
|
|
source_guideline: "ERC",
|
|
source_year: 2021,
|
|
version: "1.0.0",
|
|
content: {
|
|
generic_name: "Adrenalina",
|
|
trade_name: "Adrenalina",
|
|
category: "cardiovascular",
|
|
presentation: "Ampollas 1mg/1ml",
|
|
adult_dose: "1mg IV cada 3-5 min (PCR)",
|
|
pediatric_dose: "0.01 mg/kg IV cada 3-5 min (PCR)",
|
|
routes: ["IV", "IO", "IM"],
|
|
indications: ["PCR", "Anafilaxia", "Shock anafiláctico"],
|
|
contraindications: ["Hipersensibilidad"],
|
|
side_effects: ["Taquicardia", "Hipertensión"],
|
|
notes: [
|
|
"Primera línea en PCR",
|
|
"En anafilaxia: 0.3-0.5 mg IM"
|
|
],
|
|
critical_points: [
|
|
"NO diluir en PCR",
|
|
"Administrar lo antes posible"
|
|
]
|
|
},
|
|
tags: ["adrenalina", "epinefrina", "pcr", "anafilaxia", "critica"]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ VALIDACIONES Y CONSTRAINTS
|
|
|
|
### Validaciones de Contenido
|
|
|
|
1. **ContentItem:**
|
|
- `slug` debe ser único
|
|
- `version` debe seguir semver (1.2.3)
|
|
- `status` debe ser válido según flujo
|
|
- `clinical_context` debe ser válido
|
|
|
|
2. **ProtocolContent:**
|
|
- `steps` debe tener al menos 1 paso
|
|
- `steps[].order` debe ser secuencial (1, 2, 3...)
|
|
- `checklist.items` debe tener al menos 1 item si `enabled: true`
|
|
|
|
3. **GuideContent:**
|
|
- `sections` debe tener exactamente 8 secciones
|
|
- `sections[].numero` debe ser 1-8
|
|
- `sections[].markdown` no debe estar vacío
|
|
|
|
4. **MediaResource:**
|
|
- `file_url` debe ser URL válida
|
|
- `alt_text` no debe estar vacío (accesibilidad)
|
|
- Si `type: 'video'`, `duration_seconds` debe estar presente
|
|
|
|
5. **ContentResourceAssociation:**
|
|
- `content_item_id` debe existir
|
|
- `media_resource_id` debe existir
|
|
- `section` + `position` debe ser único por `content_item_id`
|
|
|
|
---
|
|
|
|
## 🔍 BÚSQUEDA Y FILTRADO
|
|
|
|
### Índices Recomendados
|
|
|
|
1. **Búsqueda de texto:**
|
|
- `title`, `description`, `tags` → Índice GIN (full-text search)
|
|
|
|
2. **Filtrado por tipo:**
|
|
- `type`, `usage_type`, `status`, `priority` → Índices B-tree
|
|
|
|
3. **Búsqueda por contexto:**
|
|
- `clinical_context`, `category` → Índices B-tree
|
|
|
|
4. **Relaciones:**
|
|
- `related_*_ids` → Índices GIN (array search)
|
|
|
|
---
|
|
|
|
## 📚 REFERENCIAS
|
|
|
|
- **Interfaces TypeScript:** `admin-panel/shared/types/content-canonical.ts`
|
|
- **Schema SQL:** `docs/SUPABASE_SCHEMA.sql`
|
|
- **Content Pack Spec:** `docs/CONTENT_PACK_SPEC.md`
|
|
- **Plan Técnico:** `docs/PLAN_TECNICO_SISTEMA_CONTENIDO.md`
|
|
|
|
---
|
|
|
|
**Fin del Documento**
|
|
|