Academia Deltamontero · Documentación
Desarrollolocal_deltamodules

Importador de módulos

Asistente "Crear módulo desde archivo" (import.php), lectores en Python (parse-curriculum.py, parse-document.py), plantilla Excel, bancos de preguntas e IA opcional.

El asistente Nuevo módulo (import.php, también "Crear módulo desde archivo" en Administración del sitio → Cursos) crea un módulo con la estructura oficial de la Academia a partir de un archivo, de un documento libre o desde cero. El módulo siempre queda como borrador (oculto) hasta publicarlo.

DatoValor
Página/local/deltamodules/import.php
Capacidadmoodle/course:create (por perfil, el Jefe de Talento Humano; y el super administrador)
Página externa de administraciónlocal_deltamodules_import (settings.php, sección courses)
Estado entre pasos$SESSION->deltamodimport
Clasesimporter, template, academy, ai_author, formularios classes/form/import_*.php
Lectores/parse-curriculum.py y /parse-document.py (Python 3, copiados a la raíz del contenedor)

Pasos del asistente

Paso (step)Qué hace
uploadElige el modo (mode) y sube el archivo.
aiSolo en modo documento con IA: completa los temas de a uno (ver más abajo). Se puede saltar con skip=1.
reviewTabla con cada tema y su estado (video, enlace, pendiente, lectura, preguntas). Si el archivo trae varios módulos, se elige uno. Muestra cómo se reparten las preguntas del banco.
configFormulario import_config: número, título, categoría, objetivo, aplicación, cargos obligatorios, complementarios y responsables de contenido, tipo de asignación, responsable de revisión, estructura (repasos, candado, certificado) y reglas propias. Crea el módulo con academy::create_module(). Si el número ya existe, propone academy::next_module_number().
done"Revisar y publicar": estado del módulo, cargar preguntas de todos los repasos con un archivo (import_questions → academy::load_questions), responsable y fecha de la próxima revisión (academy::update_review), asignar cargos (import_cargos → academy::assign_cargos), publicar o eliminar el borrador.
publishPOST con sesskey: course_change_visibility($courseid, true). Exige moodle/course:visibility.
cancelBorra el banco de preguntas temporal y el estado de sesión y vuelve a Módulos.

En modo "en blanco" el módulo, al crearse, se abre directamente en modo de edición (editmode.php). Un borrador solo se puede eliminar si no es un módulo del manual (access::is_manual) ni PTS ni APOYO-IA, y con moodle/course:delete.

Modos de entrada

Formulario import_upload. Acepta .xlsx, .pdf, .docx, .csv y .json (máximo 20 MB) y, opcionalmente, un banco de preguntas (.docx, .txt, .xml, .gift). El archivo se lee con importer::parse($path), que ejecuta python3 /parse-curriculum.py <archivo>.

FormatoCómo lo lee parse-curriculum.py
.xlsxPlantilla oficial: hoja Temas (A1 = "MÓDULO N: Nombre", fila 2 de títulos) y hoja opcional Preguntas, que se cuelga de cada tema por su número. Otros Excel: cada hoja es un módulo (o una columna "Módulo"), con hipervínculos.
.pdfTabla "Nº, Tema (código + nombre), Objetivo, Ver video, casilla", con varios "MÓDULO N: …" en el mismo PDF; usa pypdf para texto e hipervínculos.
.docxPárrafos "MÓDULO N: …" seguidos de tablas de temas (con hipervínculos).
.csvCualquier cabecera reconocible (Tema o Nombre, Objetivo, Enlace o URL, Código, Módulo); separador ; o ,.
.jsonSe valida y se devuelve tal cual.

Word, Excel y CSV detectan las columnas de forma flexible (map_headers). Los códigos PowerBuilder se reconocen con una expresión regular del estilo G-3-OP-01-GE-01002.

Salida común de los lectores

Los dos scripts de Python imprimen una línea JSON por la salida estándar (el PHP toma la última línea JSON válida). importer::sanitize_modules() normaliza el resultado:

{"ok": true, "source": "archivo.xlsx", "modules": [
  {"n": 13, "raw_title": "MÓDULO 13: ...", "titulo": "...",
   "temas": [{"n": 1, "codigo": "G-3-OP-...", "tema": "...", "objetivo": "...",
              "url": "https://youtu.be/...", "texto": "", "preguntas": []}]}
]}

En caso de error: {"ok": false, "error": "..."}. Si python3 no está disponible, el asistente lo informa ("Python 3 no está disponible en el servidor"). Las dependencias pypdf, python-docx y openpyxl se instalan en el Dockerfile.

importer::clean_title() quita el prefijo "Módulo N:" y pasa a mayúscula inicial los títulos que vienen completos en MAYÚSCULAS.

Plantilla Excel

template.php (capacidad moodle/course:create) descarga Plantilla modulo Academia Deltamontero.xlsx, generada por local_deltamodules\template::spreadsheet() con PhpSpreadsheet:

HojaContenido
InstruccionesLogo (/opt/delta-reports/logo.png), pasos de uso y una fila de ejemplo.
TemasA1 = "MÓDULO 13: Nombre del módulo"; columnas Nº, Tema, Objetivo, Enlace del video, Código PowerBuilder; 30 filas numeradas.
PreguntasNº tema, Pregunta, Tipo (lista: Opción múltiple, Verdadero o falso), Opción A a Opción D, Respuesta correcta (lista: A, B, C, D, Verdadero, Falso), Explicación.

Las cabeceras son las que reconoce parse-curriculum.py; si se cambian, hay que cambiar ambos.

Bancos de preguntas

importer acepta preguntas en varios formatos y las convierte a lo que entiende el importador nativo de Moodle:

EntradaTratamiento
.xml (Moodle XML)Directo (qformat_xml).
.gift o texto con sintaxis GIFTDirecto (qformat_gift).
.docx o .txt escritos a manoimporter::parse_free() los interpreta y importer::to_gift() los convierte a GIFT.
AikenSe detecta por contenido cuando no es GIFT (qbank_format).
.xlsx (hoja Preguntas de la plantilla)Se leen con parse-curriculum.py y se crean con builder::add_questions() (solo en "Revisar y publicar" y en las pruebas técnicas).

Formato reconocido por parse_free():

Tema 2                         (o "13.2 Nombre del tema": las preguntas siguientes van a ese tema)
1. ¿Pregunta?                  (numerada o no; puede seguir en varias líneas)
a) opción                      (A. / a- / a) ; mayúscula o minúscula)
*b) opción correcta            (o "(correcta)" / "✓" al final de la opción)
Respuesta: b                   (o "ANSWER: B"; "Respuesta: Falso" = verdadero/falso)

importer::docx_lines() lee el Word directamente del ZIP y reconstruye las letras y números de las listas automáticas de Word (que no vienen en el texto). Una sublista numerada bajo una pregunta se interpreta como opciones.

importer::qbank_preview() lee el archivo sin guardar nada y cuenta preguntas por tipo y por categoría. importer::match_topic($categoria, $temas) asigna cada categoría a un tema por su número (13.2 …, 2. …, Tema 2) o por su nombre (con o sin "Repaso:"). Las preguntas que no corresponden a un tema quedan en el banco del módulo, en la categoría "Módulo N. Título".

IA opcional (ai_author)

En modo documento, si se marcó "Asistente de IA", el paso ai llama a ai_author::enrich_topic() tema por tema (los temas con al menos 200 caracteres de texto, ai_author::MIN_TEXT):

  • Usa la instancia de local_ai_manager del chat (conector chatgpt con endpoint de Groq, o la instancia llamada "Chat IA - Academia Deltamontero") y el modelo openai/gpt-oss-20b (ai_author::MODEL), temperatura 0.2 y respuesta en formato JSON.
  • Envía hasta 6.000 caracteres del tema y pide un objetivo breve y 3 preguntas de opción múltiple (4 opciones, una correcta) sacadas solo del texto.
  • Si recibe un error 429 espera lo que indica el proveedor (máximo 20 s) y reintenta una vez.
  • Valida la respuesta (validate): descarta preguntas incompletas, con opciones repetidas o con menos de 3 o más de 5 opciones. Solo completa el objetivo si el tema no tenía y las preguntas si el tema no traía.
  • Si la IA falla en un tema, ese tema queda como estaba y el módulo se crea igual.

Otros caminos para crear módulos

CaminoLógica
php /create-courses.php (CLI)builder::create_module() sobre /opt/academia-dm/academia_dm_contenido.json, con la tabla de equivalencias de nombres cortos de /manual-academia.json. Idempotente, sin certificado.
Función externa local_deltamodules_create_modulebuilder::create_module() + builder::add_certificate(). Ver Servicios web.
php /import-checkpoints.phpConvierte las preguntas de los videos (/checkpoints/c2_*.json) en cuestionarios después de cada video interactivo. Ver Scripts de configuración.

"Subir cursos" de Moodle (/admin/tool/uploadcourse/) redirige a Módulos → Nuevo módulo para quien no es super administrador; para el super administrador muestra un aviso que recomienda el asistente.

En esta página