# Base de Datos — Parte Diario Docente

## Segregación por Programa

El sistema soporta múltiples programas de estudio en un mismo instituto. Cada programa tiene su propio coordinador, docentes, cursos, horarios y registros. La segregación se aplica a nivel de aplicación (controladores) mediante filtros `WHERE programa_id = X`.

### Tablas con `programa_id` directo

| Tabla | Columna | FK |
|---|---|---|
| `programas` | (PK propia) | `coordinador_personal_id` → `personal(id)` |
| `docentes` | `programa_id` | → `programas(id)` (nullable, SET NULL on delete) |
| `cursos` | `programa_id` | → `programas(id)` (NOT NULL, RESTRICT on delete) |

### Tablas sin `programa_id` directo (heredan por FK)

| Tabla | Cadena de herencia |
|---|---|
| `asignaciones` | `curso_id` → `cursos.programa_id` y `docente_id` → `docentes.programa_id` |
| `horarios` | `asignacion_id` → `asignaciones` → `cursos.programa_id` |
| `registros_sesion` | `horario_id` → `horarios` → `asignaciones` → `cursos.programa_id` |
| `disponibilidad_docente` | `docente_id` → `docentes.programa_id` |

### Tablas compartidas (sin programa, globales del instituto)

- `periodos_academicos`
- `feriados`
- `franjas_academicas`
- `horario_academico`
- `config_sistema`

### Disponibilidad docente

La tabla `disponibilidad_docente` existe en la base de datos pero no se usa en la interfaz actual. Las rutas y controladores fueron eliminados.

### Cómo se asigna un coordinador a un programa

1. Se crea un usuario con rol `coordinador`
2. Se crea su registro en `personal` (con DNI, nombres, etc.)
3. En `programas`, el campo `coordinador_personal_id` apunta a ese registro de `personal`
4. El método `BaseController::getCoordinadorPrograma()` obtiene el programa del coordinador autenticado

### Cómo se filtra por programa (controladores)

Todos los controladores del módulo `Coordinador\*` usan:

```php
$programaId = $this->getProgramaId();
// luego filtran toda consulta con:
->where('tabla.programa_id', $programaId)
```

Ejemplos:
- `CursosController` — filtra `cursos.programa_id`
- `DocentesController` — solo lectura, filtra `docentes.programa_id`
- `AsignacionesController` — filtra por JOIN con `cursos.programa_id` y valida que curso y docente pertenezcan al mismo programa
- `HorariosController`, `RegistrosController`, `DashboardController`, `ReportesController` — similar mediante JOINs

## Esquema completo

### `users` (CodeIgniter Shield)

| Columna | Tipo |
|---|---|
| id | INT(11) UNSIGNED PK |
| username | VARCHAR(30) |
| status | VARCHAR(255) |
| status_message | VARCHAR(255) |
| active | TINYINT(1) |
| last_active | DATETIME |
| created_at | DATETIME |
| updated_at | DATETIME |
| deleted_at | DATETIME |

### `auth_groups_users`

| Columna | Tipo |
|---|---|
| id | INT(11) UNSIGNED PK |
| user_id | INT(11) UNSIGNED FK → `users(id)` |
| group | VARCHAR(255) — `admin`, `coordinador`, `docente` |

### `personal`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| user_id | INT UNSIGNED FK → `users(id)` (UK) |
| dni | VARCHAR(20) (UK) |
| apellido_paterno | VARCHAR(75) |
| apellido_materno | VARCHAR(75) |
| primer_nombre | VARCHAR(75) |
| segundo_nombre | VARCHAR(75) |
| fecha_nacimiento | DATE |
| genero | ENUM('M','F','Otro') |
| celular | VARCHAR(20) |
| telefono_fijo | VARCHAR(20) |
| correo_personal | VARCHAR(255) |
| direccion_domicilio | VARCHAR(255) |

### `programas`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| nombre | VARCHAR(150) |
| coordinador_personal_id | INT UNSIGNED FK → `personal(id)` (nullable) |
| estado_activo | TINYINT(1) |

### `docentes`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| personal_id | INT UNSIGNED FK → `personal(id)` (UK) |
| programa_id | INT UNSIGNED FK → `programas(id)` (nullable) |

### `cursos`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| codigo_curso | VARCHAR(20) (UK) |
| nombre | VARCHAR(150) |
| periodo_id | INT UNSIGNED FK → `periodos_academicos(id)` (nullable, SET NULL) |
| creditos | TINYINT UNSIGNED |
| horas_teoria_semanales | TINYINT UNSIGNED |
| horas_practica_semanales | TINYINT UNSIGNED |
| tipo_materia | ENUM('Teórico','Práctico','Laboratorio','Virtual') |
| programa_id | INT UNSIGNED FK → `programas(id)` (NOT NULL) |
| estado_activo | TINYINT(1) |

### `periodos_academicos`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| codigo | VARCHAR(20) |
| descripcion | VARCHAR(150) (nullable) |
| fecha_inicio | DATE |
| fecha_fin | DATE |
| es_actual | TINYINT(1) |
| estado_activo | TINYINT(1) |

### `asignaciones`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| curso_id | INT UNSIGNED FK → `cursos(id)` |
| docente_id | INT UNSIGNED FK → `docentes(id)` |
| periodo_id | INT UNSIGNED FK → `periodos_academicos(id)` |
| fecha_asignacion | DATETIME |
| UK | (curso_id, docente_id, periodo_id) |

### `horarios`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| asignacion_id | INT UNSIGNED FK → `asignaciones(id)` |
| dia_semana | ENUM('lunes','martes','miercoles','jueves','viernes','sabado','domingo') |
| hora_inicio | TIME |
| hora_fin | TIME |
| estado_activo | TINYINT(1) |

### `registros_sesion`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| horario_id | INT UNSIGNED FK → `horarios(id)` |
| fecha_clase | DATE |
| hora_envio_sistema | DATETIME |
| tema_silabo_semana | TINYINT UNSIGNED |
| actividad_realizada | TEXT |
| tareas_dejadas | TEXT |
| observacion | TEXT |
| estado_registro | ENUM('en_tiempo','fuera_de_plazo') |
| estado_revision_coordinador | ENUM('Pendiente','Aprobado','Observado') |
| comentario_coordinador | TEXT |
| dispositivo_origen | ENUM('web','app') |
| UK | (horario_id, fecha_clase) |

### `disponibilidad_docente`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| docente_id | INT UNSIGNED FK → `docentes(id)` |
| dia_semana | ENUM('lunes'…'domingo') |
| hora_inicio | TIME |
| hora_fin | TIME |
| estado_activo | TINYINT(1) |

### `feriados`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| fecha | DATE |
| descripcion | VARCHAR(100) |
| es_laborable | TINYINT(1) |

### `franjas_academicas`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| nombre | VARCHAR(100) |
| hora_inicio | TIME |
| hora_fin | TIME |
| tipo | ENUM('hora_clase','recreo','almuerzo') |
| duracion_minutos | INT UNSIGNED |
| estado_activo | TINYINT(1) |

### `horario_academico`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| hora_inicio_jornada | TIME (default 07:00:00) |
| hora_fin_jornada | TIME (default 22:00:00) |
| dias_laborales | SET('lunes','martes','miercoles','jueves','viernes','sabado','domingo') |
| tolerancia_default | INT (default 20) |
| usar_franjas_academicas | TINYINT(1) (default 0) |

### `config_sistema`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| clave | VARCHAR(100) (UK) |
| valor | TEXT |
| descripcion | VARCHAR(255) |

### `auditoria`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| user_id | INT UNSIGNED FK → `users(id)` (nullable, SET NULL on delete) |
| tabla_afectada | VARCHAR(50) |
| accion | ENUM('insercion','actualizacion','eliminacion','login','logout','login_fallido','bloqueo_cuenta') |
| descripcion_cambio | TEXT (nullable) |
| fecha_evento | DATETIME (default CURRENT_TIMESTAMP) |
| direccion_ip | VARCHAR(45) (nullable, soporta IPv6) |

> **Nota:** Esta tabla es escrita por el trait `App\Traits\AuditLogger` utilizado en todos los controladores del sistema. Cada acción crítica (crear, actualizar, eliminar, login, logout) genera un registro automático. El panel de auditoría (`admin/auditoria`) permite consultar y filtrar estos registros.

### `notificaciones`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| user_id | INT UNSIGNED FK → `users(id)` |
| titulo | VARCHAR(255) |
| mensaje | TEXT |
| fue_leida | TINYINT(1) (default 0) |
| fecha_envio | DATETIME |

### `solicitudes_password`

| Columna | Tipo |
|---|---|
| id | INT UNSIGNED PK |
| user_id | INT UNSIGNED FK → `users(id)` |
| estado | ENUM('pendiente','atendida','rechazada') (default 'pendiente') |
| comentario | VARCHAR(255) (nullable) |
| created_at | DATETIME |
| atendida_at | DATETIME (nullable) |

## Relaciones (DER textual)

```
users
  ├── auth_groups_users (user_id, group)
  └── personal (user_id)
        ├── programas (coordinador_personal_id)
        └── docentes (personal_id)
              ├── programas (programa_id)
              └── asignaciones (docente_id)
                    ├── cursos (curso_id)
                    │     └── programas (programa_id)
                    ├── periodos_academicos (periodo_id)
                    └── horarios (asignacion_id)
                          └── registros_sesion (horario_id)
```

## Roles y permisos

| Grupo | Acceso |
|---|---|
| `admin` | Todo el sistema (`admin/*`) |
| `coordinador` | Gestión educativa (`coordinador/*`) + docente (`docente/*`) |
| `docente` | Registro de clases (`docente/*`) |

## Backups

Los backups se realizan a nivel de base de datos completa usando `mysqldump` o similar. No hay respaldo por programa individual; si se necesita exportar datos de un solo programa, debe hacerse con scripts personalizados que filtren por `programa_id`.
