Consumo del contexto de actividades — get_activity_context
En el ambiente de Pruebas el plugin local_pccntr8203403_iacontext está instalado y la función
forma parte del servicio "Componente IA Externo", así que es alcanzable por REST con el token del
servicio. El endpoint es aditivo: no altera ninguna de las funciones que el agente ya consume.
Pendiente para darlo por operativo: la cuenta de servicio no administradora descrita en puesta en servicio. Mientras el token sea de un administrador, las comprobaciones de permisos no se ejercitan.
Contrato de integración para el desarrollador del servicio de IA externo. La función
local_pccntr8203403_iacontext_get_activity_context devuelve, en una sola llamada, una fila
plana por actividad con su nombre, su sección, su tipo, su enlace, su estado de completitud, su nota
y su fecha límite, ya unidos por cmid.
Hoy esa unión no existe en ninguna función core: hay que cruzar core_course_get_contents (nombre y
sección), core_completion_get_activities_completion_status (estado) y
gradereport_user_get_grade_items (nota) por cmid en tiempo de inferencia. Ninguna de las tres trae
por sí sola las cuatro cosas, y la de completitud ni siquiera devuelve el nombre de la actividad.
Qué aporta frente al cruce de funciones core
Medido sobre un curso de 50 módulos, con el mismo estudiante y curso por ambas vías:
| Cruce de 3 funciones core | get_activity_context | |
|---|---|---|
| Caracteres de la respuesta | 73 522 | 8 585 |
| Rutas de campo distintas | 134 | 27 |
| Caracteres por actividad | 1 470 | 277 |
| Llamadas | 3, más la unión por cmid | 1 |
| Estructura | 3 árboles anidados distintos | 1 array plano |
| Campos con datos personales | nombre completo y cédula del estudiante | ninguno con la configuración por defecto |
Además excluye la decoración del curso (etiquetas y demás módulos sin página de vista), que en ese curso son 17 de 50 filas sin nota y con un nombre generado a partir de su propio HTML. Una etiqueta puede, sin embargo, tener seguimiento de completitud: ver el ajuste "Excluir módulos sin página de vista".
Requisitos de acceso
| Requisito | Detalle |
|---|---|
| Token | Del servicio "Componente IA Externo" — ver Web Services IA |
| Capacidad | local/pccntr8203403_iacontext:viewcontext en el contexto de cada curso |
| Capacidad | moodle/course:view, porque la cuenta de servicio no está matriculada en ningún curso |
| Matrícula del estudiante | El userid consultado sí debe tener matrícula activa en el courseid |
La capacidad viewcontext se define sin arquetipos: no la hereda ningún rol estándar y hay que
asignarla explícitamente al rol de la cuenta de servicio. Es de tipo lectura y riesgo
RISK_PERSONAL.
Cómo pedir los datos
curl -X POST "https://<host-del-campus>/webservice/rest/server.php" \
-d "wstoken=<TOKEN>" \
-d "wsfunction=local_pccntr8203403_iacontext_get_activity_context" \
-d "moodlewsrestformat=json" \
-d "userid=<ID_ESTUDIANTE>" \
-d "courseid=<ID_CURSO>"
| Parámetro | Valor |
|---|---|
userid | Obligatorio. Id Moodle del estudiante — el mismo moodle_user_id que llega en el payload del tutor (/webhook/v1/campus/tutor) |
courseid | Opcional. Id del curso. Con 0 (u omitido) responde todos los cursos con matrícula activa del estudiante |
Ambos son ids internos numéricos de Moodle (mdl_user.id y mdl_course.id). La función no
acepta ni devuelve idnumber, username, cédula ni correo, y no resuelve identificadores: si el
consumidor tuviera otro identificador, la resolución se hace aparte con
core_user_get_users_by_field.
Desde PowerShell
Hay que invocar curl.exe con la extensión: curl a secas es un alias de Invoke-WebRequest y no
entiende -d ni -s. El acento grave es el carácter de continuación de línea.
curl.exe -s "https://<host-del-campus>/webservice/rest/server.php" `
-d "wstoken=<TOKEN>" `
-d "wsfunction=local_pccntr8203403_iacontext_get_activity_context" `
-d "moodlewsrestformat=json" `
-d "userid=<ID_ESTUDIANTE>" `
-d "courseid=<ID_CURSO>"
O con el cliente nativo, que devuelve el objeto ya deserializado:
$body = @{
wstoken = '<TOKEN>'
wsfunction = 'local_pccntr8203403_iacontext_get_activity_context'
moodlewsrestformat = 'json'
userid = <ID_ESTUDIANTE>
courseid = <ID_CURSO>
}
$r = Invoke-RestMethod -Uri 'https://<host-del-campus>/webservice/rest/server.php' -Method Post -Body $body
$r.courses[0].activities | Format-Table cmid, name, section, state, grade, grademax, duedate -AutoSize
Con el certificado mkcert de https://localhost:8443, curl.exe falla con exit 35 porque Schannel
intenta comprobar la revocación del certificado y no encuentra dónde consultarla. Añadir
--ssl-no-revoke, que mantiene la validación y sólo salta esa comprobación. Invoke-RestMethod no
lo necesita.
Respuesta (abreviada)
{
"schemaversion": 2,
"userid": 918,
"student": {
"lastaccess": 1786463897 // el nombre solo aparece si administración lo activa
},
"courses": [
{
"courseid": 5,
"coursename": "Puesta en marcha de la biblioteca escolar",
"truncated": false, // true si se aplicó la cota de actividades por curso
"completion": { // ausente si el curso no tiene seguimiento de completitud
"completed": false,
"criteriacompleted": 0,
"criteriatotal": 0,
"timecompleted": null
},
"activities": [
{
"cmid": 122,
"name": "Tema 1. Actividad complementaria 1",
"section": "Modulo 1",
"sectionid": 21,
"sectionnum": 1,
"instance": 26,
"type": "assign",
"url": "https://<host-del-campus>/mod/assign/view.php?id=122",
"visible": true,
"state": 0,
"grade": null,
"grademax": 100,
"duedate": 0
}
]
}
]
}
schemaversion vale 2 desde que la respuesta incorpora las claves de sección, instance, el
bloque completion por curso, el bloque student y warnings.
Diccionario rápido
| Campo | Tipo | Significado |
|---|---|---|
cmid | int | Id del módulo de curso. Siempre presente: es la llave para volver a Moodle por más detalle |
name | string | Nombre de la actividad tal como lo ve el estudiante |
section | string | Nombre de la sección que contiene la actividad. Nunca el nombre del curso |
sectionid | int | Id de esa sección. Estable aunque se reordene el curso, a diferencia de sectionnum |
sectionnum | int | Posición de la sección dentro del curso |
sectionvisible | bool | Si el estudiante puede ver la sección. Desactivado por defecto: con la configuración por defecto llega true en todas las filas |
instance | int | Id de la actividad en la tabla de su propio módulo (mdl_assign.id y equivalentes). Es lo que las funciones mod_* piden como instance. No es intercambiable con cmid |
type | string | Tipo genérico: assign, quiz, scorm, forum, h5pactivity... |
url | string | null | Enlace accionable a la actividad |
visible | bool | Si el estudiante puede verla |
state | int | null | 0 incompleta, 1 completa, 2 completa con aprobado, 3 completa con suspenso. null = la actividad no tiene seguimiento de completitud, que no es lo mismo que 0 |
grade | float | null | Nota final. null = sin calificar aún, u oculta para quien consulta |
grademax | float | null | Nota máxima, para contextualizar ("8 sobre 10"). null = la actividad no es calificable |
duedate | int | null | Fecha límite en epoch, con la extensión individual del estudiante ya aplicada. 0 = sin fecha límite configurada; null = ese tipo de actividad no tiene concepto de fecha límite |
Las distinciones entre null y 0 de state y duedate son deliberadas y el consumidor debe
respetarlas: colapsarlas hace que el tutor reporte como pendiente lo que nunca se rastreó, o que
invente fechas límite donde no las hay.
Los campos del sobre (schemaversion, userid, courseid, coursename, truncated) siempre
viajan. Los catorce campos de actividad son configurables desde administración, salvo cmid.
Bloque student
Datos del estudiante sobre el que se informa. Se omite entero si administración no activa ninguno de sus campos.
| Campo | Tipo | Significado |
|---|---|---|
firstname | string | Nombre de pila. Dato personal: desactivado por defecto |
fullname | string | Nombre completo. Dato personal: desactivado por defecto |
lastaccess | int | Último acceso al sitio en epoch, no a un curso concreto. 0 = nunca ha entrado |
Bloque completion (por curso)
Completitud a nivel de curso, el agregado que core_completion_get_course_completion_status
entrega en una llamada aparte.
| Campo | Tipo | Significado |
|---|---|---|
completed | bool | El estudiante completó el curso |
criteriacompleted | int | Criterios de completitud que ha cumplido |
criteriatotal | int | Criterios que define el curso. 0 significa que hay seguimiento pero nadie configuró criterios |
timecompleted | int | null | Cuándo completó el curso, en epoch. null = no lo ha completado |
El bloque se omite cuando el curso no tiene seguimiento de completitud, o cuando el estudiante
no está incluido en los informes de completitud. Ausencia del bloque y criteriatotal: 0 son cosas
distintas: la primera es "la pregunta no aplica", la segunda es "aplica y vale cero".
Configuración desde administración
En Administración del sitio > Plugins > Plugins locales > Contexto IA:
| Ajuste | Por defecto | Efecto |
|---|---|---|
| Campos de actividad | 13 de 14 activos | Desmarcar un campo lo elimina de la respuesta. cmid se envía siempre. sectionvisible es el único que nace desmarcado |
| Campos del estudiante | solo lastaccess | firstname y fullname son datos personales y nacen desmarcados. Desmarcar los tres elimina el bloque student de la respuesta |
| Bloque de completitud del curso | activado | Añade completion a cada curso |
| Incluir actividades ocultas | desactivado | Con el valor por defecto, las actividades que el estudiante no ve no se devuelven |
| Excluir módulos sin página de vista | activado | Deja fuera la decoración del curso (etiquetas), que no es una actividad que el estudiante pueda abrir. Excluye también las etiquetas que tienen seguimiento de completitud, que sí cuentan para el avance del curso: con el ajuste activado, el número de actividades con state no nulo puede ser menor que el que reporta core_completion_get_activities_completion_status para el mismo estudiante |
| Máximo de actividades por curso | 500 | Cota dura. Al aplicarse, ese curso se marca con truncated: true |
| Vigencia de la caché | 60 s | Antigüedad máxima de las filas construidas, por estudiante y curso. 0 desactiva la caché |
Ajustar el conjunto de campos es un cambio de configuración: no requiere desplegar el plugin ni una ventana de mantenimiento. La capacidad y la matrícula se comprueban siempre, nunca se sirven desde la caché.
Notas y errores comunes
El consumidor debe ramificar sobre errorcode, nunca sobre message: el identificador es
estable entre idiomas, el texto no.
errorcode | Causa | Qué hacer |
|---|---|---|
usernotenrolled | El par (userid, courseid) no corresponde a una matrícula activa: sin matrícula, suspendida, caducada, o con la instancia de matriculación deshabilitada | Invalidar el par guardado y volver a resolverlo. No reintentar igual |
invalidcourseid | El courseid no existe | Corregir el id del curso |
invalidparameter | Falta un parámetro, es de otro tipo, o se pidió la portada del sitio | Corregir la petición |
nopermissions | Al rol de la cuenta de servicio le falta viewcontext en ese curso | Revisar la asignación del rol. Es un problema de configuración, no del estudiante |
userdeleted, suspended, guestsarenotallowed, invaliduser | El userid no es una cuenta activa | Refrescar el id del estudiante |
La autorización del llamante se resuelve antes que cualquier dato del estudiante: quien no puede
reportar sobre un curso recibe nopermissions, y no puede deducir por la diferencia entre errores si
un estudiante está o no matriculado en él.
warnings: cuándo el informe está incompleto
Solo el modo descubrimiento produce warnings. Con un courseid explícito las mismas
condiciones lanzan excepción, y eso no cambia.
warningcode | Significado |
|---|---|
nocapability | Se omitieron cursos porque a la cuenta de servicio le falta viewcontext en ellos |
contextnotvalid | Se omitieron cursos porque la cuenta de servicio no puede acceder a su contexto |
El aviso dice cuántos cursos se omitieron, nunca cuáles. Los ids se retienen a propósito: en modo descubrimiento la lista de cursos sale de las matrículas activas del estudiante, así que nombrar un curso omitido le revelaría a un llamante sin permiso sobre ese curso que el estudiante está matriculado en él. Esa inferencia es justo la que el orden de comprobaciones del endpoint existe para impedir.
Lo que el consumidor necesita saber es que el informe es parcial, para no razonar sobre un conjunto
incompleto; eso es lo que el conteo le da. warnings no sustituye a errorcode: un aviso
señala un problema de configuración de la cuenta de servicio, no algo que le ocurra al estudiante.
Los dos modos de uso
| Modo | Cuándo | Comportamiento |
|---|---|---|
Aserción (courseid explícito) | Turno conversacional, donde el par acaba de ser validado por el propio campus | El consumidor afirma que el par es válido. Si no lo es, recibe usernotenrolled |
Descubrimiento (courseid = 0) | Consulta periódica | El consumidor guarda sólo el userid y deja que Moodle recalcule los cursos en cada llamada. Es auto-sanante: una baja o una suspensión hacen desaparecer el curso de la respuesta, sin error |
Para el sondeo periódico se recomienda el modo descubrimiento: elimina de raíz el problema del par guardado que caduca entre turnos.
Distinguir "no hay nada" de "el par caducó"
| Señal | Significado |
|---|---|
200 con activities: [] | Curso real, matrícula viva, sin actividades visibles |
200 con courses: [] (modo descubrimiento) | El estudiante no tiene ninguna matrícula activa |
usernotenrolled | El par guardado caducó |
nopermissions | Problema de la cuenta de servicio, no del estudiante |
Puesta en servicio
Tres pasos de administración. En Pruebas el primero está hecho; los otros dos siguen pendientes.
- Añadir
local_pccntr8203403_iacontext_get_activity_contextal servicio "Componente IA Externo", enAdministración del sitio > Servidor > Servicios web > Servicios externos. - Asignar
local/pccntr8203403_iacontext:viewcontextymoodle/course:viewal rol de la cuenta de servicio, en contexto de sistema. - Emitir el token con una cuenta no administradora. Una cuenta con privilegios de administrador salta toda verificación de capacidades, de modo que el endpoint respondería aunque los permisos estuvieran mal configurados, y la prueba no demostraría nada.