Módulos y estándar
Estructura oficial de los módulos de la Academia y cómo se crea y se mantiene con academy, builder, standard y health.
Un módulo de la Academia es un curso de Moodle dentro de la categoría Academia Deltamontero (o de una subcategoría suya). Cuatro clases se reparten el trabajo:
| Clase | Archivo | Papel |
|---|---|---|
academy | classes/academy.php | Estructura oficial de un módulo nuevo y operaciones de alto nivel (certificado, completado, revisión anual, bancos de preguntas). |
builder | classes/builder.php | Fábrica de bajo nivel: crea cursos, secciones, actividades, preguntas y el certificado. |
standard | classes/standard.php | Estándar que deben cumplir todos los módulos (los existentes y los nuevos), aplicado en cada despliegue y al instante al crear o editar una actividad. |
health | classes/health.php | Revisión de los módulos: encuentra lo que impide avanzar y lo repara cuando se puede. |
¿Qué es un módulo de la Academia?
standard::is_academy(int $courseid) decide si un curso sigue el estándar: es true cuando la categoría del curso es la categoría raíz cuyo nombre es academy::CATEGORY ('Academia Deltamontero') o una subcategoría suya (se compara el path de la categoría). El sitio (SITEID) nunca lo es. El resultado se guarda en una caché estática por petición.
Casi todo lo automático (reglas, observadores, avisos de borrador, pantalla corta de nuevo repaso) solo actúa sobre cursos para los que is_academy() devuelve true. El curso de la Prueba Técnica de Selección (seleccion_pruebas_tecnicas, número de ID PTS) y el curso de Introducción se tratan aparte.
Estructura oficial (academy)
academy::create_module(array $def) crea un módulo con exactamente la estructura de los módulos 1 a 12. La usa el asistente "Crear módulo desde archivo" (Importador).
| Elemento | Cómo queda |
|---|---|
| Nombre completo | Módulo N. Título |
| Nombre corto | m{N}_{titulo} en minúsculas, sin tildes, máximo 40 caracteres de título (importer::suggest_shortname) |
| Número de ID | MNN con dos dígitos (academy::idnumber, p. ej. M13) |
| Categoría | La indicada o, por defecto, la raíz "Academia Deltamontero" (builder::ensure_category) |
| Formato | topics, una sección por tema, idioma es, completado activado, showgrades = 0 |
| Visibilidad | Oculto (borrador) hasta publicarlo |
| Descripción | Bloque con la marca academy::MARK (data-delta-manual="1") con Objetivo y Aplicación principal |
| Secciones | N.1 Tema, N.2 Tema… con el objetivo como resumen |
| Contenido de cada tema | Video interactivo si la URL es de YouTube; enlace si no lo es; una página "Lectura: …" si el tema trae texto; el código PowerBuilder va en el idnumber del contenido principal |
| Repaso | Repaso: Tema por tema con video (si se marcó "repasos"), o si el tema trae preguntas, o si el banco de preguntas tiene una categoría para ese tema |
| Candado | Con "candado", cada repaso queda bloqueado hasta completar el contenido principal de su tema |
| Certificado | Sección final "Certificado" con mod_customcert y la plantilla de la regla "Diseño del certificado" |
| Completado del curso | Todas las actividades con seguimiento, menos el certificado (apply_course_completion) |
| Campos personalizados del curso | ultima_revision, proxima_revision (+12 meses), responsable_revision, asignacion_por_cargo (HTML con "Obligatorio para" y "Complementario / según asignación para"), tipo_asignacion (1 Obligatorio, 2 Complementario, 3 Por desarrollo, 4 Por cliente) |
| Matrículas | Por cohorte de cargo para los cargos obligatorios (rol student) y para los responsables de contenido (rol delta_resp_contenidos) |
Definición que recibe create_module
$def = [
'n' => 13, // número del módulo
'titulo' => 'Nombre del módulo',
'catid' => 0, // 0 = categoría Academia Deltamontero
'objetivo' => '...', 'aplicacion' => '...',
'responsable' => 'Talento Humano',
'tipo' => 1, // 0..4 (ver academy::TIPOS)
'obligatorios' => [/* cohortid */], 'complementarios' => [], 'editores' => [],
'repasos' => true, 'candado' => true, 'certificado' => true,
'rules' => ['attempts' => 3], // reglas propias del módulo (opcional)
'qbank' => ['path' => '...', 'name' => '...', 'format' => 'gift'], // opcional
'temas' => [
['tema' => '...', 'objetivo' => '...', 'codigo' => 'G-3-OP-...', 'url' => 'https://youtu.be/...',
'texto' => '...', 'preguntas' => [/* formato de builder::add_questions */]],
],
];Las reglas propias (rules) se guardan antes de crear el contenido, para que cada video y repaso nazca con ellas. Devuelve courseid, quizcms (cmids de los repasos creados), contadores (videos, links, pending, questions, readings), bank (resultado del banco de preguntas), cert y criteria.
Otras operaciones de academy
| Método | Qué hace |
|---|---|
exam_settings() | Configuración común de todos los cuestionarios: intentos y tiempo de la regla general, sin ventana segura, una pregunta por página, respuestas mezcladas, navegación secuencial, calificación por la nota más alta, revisión del intento sin mostrar la respuesta correcta ni la retroalimentación. |
exam_notice($candidate, $attempts, $gradepass, $grade, $courseid) | Recuadro "Antes de empezar, lee esto" con los intentos, la nota para aprobar, la regla de volver a ver el video y el control de cambio de pantalla. Con $candidate = true es el aviso de la prueba técnica (aclara que aprobar no garantiza la contratación). |
custom_notice, custom_notice_html, strip_custom_notice, with_notice, strip_notice | Manejo del aviso automático y del escrito a mano (marca data-delta-exam-notice="custom"). Ver Repasos y editor. |
next_module_number() | Siguiente número libre a partir de los idnumber M\d\d. |
cargo_cohorts() | Cohortes con idnumber que empieza por cargo_, sin el prefijo "Cargo: " del nombre. |
review_owners() | Sugerencias de responsables de revisión (cuatro fijas más los valores usados). |
import_bank(...) | Importa un banco Moodle XML, GIFT o Aiken al banco del curso con el importador nativo; reparte cada pregunta al repaso de su tema según el nombre de su categoría (importer::match_topic). |
course_topics($courseid) | Temas de un módulo ya creado (sin la sección 0 ni "Certificado") y el repaso de cada uno. |
load_questions($courseid, $path, $name) | Carga preguntas desde un solo archivo en un módulo existente; crea los repasos que falten. |
import_into_quiz($cmid, $path, $name) | Carga todas las preguntas de un archivo en un cuestionario (pruebas técnicas). |
assign_cargos($courseid, $cohortids) | Matricula cargos obligatorios y reescribe "Obligatorio para" del campo asignacion_por_cargo. |
review_courses() | Cursos con revisión anual: idnumber M\d\d+ y PTS. |
gate_certificate($courseid) | Restricción del certificado. Ver Certificados. |
apply_course_completion($courseid) | Criterio de completado del curso: todas las actividades visibles con seguimiento, sin el certificado y sin los temas que son solo para un grupo. |
update_review($courseid, $responsable, $next) | Responsable y próxima revisión desde "Revisar y publicar". |
Fábrica de bajo nivel (builder)
| Método | Qué hace |
|---|---|
create_module(array $modulo, ?int $catid) | Formato heredado (JSON academia_dm_contenido.json y función MCP): id (shortname) y titulo, con temas o temas_agrupados. Idempotente por shortname: si existe, devuelve su id. Crea el curso visible. |
ensure_category($name) | Devuelve o crea una categoría raíz por nombre. |
is_youtube_url, normalize_youtube_url | Detecta y normaliza enlaces de YouTube a https://www.youtube.com/watch?v=ID. |
add_item(...) | Crea un contenido de un tema según su tipo: video, enlace, texto, aviso, imagen, archivo, pagina, prueba. Devuelve si usó el código PowerBuilder del tema. |
create_quiz($courseid, $sectionnum, $name, $intro) | Cuestionario con academy::exam_settings(), nota máxima 10, completado al recibir calificación y el aviso inicial. Registra el cmid en builder::$createdquizzes. |
add_questions($cmid, $qs, $title) | Convierte preguntas a GIFT y las importa con qformat_gift al banco del cuestionario; recalcula la nota. |
add_certificate($courseid, $templateid) | Sección nueva "Certificado" con mod_customcert ("Certificado del curso", entrega I en línea). Idempotente: si el curso ya tiene certificado, devuelve su cmid. Necesita la plantilla del sitio. |
load_cert_template($cmid, $templateid) | Copia páginas y elementos de una plantilla del sitio al certificado del módulo (reemplaza los anteriores). |
Formato de las preguntas que reciben add_questions y academy::create_module:
$qs = [
['q' => 'Enunciado', 't' => 'mc', 'opts' => ['A', 'B', 'C'], 'ok' => 1, 'fb' => 'Retroalimentación'],
['q' => 'Enunciado', 't' => 'vf', 'ok' => 0], // vf: ok 0 = Verdadero, 1 = Falso
];Los videos se crean con type = 'yt', source = 'url', completionpercentage = 0 y extendedcompletion = {"watchtillend":1} (completado nativo del plugin al llegar al final). Los enlaces se abren en una pestaña nueva (display = 3) y se marcan como hechos al abrirlos.
Estándar de la Academia (standard)
standard hace que todos los módulos se comporten igual según sus Reglas. Se aplica:
- en cada despliegue, desde
infrastructure/moodle/normalize-academy.php(víahealth::repairystandard::sweep_pending); - al instante, con el observador
module_changedsobrecourse_module_createdycourse_module_updated; - cuando Talento Humano cambia una regla (
apply_changes).
Videos: apply_video($cm)
- Con la regla
video_player = 1,displayoptionsqueda en{"distractionfreemode":1}y conserva solousecustomposterimageyposterimagesize(portada propia de los videos alojados en R2). Undisplayoptionsvacío no se toca. - Con
video_completion = end:extendedcompletion.watchtillend = 1,completionpercentage = 0, completado automático sincompletionview. Conview: se quitawatchtillendy se marca al abrirlo.
Repasos: apply_quiz($cm, $isnew, $force)
- Anota el cuestionario en la configuración
examsecured(lista JSON de ids) para queconfigure-exam-security.phpno le vuelva a imponer las reglas de examen. - Intentos y tiempo: los del cuestionario. Solo si la regla
attemptsotimelimitestá en$force(Talento Humano la acaba de cambiar) se impone el valor de la regla. - Nota para aprobar: si el cuestionario no tiene (
gradepass <= 0) o sipasspercentestá en$force, se fijagrade * passpercent / 100en el ítem de calificación y se recalcula el completado de quien ya rindió. - Aviso inicial: con
exam_notice = 1, se reescribe con los valores reales; un aviso escrito a mano se respeta. - Completado: automático por calificación (
completiongradeitemnumber = 0), concompletionpassgrade = 1si la reglaquiz_completionespassy hay nota para aprobar. - Candado: con
quiz_lock = 1, la restricción de acceso exige completar todos los videos interactivos de la misma sección (se agregan los que falten); con0, se quitan esas condiciones. Solo se toca una restricción raíz con operador&.
Módulo completo: apply_course($courseid, $force)
Aplica apply_video y apply_quiz a cada actividad, luego apply_certificate (solo si cambió cert_enabled o cert_template), academy::gate_certificate y academy::apply_course_completion. Si cambiaron video_completion o quiz_completion, reinicia el estado de completado (reset_all_state) de las actividades afectadas para recalcular el avance de todos con la regla nueva. apply_changes($changes) recorre los módulos devueltos por rules::save() con un límite de tiempo de 600 s.
Al crear o editar una actividad: module_changed
- Video:
apply_video. - Cuestionario:
apply_quiz; si se creó desde/course/modedit.php, guarda$SESSION->deltanewquizpara que la siguiente página sea su editor (questions.php); además registra una función de cierre (core_shutdown_manager) que vuelve a aplicarapply_quizcuando Moodle termina de guardar la nota para aprobar. - Al crear cualquier actividad o al tocar un certificado: recalcula
gate_certificateyapply_course_completion.
Volver a ver el video
Regla: quien agota sus intentos sin aprobar vuelve a ver el video del tema y recibe intentos nuevos (retakeattempts, 2 por defecto; 0 lo desactiva).
Marcar como pendiente
El observador attempt_submitted (evento \mod_quiz\event\attempt_submitted) comprueba needs_retake(): tiene nota para aprobar, la regla da intentos nuevos, su mejor nota no alcanza y ya usó todos los intentos permitidos. Si es así, agrega el id del cuestionario a la preferencia local_deltamodules_retake (standard::RETAKE_PREF). No bloquea en ese momento para que pueda ver la revisión de su intento.
Procesar al volver
El hook standard::before_http_headers revisa los pendientes del curso actual. En la revisión del intento (mod-quiz-review) solo muestra un aviso; en el curso, en un video o en el repaso ejecuta process_retake().
Reiniciar el video y dar intentos
process_retake() borra el progreso del reproductor (interactivevideo_log, interactivevideo_completion), deja el video como no completado y crea o actualiza una excepción de usuario del cuestionario (mod_quiz\local\override_manager) con intentos = usados + retakeattempts. Desde la página del repaso redirige al video.
sweep_pending($courseid) marca como pendientes a quienes ya habían agotado sus intentos antes de existir la regla (lo ejecuta normalize-academy.php).
Revisión de módulos (health)
health::check_all() revisa todos los módulos de la Academia (menos PTS) y health::check($courseid) devuelve problemas con nivel error, warn o info, si los arregla el botón Reparar (fix) y un enlace a donde corregirlos (action). Lo usa modcheck.php (Módulos → Revisar los módulos).
| Revisión | Nivel | ¿Reparable? |
|---|---|---|
| Seguimiento de completado apagado en el curso | error | Sí |
| Video que no se marca como hecho según la regla | error | Sí |
| Video sin enlace | error | No (enlace al formulario) |
| Video de YouTube eliminado, privado o que no permite insertarse | error | No (enlace al formulario) |
| Repaso sin preguntas | error | No (enlace al editor) |
| Repaso sin nota para aprobar (con la regla "al aprobarlo") | error | Sí |
| Repaso sin completado automático | error | Sí |
| Restricción que pide una actividad que ya no existe | error | Sí (se quita la condición) |
| Restricción que pide una actividad oculta | error | No |
| Módulo sin certificado con la regla "Entrega certificado" activa | warn | No |
| Módulo con actividades pero sin criterio de completado | warn | Sí |
| Tema visible vacío | info | No |
| Personas que vieron un video completo y quedaron sin "Hecho" | error | Sí (recalcula) |
| Personas que ven videos sin tener el módulo asignado | info | No |
La comprobación de YouTube (youtube_status) consulta el servicio oEmbed de YouTube en tandas de 10 con curl_multi, forzando IPv4 y respetando $CFG->proxyhost. Interpreta 200 = reproducible, 401/403 = privado o sin permiso de inserción, 404 = eliminado. Las respuestas se recuerdan 6 horas (health::YT_TTL) en la configuración ytcheck; una consulta sin respuesta no se guarda.
health::repair($courseid) activa el completado si estaba apagado, quita de las restricciones las condiciones sobre actividades inexistentes, aplica standard::apply_course() y recalcula el completado de las vistas completas que quedaron sin "Hecho". Devuelve cuántos avances recalculó. No cambia intentos, tiempo ni nota configurados en cada repaso.
local_deltamodules
Visión general del plugin principal de la Academia Deltamontero, sus responsabilidades y la organización de su código.
Reglas de la Academia
Catálogo de reglas con dos niveles (general y por módulo), la tabla local_deltamodules_rule y cómo se aplican con apply_course y apply_changes.