codigo0/docs/FASE_C_MODELO_DATOS_CANONICO.md

17 KiB

🅲 FASE C - MODELO DE DATOS CANÓNICO

Proyecto: Guía TES - Sistema de Gestión de Contenido
Fecha: 2025-01-06
Arquitecto: Sistema de Contenido
Estado: 📋 PLANIFICACIÓN


🎯 OBJETIVO

Definir el modelo de datos lógico (conceptual) que soporte:

  • Base de datos en nuestro servidor (PostgreSQL/SQLite)
  • Panel de administración completo
  • Exportación a JSON (Content Pack)
  • Consumo offline por la app PWA
  • Versionado y reversibilidad
  • Estados de validación (draft → review → approved → published)

⚠️ IMPORTANTE: Este es un modelo CONCEPTUAL, no tecnológico. Define QUÉ datos necesitamos, no CÓMO implementarlos.


📊 ENTIDADES PRINCIPALES

1. CONTENIDO (Content Items)

Representa cualquier pieza de contenido: protocolos, guías, manual, fármacos, checklists.

1.1 Identificación y Clasificación

ContentItem {
  // IDENTIFICACIÓN ÚNICA
  id: UUID                    // ID único e inmutable
  slug: String                // Identificador legible (ej: "control-hemorragias")
  version: String             // Versión semántica (ej: "1.2.3")
  latest_version: Boolean     // ¿Es la última versión?
  
  // CLASIFICACIÓN
  type: Enum                  // 'protocol' | 'guide' | 'manual' | 'drug' | 'checklist'
  category: String            // Categoría clínica (ej: "soporte_vital", "trauma")
  subcategory: String?        // Subcategoría opcional (ej: "hemorragia", "shock")
  
  // CAPA FUNCIONAL (CRÍTICO: NO MEZCLAR)
  functional_layer: Enum      // 'operativa' | 'formativa' | 'referencia'
  
  // METADATOS BÁSICOS
  title: String               // Título completo
  short_title: String?       // Título corto (para UI)
  description: String        // Descripción breve
  keywords: String[]         // Palabras clave para búsqueda
  
  // CONTENIDO ESPECÍFICO
  content: JSON              // Estructura específica por tipo (ver abajo)
  
  // PRIORIDAD Y CLASIFICACIÓN CLÍNICA
  priority: Enum             // 'critical' | 'high' | 'medium' | 'low'
  clinical_priority: Enum?   // Prioridad clínica específica (si aplica)
  age_group: Enum?           // 'adulto' | 'pediatrico' | 'neonatal' | 'todos'
  
  // ESTADO Y VALIDACIÓN
  status: Enum               // 'draft' | 'in_review' | 'approved' | 'published' | 'archived'
  validation_workflow: JSON?  // Historial de validación (ver abajo)
  
  // RELACIONES
  related_content: UUID[]    // IDs de contenido relacionado (bidireccional)
  parent_content: UUID?      // Si es versión de otro contenido
  replaces_content: UUID?    // Si reemplaza a otro contenido
  
  // RECURSOS ASOCIADOS
  media_resources: UUID[]    // IDs de recursos multimedia asociados
  
  // METADATOS DE GESTIÓN
  created_by: UUID           // Usuario que creó
  created_at: Timestamp      // Fecha de creación
  updated_by: UUID           // Usuario que actualizó
  updated_at: Timestamp      // Fecha de última actualización
  published_at: Timestamp?   // Fecha de publicación (si está published)
  published_by: UUID?        // Usuario que publicó
  
  // FUENTE Y TRAZABILIDAD
  source_guideline: String?  // Fuente clínica (ej: "Manual TES Digital", "ERC 2021")
  source_reference: String?  // Referencia específica (ej: "BLOQUE_03_6_CONTROL_HEMORRAGIAS.md")
  
  // METADATOS ADICIONALES
  metadata: JSON             // Metadatos flexibles (tiempo_lectura, dificultad, etc.)
}

1.2 Estructura de content por Tipo

PROTOCOLO (Operativa):

{
  "steps": [
    {
      "order": 1,
      "text": "Texto del paso",
      "critical": false,
      "verification": "¿Verificación opcional?",
      "warnings": ["Advertencia 1", "Advertencia 2"]
    }
  ],
  "warnings": ["Advertencia global 1"],
  "key_points": ["Punto clave 1"],
  "equipment": ["Equipo 1", "Equipo 2"],
  "drugs": ["Fármaco 1", "Fármaco 2"],
  "checklist_mode": true,
  "checklist_steps": ["Paso checklist 1"]
}

GUÍA (Formativa):

{
  "secciones": [
    {
      "numero": 1,
      "titulo": "Título de sección",
      "archivo": "SECCION_01_NOMBRE.md",
      "ruta": "/docs/consolidado/SECCION_01_NOMBRE.md",
      "contenido_markdown": "...",
      "tiempo_estimado": 5
    }
  ],
  "protocolo_operativo": {
    "id": "uuid-protocolo",
    "titulo": "Título del protocolo",
    "ruta": "/ruta-protocolo"
  },
  "scorm_available": false,
  "tiempo_total_estimado": 40
}

MANUAL (Referencia):

{
  "parte": 1,
  "parte_nombre": "Nombre de parte",
  "bloque": 0,
  "bloque_nombre": "Nombre de bloque",
  "capitulo": "1.1.1",
  "ruta_archivo": "/manual/BLOQUE_XX/ARCHIVO.md",
  "ruta_url": "/manual/parte-i/bloque-0/1.1.1",
  "contenido_markdown": "...",
  "nivel_dificultad": "basico",
  "importancia": "alta",
  "tiempo_lectura": 15,
  "navegacion": {
    "anterior": "1.0.1",
    "siguiente": "1.1.2",
    "relacionados": ["1.1.2", "1.1.3"]
  }
}

FÁRMACO (Referencia):

{
  "generic_name": "Nombre genérico",
  "trade_name": "Nombre comercial",
  "category": "cardiovascular",
  "presentation": "Presentación",
  "adult_dose": "Dosis adulto",
  "pediatric_dose": "Dosis pediátrica",
  "routes": ["IV", "IM"],
  "dilution": "Dilución",
  "indications": ["Indicación 1"],
  "contraindications": ["Contraindicación 1"],
  "side_effects": ["Efecto adverso 1"],
  "antidote": "Antídoto",
  "notes": ["Nota 1"],
  "critical_points": ["Punto crítico TES 1"],
  "source": "Fuente"
}

CHECKLIST (Operativa):

{
  "phase": "pre_escena",
  "sections": [
    {
      "id": "seccion-1",
      "title": "Título sección",
      "category": "oxigeno",
      "items": [
        {
          "id": "item-1",
          "text": "Texto del item",
          "critical": false
        }
      ],
      "notes": "Notas opcionales"
    }
  ]
}

2. RECURSOS MULTIMEDIA (Media Resources)

Representa imágenes, vídeos y otros recursos visuales.

MediaResource {
  // IDENTIFICACIÓN
  id: UUID                    // ID único
  filename: String            // Nombre del archivo (ej: "hemorragia_presion_directa.jpg")
  path: String                // Ruta relativa (ej: "/assets/infografias/hemorragia/...")
  url: String?                // URL completa (si está en CDN)
  
  // TIPO Y FORMATO
  type: Enum                  // 'image' | 'video' | 'audio' | 'document'
  format: String              // 'jpg' | 'png' | 'svg' | 'mp4' | 'webm' | 'pdf'
  mime_type: String           // 'image/jpeg', 'video/mp4', etc.
  
  // METADATOS VISUALES
  alt: String                 // Texto alternativo (accesibilidad)
  caption: String?            // Caption opcional
  title: String?              // Título del recurso
  
  // CLASIFICACIÓN
  functional_layer: Enum      // 'operativa' | 'formativa' | 'referencia'
  category: String?           // Categoría (ej: "hemorragia", "rcp")
  tags: String[]              // Tags para búsqueda
  
  // DIMENSIONES Y TAMAÑO
  width: Integer?             // Ancho (para imágenes/vídeos)
  height: Integer?            // Alto (para imágenes/vídeos)
  file_size: Integer          // Tamaño en bytes
  duration: Integer?          // Duración en segundos (para vídeos/audio)
  
  // PRIORIDAD Y USO
  priority: Enum              // 'critical' | 'high' | 'medium' | 'low'
  usage_type: Enum[]          // ['operativo', 'formativo'] - dónde se usa
  
  // FUENTE Y ATRIBUCIÓN
  source: String?             // Fuente del recurso
  attribution: String?        // Atribución si es necesario
  license: String?             // Licencia (ej: "CC BY 4.0")
  
  // ESTADO
  status: Enum                // 'draft' | 'approved' | 'published' | 'archived'
  
  // METADATOS DE GESTIÓN
  created_by: UUID
  created_at: Timestamp
  updated_by: UUID
  updated_at: Timestamp
  uploaded_at: Timestamp      // Fecha de subida
  
  // METADATOS ADICIONALES
  metadata: JSON              // Metadatos flexibles
}

3. RELACIONES CONTENIDO ⇄ RECURSOS

Tabla de relación muchos-a-muchos entre contenido y recursos multimedia.

ContentResourceRelation {
  id: UUID
  content_id: UUID            // FK a ContentItem
  resource_id: UUID           // FK a MediaResource
  
  // CONTEXTO DE LA RELACIÓN
  context: String?            // Dónde se usa (ej: "step-3", "seccion-2", "header")
  order: Integer?             // Orden de aparición
  is_primary: Boolean         // ¿Es recurso principal?
  
  // METADATOS
  created_at: Timestamp
  created_by: UUID
}

4. RELACIONES CONTENIDO ⇄ CONTENIDO

Tabla de relación bidireccional entre contenidos (Protocolo ↔ Guía ↔ Manual).

ContentRelation {
  id: UUID
  source_content_id: UUID     // FK a ContentItem (origen)
  target_content_id: UUID     // FK a ContentItem (destino)
  
  // TIPO DE RELACIÓN
  relation_type: Enum         // 'protocol_to_guide' | 'guide_to_protocol' | 
                              // 'protocol_to_manual' | 'guide_to_manual' |
                              // 'manual_to_protocol' | 'manual_to_guide'
  
  // METADATOS
  is_bidirectional: Boolean   // ¿Es bidireccional?
  order: Integer?             // Orden de aparición
  metadata: JSON?             // Metadatos adicionales
  
  created_at: Timestamp
  created_by: UUID
}

5. VERSIONADO

Historial de versiones de contenido.

ContentVersion {
  id: UUID
  content_id: UUID            // FK a ContentItem (versión actual)
  version: String             // Versión semántica (ej: "1.2.3")
  previous_version_id: UUID?  // FK a ContentVersion (versión anterior)
  
  // CAMBIOS
  change_summary: String      // Resumen de cambios
  change_details: JSON        // Detalles de cambios (qué cambió)
  change_type: Enum           // 'major' | 'minor' | 'patch'
  
  // METADATOS
  created_at: Timestamp
  created_by: UUID
  published_at: Timestamp?
  published_by: UUID?
}

6. VALIDACIÓN Y WORKFLOW

Historial de validación y estados.

ValidationWorkflow {
  id: UUID
  content_id: UUID            // FK a ContentItem
  
  // ESTADO ACTUAL
  current_status: Enum        // 'draft' | 'in_review' | 'approved' | 'rejected' | 'published'
  
  // HISTORIAL
  history: JSON[]             // Array de eventos:
                              // [
                              //   {
                              //     "status": "draft",
                              //     "timestamp": "...",
                              //     "user_id": "...",
                              //     "notes": "..."
                              //   },
                              //   {
                              //     "status": "in_review",
                              //     "timestamp": "...",
                              //     "user_id": "...",
                              //     "reviewer_id": "...",
                              //     "notes": "..."
                              //   }
                              // ]
  
  // METADATOS
  submitted_at: Timestamp?
  submitted_by: UUID?
  reviewed_at: Timestamp?
  reviewed_by: UUID?
  approved_at: Timestamp?
  approved_by: UUID?
  rejected_at: Timestamp?
  rejected_by: UUID?
  rejection_reason: String?
  
  updated_at: Timestamp
}

7. USUARIOS Y ROLES

Gestión de usuarios del panel de administración.

User {
  id: UUID
  email: String               // Email único
  username: String            // Nombre de usuario
  password_hash: String       // Hash de contraseña
  role: Enum                  // 'super_admin' | 'admin' | 'editor_clinico' | 
                             // 'editor_formativo' | 'revisor' | 'viewer' | 
                             // 'tes_validador' | 'formador' | 'medico'
  
  // PERMISOS GRANULARES
  permissions: JSON           // {
                              //   "content:create": true,
                              //   "content:edit": true,
                              //   "content:delete": false,
                              //   "content:publish": false,
                              //   "content:validate": true,
                              //   "media:upload": true,
                              //   ...
                              // }
  
  // ESTADO
  is_active: Boolean
  last_login: Timestamp?
  
  // METADATOS
  created_at: Timestamp
  updated_at: Timestamp
}

8. AUDITORÍA

Registro de todas las acciones importantes.

AuditLog {
  id: UUID
  entity_type: String         // 'content_item' | 'media_resource' | 'user' | ...
  entity_id: UUID             // ID de la entidad
  
  // ACCIÓN
  action: Enum                // 'create' | 'update' | 'delete' | 'publish' | 
                              // 'approve' | 'reject' | 'submit' | ...
  action_details: JSON?        // Detalles de la acción
  
  // USUARIO Y CONTEXTO
  user_id: UUID               // FK a User
  user_role: String           // Rol del usuario (snapshot)
  ip_address: String?
  user_agent: String?
  
  // METADATOS
  timestamp: Timestamp
  metadata: JSON?             // Metadatos adicionales
}

🔗 RELACIONES ENTRE ENTIDADES

ContentItem (1) ──< (N) ContentResourceRelation (N) >── (1) MediaResource
ContentItem (1) ──< (N) ContentRelation (N) >── (1) ContentItem
ContentItem (1) ──< (N) ContentVersion
ContentItem (1) ──< (1) ValidationWorkflow
User (1) ──< (N) ContentItem (created_by, updated_by, published_by)
User (1) ──< (N) MediaResource (created_by, updated_by)
User (1) ──< (N) AuditLog

📦 EXPORTACIÓN A CONTENT PACK

El Content Pack JSON se genera desde estas entidades:

{
  "version": "1.0.0",
  "generatedAt": "2025-01-06T12:00:00Z",
  "hash": "sha256:...",
  "content": {
    "protocols": [
      // ContentItem donde type='protocol' AND status='published' AND functional_layer='operativa'
      // Incluye: id, slug, title, short_title, description, content, priority, etc.
    ],
    "guides": [
      // ContentItem donde type='guide' AND status='published' AND functional_layer='formativa'
    ],
    "drugs": [
      // ContentItem donde type='drug' AND status='published' AND functional_layer='referencia'
    ],
    "checklists": [
      // ContentItem donde type='checklist' AND status='published' AND functional_layer='operativa'
    ],
    "manuals": [
      // ContentItem donde type='manual' AND status='published' AND functional_layer='referencia'
    ]
  },
  "media": {
    "images": [
      // MediaResource donde type='image' AND status='published'
      // Incluye: id, filename, path, url, alt, caption, etc.
    ],
    "videos": [
      // MediaResource donde type='video' AND status='published'
    ]
  },
  "relations": [
    // ContentRelation donde ambos contenidos están published
    // Incluye: source_content_id, target_content_id, relation_type
  ],
  "content_resource_relations": [
    // ContentResourceRelation donde ambos están published
    // Incluye: content_id, resource_id, context, order
  ]
}

🎯 PRINCIPIOS DE DISEÑO

1. Separación de Capas Funcionales

CRÍTICO: El campo functional_layer garantiza que:

  • Contenido operativo NO se mezcle con formativo
  • Contenido formativo NO se mezcle con referencia
  • Cada capa tiene su propia estructura de content

2. Versionado Semántico

  • Major (X.0.0): Cambios incompatibles
  • Minor (0.X.0): Nuevas funcionalidades compatibles
  • Patch (0.0.X): Correcciones compatibles

3. Estados y Workflow

draft → in_review → approved → published
  ↓         ↓           ↓
rejected  rejected   archived

4. Trazabilidad Completa

  • Todo cambio queda registrado en AuditLog
  • Historial de versiones en ContentVersion
  • Workflow de validación en ValidationWorkflow

5. Relaciones Bidireccionales

  • ContentRelation permite relaciones bidireccionales
  • Campo is_bidirectional indica si la relación es simétrica
  • El Content Pack incluye todas las relaciones

6. Offline-First

  • El Content Pack es autocontenido (incluye todo lo necesario)
  • Las relaciones están incluidas en el pack
  • Los recursos multimedia tienen URLs relativas o absolutas

📝 NOTAS TÉCNICAS

Campos JSON Flexibles

Los campos content y metadata son JSON para permitir:

  • Estructuras específicas por tipo de contenido
  • Extensibilidad sin cambios de esquema
  • Metadatos adicionales sin migraciones

UUIDs vs IDs Numéricos

  • UUIDs para IDs principales (mejor para distribución, sin colisiones)
  • Strings para slugs (legibles, SEO-friendly)

Timestamps

  • Todos los timestamps en formato ISO 8601
  • Timezone: UTC
  • Incluir created_at y updated_at en todas las entidades principales

Soft Deletes

  • No eliminar físicamente contenido publicado
  • Usar campo status='archived' para "eliminación"
  • Mantener historial completo

🚀 PRÓXIMOS PASOS

  1. Validar modelo con stakeholders clínicos
  2. Diseñar esquema de base de datos (PostgreSQL recomendado)
  3. Implementar migraciones de base de datos
  4. Crear API REST para panel de administración
  5. Implementar generación de Content Pack desde base de datos
  6. Crear panel de administración con CRUD completo

Última actualización: 2025-01-06
Estado: Modelo conceptual completo