Importar preguntas a un cuestionario
Un profesor escribe unas cuantas preguntas en GIFT, las importa a una categoría del banco de preguntas compartido del curso y las añade a un cuestionario junto con una pregunta aleatoria sacada de una categoría de reserva.
Usa: Bancos de preguntas, Cuestionarios.
El programa
import { createMoodle, FormError, NotLoggedInError, QuestionBankError, QuizError } from "@falkenslab/moodle-driver";
// El id de actividad del banco de preguntas compartido, de moodle.questionBanks(3) (o mod/qbank/view.php?id=52).
const BANK_ID = 52;
// El id de actividad del cuestionario (id del módulo del curso), de su dirección: mod/quiz/view.php?id=41.
const QUIZ_ID = 41;
// Categorías de ese banco, por nombre: una para estas preguntas y otra que ya contiene una reserva para preguntas aleatorias.
const TARGET_CATEGORY = "Unidad 4: Recursividad";
const POOL_CATEGORY = "Unidad 4: Reserva de práctica";
// GIFT: ::nombre:: texto {respuestas}. Las líneas en blanco separan las preguntas.
const gift = `
::Caso base:: Toda función recursiva necesita un caso base para detenerse. {T}
::Factorial de cero:: ¿Qué devuelve factorial(0)? {=1 ~0 ~Nunca devuelve nada}
::Llamadas:: ¿Cuántas llamadas hace factorial(3), contando la primera? {#4}
::Nombre:: ¿Cómo se llama una función que se llama a sí misma? {=recursiva =una función recursiva}
`;
await using moodle = createMoodle({ site: "https://campus.example.org/", profileDir: "/home/teacher/.moodle-profile", headless: true, downloadBrowser: true });
try {
await moodle.logIn();
// Las categorías del banco por id; la primera es "Superior", que no contiene preguntas.
const { categories } = await moodle.questions(BANK_ID);
const target = categories.find((c) => c.name === TARGET_CATEGORY);
const pool = categories.find((c) => c.name === POOL_CATEGORY);
if (!target || !pool) throw new Error(`El banco ${BANK_ID} no tiene la categoría "${TARGET_CATEGORY}" o "${POOL_CATEGORY}": créalas antes en Moodle.`);
// Con categoryId, todo va a parar a esa categoría, diga lo que diga el archivo.
const imported = await moodle.importQuestions(BANK_ID, { format: "gift", text: gift, categoryId: target.id });
console.log(imported.changed);
// Las preguntas importadas por id, y una pregunta aleatoria de la reserva, al final del cuestionario.
const result = await moodle.addQuestionsToQuiz(QUIZ_ID, {
questionIds: imported.questions.map((q) => q.id),
random: { categoryId: pool.id, count: 1 },
});
console.log(result.changed);
// El cuestionario tal como queda, posición a posición; una posición aleatoria no tiene questionId.
for (const slot of result.slots) console.log(` página ${slot.page} #${slot.number ?? "-"} [${slot.type}] ${slot.name} ${slot.mark ?? ""}`);
} catch (error) {
if (error instanceof QuestionBankError || error instanceof QuizError) console.error(`${error.code}: ${error.message}`);
else if (error instanceof FormError) for (const { field, message } of error.fields) console.error(`${field}: ${message}`);
else if (error instanceof NotLoggedInError) console.error("No hay sesión: vuelve a ejecutarlo para iniciar sesión.");
else throw error;
}
Paso a paso
BANK_IDes el id de actividad del banco compartido:questionBanks(courseId)enumera los bancos del curso, conshared: truepara uno compartido (type: "qbank").QUIZ_IDes el id de actividad del cuestionario, elidde su dirección. Cada cuestionario tiene además un banco propio, así queBANK_IDpodría serQUIZ_IDpara guardar las preguntas solo en el cuestionario.logIn()abre una ventana la primera vez para que el profesor inicie sesión; las siguientes ejecuciones reutilizan la sesión.questionslee las categorías del banco (y sus preguntas, que aquí no se usan). El programa busca las dos categorías por nombre y se detiene si falta alguna, antes de importar nada.importQuestionssube el texto GIFT como archivo a través del formulario de importación del banco, como lo haría el profesor. ConcategoryId, las preguntas van a esa categoría; el resultado enumera las preguntas creadas, con sus ids y tipos (truefalse,multichoice,numerical,shortanswer).addQuestionsToQuizañade esas preguntas por id y una pregunta aleatoria de la categoría de reserva, al final del cuestionario, como hace la página de edición del cuestionario. Responde con las posiciones del cuestionario tal como quedan.- El programa imprime cada posición: su página, su número en el cuestionario, su tipo (
"random"para la aleatoria) y su puntuación máxima.
Lo que imprime
Una ejecución de ejemplo:
Imported 4 questions into bank 52.
Added 5 questions to quiz 41.
página 1 #1 [truefalse] Caso base 1.00
página 1 #2 [multichoice] Factorial de cero 1.00
página 1 #3 [numerical] Llamadas 1.00
página 1 #4 [shortanswer] Nombre 1.00
página 1 #5 [random] Aleatoria (Unidad 4: Reserva de práctica) 1.00
Variaciones
- Importar desde un archivo en vez de un texto:
{ format: "gift", file: "/home/teacher/unidad-4/preguntas.gift", categoryId: target.id }, oformat: "xml"para Moodle XML. - Varias preguntas aleatorias, con las subcategorías de la reserva:
random: { categoryId: pool.id, count: 3, includeSubcategories: true }. - Añadirlas a una página existente en vez de al final:
page: 2, un número de página tal como los daquizStructure(QUIZ_ID)en elpagede cada posición. - Una pregunta con su propia retroalimentación por respuesta, sin GIFT:
createQuestion(BANK_ID, { type: "multichoice", name, text, answers, categoryId: target.id }), y después añade suquestionId.
Cuando algo sale mal
QuestionBankError(códigoquestion-bank):BANK_IDno es un módulo que guarde preguntas (el driver lo comprueba antes de abrir su banco, así que no queda nada a medias), el profesor no puede gestionar sus preguntas, la categoría no está en el banco, o Moodle no pudo analizar el GIFT ("Moodle didn't import the questions: ..."), y en ese caso no se importó nada.QuizError(códigoquiz): el cuestionario ya tiene intentos, así que Moodle no deja cambiar sus preguntas (crea un cuestionario nuevo, o borra los intentos en Moodle); o no se añadió ninguna pregunta (ids o categoría equivocados). Las preguntas importadas antes se quedan en el banco: vuelve a ejecutar solo la segunda mitad.FormError: Moodle rechazó el formulario de importación; susfieldscontienen los mensajes de Moodle.NotLoggedInError: la sesión caducó; vuelve a ejecutarlo e inicia sesión.
Datos que toca
questions, importQuestions y addQuestionsToQuiz son course-content: preguntas y cuestionarios que escriben los profesores, ni intentos ni respuestas de nadie. Consulta categorías de datos.