# 03 — Modelo de datos

Base de datos MySQL `evaluacion_docente` (conexión lógica `klee`, definida en
`ConfigEnv::$DB_CONNECTIONS`). No hay migraciones ni esquema versionado en el repo:
**la fuente de verdad del esquema son los modelos** (`app/models/*Model.php::getOptionsAttributes()`)
y las vistas SQL creadas directamente en la base.

Convenciones transversales:
- PK siempre `Id` (`AutoincrementId`).
- Casi toda tabla tiene `Estado` (1 activo / 0 inactivo) — el borrado suele ser lógico vía este campo, aunque `delete()` sí borra físicamente.
- Auditoría: `UsuarioRegistro`, `FechaRegistro`, `UsuarioModificacion`, `FechaModificacion`.
- El vínculo con el semestre es `IdPeriodo` (FK lógica a `periodos.Id`); en algunas tablas se guarda el **nombre** del periodo (`20242`) en vez del Id — revisar caso por caso.
- Muchas relaciones son **por código de negocio, no por Id**: docente↔materia usa `DocumentoDocente`/`MateriaCodigo`, docente↔programa usa `docentes.Programa = programas.Codigo`. No hay FKs declaradas.

---

## 3.1 Tablas por dominio

### Configuración y seguridad

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `usuarios` | `UsuariosModel` | `Tipo`→roles, `Usuario`, `Contrasena` (MD5), `NoIdentificacion`, `Nombre`, `Apellido`, `Email`, `Telefono`, `Celular`, `Direccion`, `Cargo`, `Categoria`, `Dependencia`, `Foto`, `Sede` (multi, CSV), `Firma`, `Estado` + auditoría |
| `roles` | `RolesModel` | `Tipo` (0 admin / 1–5 público), `Nombre`, `Codigo`, `Inicio` (acción home), `Grupo`, `Descripcion`, `Orden`, `Estado` |
| `permisos` | `PermisosModel` | `IdRol`, `ModuleName`, `Permission` (JSON), `Tabs` (JSON), `Other` |
| `configuraciones` | `ConfiguracionesModel` | `Id` (clave textual), `Grupo`, `Valor`. Almacén clave-valor: pesos de consolidación, plantillas de notificación, banderas |
| `ips_autorizadas` | `IpsAutorizadasModel` | `Nombre`, `Ip`, `Estado` |
| `periodos` | `PeriodosModel` | `Nombre` (`20242`), `Descripcion`, `FechaInicio/Fin`, ventanas por actor (`InicioEstudiantes/FinEstudiantes`, `...Docentes`, `...Directores`, `...DirectoresNacionales`, `...Pares`), banderas `Aplicar*` (LaborDocente, GestionAcademica, NivelFormacion, FormacionContinuada, Estudiantes, Autoevaluacion, Pares, Director, DirectorNacional), `Sincronizacion`, `Mostrar`, `Estado` |
| `sedes` | `SedesModel` | `Nombre`, `Ciudad`, `Codigo`, `Color`, `Estado` |
| `facultades` | `FacultadesModel` | `Nombre`, `Codigo`, `Sede`, `Estado` |
| `programas` | `ProgramasModel` | `Nombre`, `Codigo`, `IdFacultad`, `Tipo`, `Grupo`, `Metodologia`, `Sede`, `CentroCostos`, `Area`, `Estado` |
| `asignaturas` | `AsignaturasModel` | `Nombre`, `Codigo`, `Programa`, `Semestre`, `Creditos`, `Estado` |
| `preguntas` | `PreguntasModel` | `Nombre` (enunciado), `Tipo` (1–6, ver 3.3), `TipoRespuesta`, `Metodologia`, `Area`, `Categoria`, `Peso`, `Orden`, `Estado` |

### Personas evaluadoras / evaluadas

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `estudiantes` | `EstudiantesModel` | `UltimoPeriodo`, `Usuario`, `NoIdentificacion`, `Nombre`, `Apellido`, `Email`, `Codigo`, `Programa` (código), `Metodologia`, `Sincronizar`, `Sede`, `Estado` |
| `docentes` | `DocentesModel` | `Usuario`, `TipoIdentificacion`, `NoIdentificacion`, `Nombre`, `Apellido`, `CorreoInstitucional`, `Nacionalidad`, `Programa` (código), `Metodologia`, `Administrativo`, `Jefe` (→`directores.Id`), `InicioContrato`, `FinContrato`, `Dedicacion` (TC/MT/CA/HC), `IdEscalafon`, `PuntosAcumulados`, `NivelFormacion`, `Foto`, `Sincronizar`, `Sede`, `LoginManual`, `Estado` |
| `directores` | `DirectoresModel` | `Usuario`, `NoIdentificacion`, `Nombre`, `Apellido`, `Email`, `Tipo` (→`roles.Id`: el rol con el que la persona entra al portal), `Programa`, `Reemplazo` (delegación), `Categoria`, `Instrumento` (clave de `$instrumentos`), `Sede`, `Firma`, `Estado` |
| `docentes_educacion` | `DocentesEducacionModel` | `IdDocente`, `Tipo` (1 Formal / 2 Continuada), `Modalidad`, `Titulo`, `Estado` |
| `docentes_experiencia` | `DocentesExperienciaModel` | `IdDocente`, `Entidad`, `FechaIngreso`, `FechaRetiro`, `Estado` |
| `docentes_investigaciones` | `DocentesInvestigacionesModel` | `IdDocente`, `Tipo`, `Entidad`, `Estado` |

### Carga académica

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `docentes_asignaturas` | `DocentesAsignaturasModel` | `IdPeriodo`, `DocumentoDocente`, `MateriaCodigo`, `GrupoCodigo`, `Metodologia`, `Semestre`, `FechaInicio/Final`, `PeriodoAnterior`, `Sincronizar`, `Estado`. **Es la unidad que evalúa el estudiante** |
| `estudiantes_asignaturas` | `EstudiantesAsignaturasModel` | `IdPeriodo`, `EstudianteCodigo`, `MateriaCodigo`, `GrupoCodigo`, `Semestre`, `Metodologia`, `FechaInicio/Final`, `PeriodoAnterior`, `Sincronizar`, `Estado` |
| `docentes_pares` | `DocentesParesModel` | `IdPeriodo`, `IdDocente`, `IdDocentePar`, `Estado`. Asignación de pares evaluadores |

### Evaluaciones (el núcleo)

Tres pares `evaluación` + `respuestas`, con la misma forma:

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `estudiantes_evaluaciones` | `EstudiantesEvaluacionesModel` | `IdPeriodo`, `IdEstudiante`, `IdAsignaturaDocente`→`docentes_asignaturas.Id`, `EstadoEvaluacion`, `Observaciones`, `ObservacionesClasificacion` (POSITIVO/NEUTRAL/OPORTUNIDAD DE MEJORA/NA, generada por IA), `Resultado`, `MateriaCodigo`, `GrupoCodigo`, `Metodologia`, `Codigo` (comprobante), `Estado` |
| `estudiantes_respuestas` | `EstudiantesRespuestasModel` | `IdPeriodo`, `IdEvaluacion`, `IdPregunta`, `Tipo`, `Calificacion` |
| `docentes_evaluaciones` | `DocentesEvaluacionesModel` | `IdPeriodo`, `IdDocente`, `IdDocentePar` (**0 = autoevaluación**, ≠0 = evaluación de par), `EstadoEvaluacion`, `Observaciones`, `Resultado`, `Metodologia`, `Codigo`, `Estado` |
| `docentes_respuestas` | `DocentesRespuestasModel` | igual que arriba |
| `directores_evaluaciones` | `DirectoresEvaluacionesModel` | `IdPeriodo`, `IdDirector`, `IdDocente`, `EstadoEvaluacion`, `Observaciones`, `Resultado`, `Metodologia`, `Codigo`, `Estado` |
| `directores_respuestas` | `DirectoresRespuestasModel` | igual que arriba |
| `consolidado_evaluaciones` | `ConsolidadoEvaluacionesModel` | `IdPeriodo`, `IdDocente`, `Estudiantes`/`Autoevaluaciones`/`Directores` (banderas de aplicabilidad), `TotalEstudiantes`, `TotalAutoevaluaciones`, `TotalDirectores`, `Total` (definitiva), `Detalles` (**JSON con todo el desglose**), `Estado` |
| `resumen_evaluacion` | `ResumenEvaluacionModel` | `IdPeriodo`, `Documento` (cédula docente), `Docente`, `Fortalezas`, `Debilidades`, `Conclusion` (texto generado por IA) |
| `graficas` | `GraficasModel` | `IdPeriodo`, `CodigoGrafica` (GEED, GEEDT, GEEDG, GAD, GED, GEDN), `Informacion` (JSON precalculado), `Fecha`. **Caché diaria de dashboards** |

`EstadoEvaluacion`: `-1` No aplica · `0` Pendiente · `1` En proceso · `2` Finalizada
(`GeneralDataArray::$estados_evaluacion`).

### Gestión docente

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `planes` | `PlanesModel` | `IdPeriodo`, `IdDocente`, `Accion`, `EstadoPlan` (1 En curso, 2 Cumplido, 3 No cumplido), `EstadoDocente`, `Estado` |
| `labor_docente` | `LaborDocenteModel` | `IdPeriodo`, `IdDocente`, `Tipo`→`tipos_labor_docente`, `Objetivo`, `HorasSemanales`, `NoSemanas`, `TotalHoras`, `Director`, `DirectorNacional`, `Progreso`, `ProgresoNacional`, `Impacto`, `ImpactoNacional`, `HorasRealizadas`, `Evidencia`, `EstadoLabor`, `IdPanda`, `Estado` |
| `tipos_labor_docente` | `TipoLaborDocenteModel` | `Nombre`, `Codigo`, `Area`, `IdDirector`, `Categoria`, `Color`, `Estado` |
| `seguimiento_progresos` | `SeguimientoProgresosModel` | `IdUsuario`, `IdLabor`, `Tipo`, `Progreso`, `Comentario`, `Estado` |
| `proyectos` | `ProyectosModel` | `Nombre`, `IdTipoLabor`, `Definicion`, `Acciones`, `Programas` (CSV), `HorasMinimas`, `HorasMaximas`, `Estado` |
| `proyectos_docentes` | `ProyectosDocentesModel` | `IdPeriodo`, `Impacto`, `HorasSemana`, `Semanas`, `Docente`, `IdProyecto`, `IdOds`, `Entregables`, `Estado` |
| `proyectos_entregables` | `ProyectosEntregablesModel` | `IdPeriodo`, `Nombre`, `IdProyecto`, `Fecha`, `Estado` |

### Contratación

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `ods` | `OdsModel` | `IdPeriodo`, `Tipo` (1 TC/MT, 2 HC), `Codigo`, `Consecutivo`, `IdSede`, `IdFacultad`, `IdPrograma`, `Metodologia`, `CentroCostos`, `Fecha`, `Observaciones`, `Recibida`, `FechaRecibida`, `IdUsuarioRecibida`, `EstadoOds`, `Clonada`, `Estado` |
| `ods_docentes` | `OdsDocentesModel` | `IdOds`, `NoIdentificacion`, `TipoDedicacion`, `Vigencia`, `TotalHorasDocencia/Virtual/Preparacion`, `InicioContrato`, `FinContrato`, `SemestreAnterior*`, `Observaciones`, `IdEscalafon`, `Salario`, `CargaPrestacional`, `SalarioTotal`, `Dn`, `FirmaDocente`, `FirmaDirector`, `FirmaCarreraDocente`, `Estado` |
| `requisiciones` | `RequisicionesModel` | `IdPeriodo`, `Codigo`, `Consecutivo`, `IdFacultad`, `IdPrograma`, `Motivo`, `Fecha`, `FechaSeleccion`, `CentroCostos`, `Salario`, `ValorHora`, `InicioLabores/FinLabores`, `TipoContrato`, `Remplazo`, `IdEscalafon`, `NivelFormacion`, `Experiencia`, `Competencias`, `JefeOperativo(+Cargo)`, `JefeFuncional(+Cargo)`, `Observaciones`, `Horario`, `Insumos`, `Permisos`, `Candidatos`, `Ods`, `CandidatoSeleccionado`, `MotivoDevolucion`, `EstadoRequisicion` (1 Solicitada, 2 Recibida, 3 Seleccionada, 4 Finalizada, 11 Rechazada), `Estado` |
| `ops` | `OpsModel` | `IdPeriodo`, `CentroCosto`, `Rubro`, `Accion`, `IdSolicitante`, `Objetivo`, `TipoDocumento`, `Documento`, `Nombre`, `TipoRequerimiento`, `Materia`, `Semestre`, `Creditos`, `DuracionContrato`, `EsVirtual`, `HorasContratadas`, `ValorHora`, `ValorTotal`, `Nivel`, `TipoPago`, `FechaInicio/Fin`, `ExperienciaLaboral`, `ExperienciaDocente`, `AprobacionEstado`, `AprobacionComentario`, `Estado` |
| `contratacion` | `ContratacionModel` | `IdPeriodo`, `Nombre`, `NoIdentificacion`, `NN`, `TipoContrato`, `NoContrato`, `FechaContrato`, `EstadoContrato`, `Estado` |
| `tipos_contrato` | `TiposContratoModel` | `Nombre`, `Codigo`, `TotalHoras`, `MinimoHorasCatedra`, `MaximoHorasCatedra`, `Estado` |

### Escalafón y ascensos

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `escalafon` | `EscalafonModel` | `Nombre`, `Posicion`, `Categoria`, `Nivel`, `PuntajeRequeridoTC/MT/CA`, `SalarioTC/MT/CA/HC`, `Estado` |
| `convocatorias` | `ConvocatoriasModel` | `Nombre`, `InicioPostulacion/FinPostulacion`, `InicioValidacion/FinValidacion`, `VigenciaAscenso`, `Resolucion`, `Responsable`, `PublicarResultados`, `Estado` |
| `docentes_convocatorias` | `DocentesConvocatoriasModel` | `IdDocente`, `IdConvocatoria`, `IdEscalafon`, `Dedicacion`, `PuntosAcumulados`, `EstadoAscenso`, `Requisitos`, `Estado` |
| `postulaciones` | `PostulacionesModel` | `IdDocenteConvocatoria`, `IdProduccionIntelectual`, `Adjunto`, `Calificacion`, `Observaciones`, `Estado` |
| `produccion_intelectual` | `ProduccionIntelectualModel` | `Nombre`, `Categoria`, `Puntuacion`, `Tipo`, `Orden`, `Estado` |

### Notificaciones, alertas y auditoría

| Tabla | Modelo | Columnas principales |
|---|---|---|
| `alertas` | `AlertasModel` | `IdRol`, `IdUsuario`, `Tipo`, `Contenido`, `Visto`, `Estado` |
| `alertas_email` | `AlertasEmailModel` | `Tipo` (`EVA_EST`, …), `Asunto`, `Descripcion`, `Destinatarios` (CSV, hasta 500), `TotalDestinatarios`, `Comentarios`, `EstadoEnvio` (1 = pendiente), auditoría |
| `log_acceso` | `LogAccesoModel` | `IdUsuario`, `IpConexion`, `Accion` (0 acceso público, 1 login, 2 logout), `Fecha`, `Acceso`, `InicioSesion`, `CierreSesion`, `Country`, `ZipCode`, `Location` (geolocalización vía ipinfo.io) |
| `log_urls` | `LogUrlsModel` | `IdAcceso`, `Modulo`, `Accion`, `Parametros`, `ValidateAccess`, `Hora` |
| `log_modules` | `LogModulesModel` | `IdAcceso`, `IdUrl`, `IdUsuario`, `Accion` (1 creación, 5 edición, 9 eliminación), `OldData`/`NewData` (JSON diff), `IdEnvio`, `Tabla`, `Fecha`, `Hora`, `Url`. **Se llena automáticamente en modelos con `$LOG = true`** |
| `debug` | `DebugModel` | `Log`, `FechaRegistro`. Bitácora de desarrollo (`DebugModel::registrar($x)`) |

---

## 3.2 Vistas SQL

Las vistas resuelven los joins que el ORM no sabe hacer. Se consumen con `getAllView()`,
`getByCriteriaView()`, `getByIdView()` y desde `ListaAjax`.

| Vista | Base | Joins | Para qué |
|---|---|---|---|
| `vista_docentes` | `docentes` | programas | Docente + programa/facultad, con `NombreCompleto` |
| `vista_estudiantes` | `estudiantes` | sedes | Estudiante + sede |
| `vista_facultades` | `facultades` | sedes | |
| `vista_programas` | `programas` | programas | Autojoin (grupos) |
| `vista_docentes_asignaturas` | `docentes_asignaturas` | asignaturas, docentes, programas, facultades, sedes | Carga docente denormalizada |
| `vista_estudiantes_asignaturas` | `estudiantes_asignaturas` | asignaturas, estudiantes | Carga del estudiante |
| `vista_estudiantes_docentes` | `docentes_asignaturas` | estudiantes_asignaturas, estudiantes_evaluaciones, docentes, asignaturas, programas, facultades, sedes | **Qué docentes puede evaluar cada estudiante y si ya lo hizo** |
| `vista_estudiantes_evaluaciones` | `estudiantes_evaluaciones` | estudiantes, docentes_asignaturas, docentes, programas, facultades, sedes, asignaturas | Evaluación + docente + materia + estudiante (la vista más usada) |
| `vista_estudiantes_respuestas` | `estudiantes_respuestas` | vista_estudiantes_evaluaciones, preguntas, periodos | Respuesta a nivel de pregunta |
| `vista_docentes_evaluaciones` / `vista_docentes_respuestas` | análogas para autoevaluación y pares | | |
| `vista_docentes_autoevaluaciones` | `docentes` | docentes_evaluaciones, periodos | Estado de autoevaluación por docente |
| `vista_docentes_pares` | `docentes_pares` | docentes, docentes_evaluaciones | |
| `vista_directores_evaluaciones` / `vista_directores_respuestas` | análogas para directores | | |
| `vista_directores_docentes` | `docentes` | directores, directores_evaluaciones, periodos | **Docentes a cargo de un director y su estado de evaluación** |
| `vista_directores_nacionales_docentes` | `docentes` | directores, directores_evaluaciones, labor_docente, tipos_labor_docente, periodos | Idem para líderes de proceso, cruzando por labor |
| `vista_docentes_sin_jefe` | `docentes` | directores | Control de calidad: docentes sin `Jefe` asignado |
| `vista_materias_evaluadas` | `estudiantes_asignaturas` | estudiantes, estudiantes_evaluaciones, programas, facultades, periodos | Cobertura de evaluación por materia |
| `vista_consolidado_evaluaciones` | `consolidado_evaluaciones` | docentes, programas, facultades, sedes | Consolidado con contexto organizacional |
| `vista_resumen_evaluacion` | `resumen_evaluacion` | docentes, programas | Resumen IA + docente |
| `vista_reporte_estudiantes` | `estudiantes` | programas, sedes | Reporte de estados por estudiante |
| `vista_reporte_facultades` | `facultades` | sedes, vista_reporte_estudiantes | Agregado por facultad |
| `vista_reporte_autoevaluaciones` | `vista_docentes_autoevaluaciones` | programas | |
| `vista_reporte_directores` | `directores` | vista_directores_docentes, periodos | Avance de evaluación por director |
| `vista_reporte_directores_nacionales` | `directores` | vista_directores_nacionales_docentes, periodos | Idem líderes |
| `vista_labor_docente` | `labor_docente` | docentes, tipos_labor_docente | |
| `vista_planes` | `planes` | docentes | |
| `vista_ods` / `vista_ods_docentes` | `ods` / `ods_docentes` | programas, sedes, docentes | |
| `vista_proyectos` / `vista_proyectos_docentes` | `proyectos` / `proyectos_docentes` | tipos_labor_docente, docentes, ods | |
| `vista_docentes_convocatorias` | `docentes_convocatorias` | docentes, postulaciones | |
| `vista_postulaciones` | `postulaciones` | docentes_convocatorias, docentes, produccion_intelectual, programas, facultades, sedes | |
| `vista_reporte_docentes_convocatorias` | `docentes_convocatorias` | + postulaciones, produccion_intelectual | Reporte de ascensos |

> Las definiciones completas están en el caché del IDE
> (`.idea/dataSources/<uuid>/storage_v2/_src_/schema/evaluacion_docente.*.zip`) y, por supuesto,
> en `SHOW CREATE VIEW` contra la base. **Si cambias una vista, hazlo en todos los entornos:
> no hay script de migración.**

---

## 3.3 Catálogos en código (`app/config/GeneralDataArray.php`)

Muchos "catálogos" no son tablas sino arrays estáticos. Los más importantes:

```php
$tipos_evaluacion  = [1 => Estudiantes, 2 => Autoevaluación, 3 => Director,
                      4 => Docente Par, 5 => Líder: Funciones Sustantivas,
                      6 => Líder: Docencia Virtual];        // ← preguntas.Tipo
$metodologias      = ['P' => Presencial, 'V' => Virtual];
$dedicaciones      = ['TC','MT','CA','HC'];
$instrumentos      = ['IT','IN','CU','AC','TA','EE','DV'];   // ← directores.Instrumento
$categorias_escalafon = [1 Instructor, 2 Asistente, 3 Principal, 4 Asociado, 5 Titular, -1 Especial];
$estados_evaluacion   = [-1 No Aplica, 0 Pendiente, 1 En Proceso, 2 Finalizada];
$estados_planes       = [1 En Curso, 2 Cumplido, 3 No Cumplido];
$estados_labor        = [0 Pendiente, 1 Realizada, 2 No Realizada];
$estados_requisicion  = [1 Solicitada, 2 Recibida, 3 Seleccionada, 4 Finalizada, 11 Rechazada, 0 Inactivo];
$tipos_ods            = ['1' => 'TC y MT', '2' => 'HC'];
$areas_labor          = ['AC','AI','AP','APD','DIT','EVF'];
$tipos_produccion_intelectual, $tipos_investigacion, $tipos_educacion,
$niveles_formacion, $tipos_requerimientos, $vigencias, $motivos_requisicion, $tipos_roles, ...
```

**Regla mnemotécnica del cuestionario**: `preguntas.Tipo` = tipo de evaluación (1–6).
Para líderes de proceso (`Tipo` 5) las preguntas se filtran además por `Area`, que se obtiene del
`tipos_labor_docente` asociado al director.
