# 🅲 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):** ```json { "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):** ```json { "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):** ```json { "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):** ```json { "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):** ```json { "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: ```json { "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