Este proyecto proporciona un sistema automatizado para generar cuestionarios de programación en C destinados a Moodle. El generador permite crear preguntas de opción múltiple con variantes aleatorias a partir de plantillas de código C, facilitando la evaluación de estudiantes con preguntas únicas que evitan el plagio y la memorización.
generador.py es el script principal que procesa plantillas de código C (.c)
ubicadas en el directorio templates/, genera múltiples variantes de cada
pregunta con valores aleatorios, compila y ejecuta el código para obtener las
respuestas correctas automáticamente, genera distractores inteligentes, y
finalmente produce un archivo XML compatible con Moodle listo para importar.
- Generación automática de variantes: Crea múltiples versiones de cada pregunta con valores aleatorios.
- Ejecución automática: Compila y ejecuta el código C para determinar la respuesta correcta.
- Distractores inteligentes: Genera respuestas incorrectas plausibles basadas en errores comunes.
- Organización por categorías: Utiliza la estructura de directorios para categorizar preguntas en Moodle.
- Modo de verificación: Permite generar solo el código C para revisión y depuración antes de crear el XML.
- Intencionalmente ilegible por el compilador: Se sustituyen diversos caracteres como operadores y símbolos equivalentes visualmente de Unicode para hacerlos resistentes a copypasteo, pero también para sumar legibilidad a caracteres invisibles.
El script se ejecuta desde la línea de comandos:
./generador.py [opciones]-so--source: Especifica el directorio donde se encuentran las plantillas (.c). Por defecto estemplates.-oo--output: Especifica el nombre del archivo XML de salida. Por defecto escuestionario_moodle.xml.-no--num: El número de preguntas (variantes) a generar por cada plantilla. Por defecto es5.-co--category: El nombre de la categoría raíz bajo la cual se organizarán las preguntas en Moodle. Por defecto esprogramacion1_gen_codigo.-to--template: Procesar solo un archivo.cespecífico en lugar de un directorio completo. Acepta rutas absolutas o relativas. Útil para probar una plantilla individual antes de procesar todas.-go--generate-only: Solo genera código C en el directoriogeneratedsin crear el XML de Moodle. Útil para verificar el funcionamiento de las plantillas.
El argumento -t o --template permite procesar un solo archivo .c en lugar de un directorio completo. Esto es especialmente útil cuando:
- Estás desarrollando o probando una nueva plantilla
- Necesitas regenerar rápidamente preguntas de una plantilla específica
- Quieres verificar cambios en una plantilla sin procesar todas
Ejemplos de uso:
# Generar XML para una sola plantilla
python3 generador.py -t templates/punteros/basico.c -o test.xml -n 3
# Generar código C de una sola plantilla para verificación
python3 generador.py -g -t templates/punteros/basico.c -n 5
# Usar ruta relativa
python3 generador.py -t ./templates/arrays/ejercicio1.cNota: Cuando se usa -t, el argumento -s es ignorado, ya que se procesa el archivo especificado directamente.
El argumento -g o --generate-only permite generar solo el código C de las
plantillas sin crear el archivo XML de Moodle. Este modo es útil para:
- Verificar que las plantillas se procesan correctamente
- Compilar y probar manualmente las variantes generadas
- Depurar problemas en las plantillas antes de generar el XML
Ejemplo de uso:
./generador.py -g -n 3Este comando generará 3 variantes de cada plantilla en el directorio
generated/. La estructura de directorios interna será la misma que en
templates/, pero cada archivo .c generará un subdirectorio con sus
variantes.
Por ejemplo, si existe templates/punteros/basico.c, se generarán:
generated/punteros/basico/basico_v1.cgenerated/punteros/basico/basico_v2.cgenerated/punteros/basico/basico_v3.c
El script también creará un Makefile en el directorio generated/ para
facilitar la compilación:
cd generated
make # Compila todos los archivos
make clean # Elimina los ejecutablesEl script utiliza la estructura de directorios dentro de la carpeta source
para crear una jerarquía de categorías en Moodle.
Por ejemplo, una plantilla ubicada en
templates/punteros/avanzado/mi_plantilla.c se añadirá a la siguiente categoría
en Moodle:
$course$/top/programacion1_gen_codigo/punteros/avanzado
Esto permite organizar las preguntas de forma lógica y coherente con el temario del curso.
Cada archivo .c es una plantilla que combina código C válido con bloques de
metadatos especiales. Debe ser compilable y ejecutable por sí mismo para
facilitar las pruebas.
El enunciado de la pregunta se define mediante dos comentarios de una sola línea
(//).
- Comentario de Introducción: La primera línea de comentario
//que encuentra el script se usa como el texto que precede al bloque de código. - Comentario de Cierre: La última línea de comentario
//que encuentra se usa como el texto que va después del bloque de código.
// Analiza el siguiente código. ¿Cuál es el valor final de 'x'?
#include <stdio.h>
int main() {
int x = 10 + 5;
printf("%d", x);
return 0;
}
// ¿Qué valor se imprime en la consola?El cuerpo del archivo debe ser un programa en C válido. Para permitir que las variables sean dinámicas, se utiliza un sistema de macros que el script reemplaza.
- Variables Dinámicas: Se definen como
__nombre_variable__. - Macros de Prueba: Para que el archivo
.cse pueda compilar de forma independiente para pruebas, puedes definir macros con los mismos nombres. El script las eliminará automáticamente antes de procesar la plantilla.
//# --- Macros para desarrollo y prueba ---
#define __val_a__ 10
#define __val_b__ 5
//# ------------------------------------
int a = __val_a__;
int b = __val_b__;Los metadatos se definen en bloques de comentarios /* seccion ... */.
Define el nombre base de la pregunta en el banco de Moodle.
/*name
Operaciones Aritméticas Básicas
*/Define las variables dinámicas. Cada línea es una variable con la sintaxis
nombre: expresion_python.
nombre: El nombre de la variable (sin los__).expresion_python: Cualquier expresión de Python válida que devuelva un iterable (como una lista o unrange). El script elegirá un valor al azar de este iterable.
/*var
val_a: range(2, 11)
val_b: range(2, 11)
operador: ["+", "-", "*"]
*/Define una lista de respuestas incorrectas fijas que siempre estarán disponibles.
/*opciones
Error de compilación.
Comportamiento no definido.
0
*/Define "distractores inteligentes". Son expresiones de Python que se evalúan para generar respuestas incorrectas plausibles, basadas en los valores de las variables dinámicas.
- Las variables se referencian con el formato
__nombre_variable__. - Las líneas que comienzan con
//#son comentarios de documentación interna y se ignoran. - Las líneas que comienzan con
#seguido de espacio son expresiones Python evaluables que se procesan para generar distractores dinámicos. - Las líneas sin
#también son expresiones evaluables (sin el prefijo).
Ejemplo básico:
/*distractors
//# Esto es un comentario que se ignora
__val_a__ + __val_b__
__val_a__ * __val_b__
*/Ejemplo con expresiones condicionales:
/*var
file_mode: ['"w"', '"a"']
valor_1: range(100, 200)
valor_2: range(300, 400)
*/
/*distractors
# __valor_1__ if __file_mode__ == '"w"' else -1
# __valor_2__ if __file_mode__ == '"a"' else -1
*/En este ejemplo, los distractores dependen del valor de file_mode, generando respuestas incorrectas contextuales según la variante.
Si este bloque está presente, su contenido se usará como la respuesta correcta sin compilar ni ejecutar el código. Es ideal para preguntas conceptuales donde la respuesta es un texto fijo.
Respuesta fija:
/*correcta
Se produce un error de compilación.
*/Respuesta evaluable con expresión Python:
Si la primera línea (sin comentarios //#) comienza con #, se interpreta como una expresión Python evaluable:
/*var
file_mode: ['"w"', '"a"']
valor_run1: range(100, 200)
valor_run2: range(300, 400)
*/
/*correcta
# __valor_run2__ if __file_mode__ == '"w"' else __valor_run1__
*/En este ejemplo, la respuesta correcta depende del modo de apertura del archivo: si es "w" (write), la respuesta es valor_run2; si es "a" (append), es valor_run1.
Define la entrada estándar (stdin) que se proporcionará al programa durante su ejecución. Este bloque es especialmente útil para programas que requieren interacción con el usuario a través de funciones como scanf, fgets, getchar, etc.
Cuando se utiliza STDIN:
- El contenido se pasa al programa durante su compilación y ejecución para obtener la respuesta correcta.
- Se muestra automáticamente en el enunciado de la pregunta debajo del código, bajo un encabezado "#### Entrada (stdin):", para que el estudiante pueda ver qué entrada recibe el programa.
El bloque soporta:
- Texto estático: Líneas de texto fijo.
- Variables dinámicas: Referencias a variables usando el formato
__nombre_variable__. - Expresiones f-string: Uso de llaves
{variable}para interpolar valores de variables.
Ejemplo con variables dinámicas:
/*var
num_a: range(1, 10)
num_b: range(1, 10)
*/
/*STDIN
__num_a__
__num_b__
*/Ejemplo con f-string:
/*var
count: range(2, 5)
base: range(10, 20)
*/
/*STDIN
{count}
{base}
{base}
{base}
*/Ejemplo completo con scanf:
// El programa lee dos números y los suma:
#include <stdio.h>
int main() {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
printf("%d\n", a + b);
return 0;
}
// ¿Qué valor imprime?
/*var
num_a: range(1, 10)
num_b: range(1, 10)
*/
/*STDIN
__num_a__
__num_b__
*/En Moodle, el enunciado de la pregunta se verá así:
Analiza el siguiente código que lee dos números y los suma:
[código C]
¿Qué valor imprime?
#### Entrada (stdin):
15 16
Cada línea del bloque /*STDIN*/ se pasará al programa como una línea de entrada estándar. Los comentarios de documentación interna (que empiezan con //#) dentro del bloque se ignoran automáticamente.
//# -----------------------------------------------------------------------------
//# PLANTILLA DE EJEMPLO
//# -----------------------------------------------------------------------------
// Dado el siguiente código, ¿cuál es el resultado final?
#include <stdio.h>
//# --- Macros para desarrollo y prueba ---
#define __val_a__ 10
#define __multiplier__ 3
//# ------------------------------------
int main() {
int a = __val_a__;
int resultado = a * __multiplier__;
printf("%d", resultado);
return 0;
}
// ¿Qué valor se imprime en la consola?
/*name
Ejemplo Completo
*/
/*var
val_a: range(2, 11)
multiplier: range(2, 5)
*/
/*opciones
Error de compilación.
0
*/
/*distractors
# Error común: usar suma en lugar de multiplicación
__val_a__ + __multiplier__
*/El script se puede configurar modificando el diccionario CONFIG al inicio del
archivo.
compilation_error_log: Nombre del archivo donde se guardan los errores de compilación de GCC, incluyendo el código que falló.parsing_error_log: Nombre del archivo donde se guardan los errores de formato de las plantillas (ej: si falta un comentario//).substitutions: Un diccionario para reemplazar automáticamente operadores de C por sus equivalentes Unicode para una mejor visualización en Moodle (ej:"==": "⩵") y hacer más díficil que se obtenga la respuesta copiando y pegando.
- Ejecuta
python3 generador.py. - Si una plantilla no se procesa, revisa
parsing_errors.log. El error te indicará qué parte del formato de la plantilla está mal. - Si una plantilla se procesa pero no genera preguntas, revisa
compile_errors.log. El error de GCC te indicará qué está mal en el código C de la plantilla.
El archivo preview.html es una herramienta de visualización que permite
previsualizar el cuestionario XML generado antes de importarlo en Moodle. Esta
herramienta es especialmente útil para revisar el formato, el contenido de las
preguntas y verificar que todo se ve correctamente.
- Visualización local: No requiere servidor web ni Moodle, se ejecuta completamente en el navegador.
- Renderizado de Markdown: Procesa formato Markdown en las preguntas y respuestas, incluyendo bloques de código.
- Resaltado de sintaxis: Utiliza highlight.js para aplicar colores a los bloques de código C.
- Organización por categorías: Muestra la categoría de cada pregunta tal como aparecerá en Moodle.
- Modo mezclar: Parámetro URL
?mix=truepara mostrar las preguntas en orden aleatorio. - Modo respuestas: Parámetro URL
?answers=truepara resaltar visualmente las respuestas correctas.
- Abre el archivo
preview.htmlen tu navegador web. - Haz clic en "Seleccionar archivo XML" y elige el archivo XML generado por
generador.py(por defectocuestionario_moodle.xml). - El cuestionario se mostrará automáticamente con formato legible.
Puedes usar parámetros en la URL para modificar la visualización:
preview.html?mix=true # Mezcla el orden de las preguntas
preview.html?answers=true # Resalta las respuestas correctas
preview.html?mix=true&answers=true # Ambas opciones combinadas
- Revisión de contenido: Verificar que los enunciados, el código y las respuestas se ven correctamente.
- Control de calidad: Detectar errores tipográficos o problemas de formato antes de importar a Moodle.
- Validación de distractores: Con
?answers=true, verificar que las respuestas incorrectas son plausibles y que la correcta es única. - Prueba de variantes: Revisar rápidamente múltiples variantes de las preguntas generadas.
