Genera variantes de preguntas de opción múltiple cuyas respuestas correctas se determinan automáticamente mediante cálculo proposicional. Combina supuestos (hipótesis) y cuestiones (opciones de respuesta) por combinatoria, calcula la veracidad o falsedad de cada cuestión según las hipótesis activas y exporta el resultado a LaTeX (AMC) o Moodle (XML vía LaTeX).
Construido sobre calcprop.
Instalación
Conviene instalar dentro de un entorno virtual (en Debian, Ubuntu o con
el Python de Homebrew, pip fuera de uno da
externally-managed-environment):
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install calcprop-qbank # instala también calcprop
El Manual (sección «Instalación paso a paso») lo explica despacio, también para Windows.
Con soporte para el editor visual en Jupyter:
pip install "calcprop-qbank[jupyter]"
qbank-init y lint llegan con la 0.6.0: si pip te deja una versión
anterior (python -c "import qbank; print(qbank.__version__)" falla o
da menos de 0.6), actualiza con pip install -U calcprop-qbank. La
versión en desarrollo se instala desde el repositorio:
pip install "calcprop-qbank[jupyter] @ git+https://github.com/mbujosab/calcprop-qbank"
Esa orden necesita git instalado. El paquete solo instala el código.
El tutorial (Tutorial.ipynb), la guía de diseño (GuiaDiseno.org y su
versión impresa GuiaDiseno.pdf), el manual (Manual.org,
Manual.pdf) y los ejemplos de ejemplosManual/ están en el
repositorio: clónalo con
git clone https://github.com/mbujosab/calcprop-qbank o abre el
tutorial sin instalar nada con la insignia de MyBinder de arriba. La
guía y el manual se leen también en la web,
mbujosab.github.io/calcprop-qbank.
Crear un banco de preguntas
La forma recomendada de empezar es la plantilla de proyecto que se
distribuye con el paquete (qbank-init necesita la versión 0.6 o
posterior):
qbank-init mi-banco
cd mi-banco
make lint # comprueba las preguntas de ejemplo (sin LaTeX)
make amc # PDF de revisión (vista profe) en build/amc/
El proyecto contiene cinco preguntas de ejemplo en preguntas/, los
scripts que las exportan a AMC y Moodle, un Makefile (lint, tabla,
amc, amc-student, moodle, lectura, clean, filtro LECCION=N),
nueva_pregunta.py, un guion para escribir una pregunta en Python y
guardarla como JSON, y en prompts/ las instrucciones para escribir
preguntas con ayuda de una IA. Su README.org explica el flujo.
make usa el intérprete python3 del sistema. Si instalaste qbank en
un entorno virtual, actívalo antes (source .venv/bin/activate) o
indícalo con PYTHON: make lint PYTHON=/ruta/al/.venv/bin/python. Si
ese intérprete no tiene qbank 0.6 o posterior, make se para y lo dice.
Uso rápido
from qbank import *
p = ProblemaTipo(
[
"Sea $f$ una función ",
[ # mundos alternativos: en cada variante se sortea uno
Supuesto("estrictamente creciente", v('crec')),
Supuesto("estrictamente decreciente", v('decr')),
],
" y sean $a < b$. Entonces:",
[ # hueco 1: el alumno ve una de estas dos
Cuestion("$f(a) < f(b)$", v('crec')),
Cuestion("$f(a) > f(b)$", v('decr')),
],
Cuestion("$f(a) = f(b)$", False), # hueco 2: siempre
[ # hueco 3
Cuestion("$f(b) - f(a) > 0$", v('crec')),
Cuestion("$f(b) - f(a) < 0$", v('decr')),
],
],
export={"last_choice": True}, # + «Ninguna de las anteriores»
)
for etiqueta, partes in p.por_partes():
for enunciado, cuestiones in partes:
print(f"Variante {etiqueta}: {enunciado}")
for texto, correcto, activa, exp in cuestiones:
print(f" {'✓' if correcto else '✗'} {texto}")
Ocho variantes (pocas para un banco real: lint lo avisa), cada una con
tres opciones más la opción por defecto («Ninguna de las anteriores»,
correcta solo si las otras tres son falsas). Una lista de Cuestion
sortea una por variante; el alumno ve tantas opciones como
elementos-Cuestion consecutivos haya (GuiaDiseno.org lo explica).
Características
Generación combinatoria de variantes
ProblemaTipo toma una lista de supuestos y cuestiones (cada uno con su
semántica proposicional) y genera todas las combinaciones posibles. Para
cada variante calcula automáticamente qué cuestiones son verdaderas y
cuáles falsas. Las variantes con precondiciones insatisfechas se
descartan. Las hipótesis contradictorias, en cambio, no se detectan (de
ellas se deduce cualquier cosa y todo sale verdadero): se evitan con
axiomas como unoDe(v('A'), v('B')).
El método por_partes_profe() muestra todas las cuestiones de cada
hueco (marcando las rechazadas por su precondición en vez de descartar
la variante), facilitando la revisión del banco.
Preguntas multi-parte
La misma lista plana de componentes admite varias partes: cada vez que
tras un bloque de cuestiones aparece texto o un Supuesto se abre una
parte nueva. Es útil para preguntas AMC con múltiples apartados o para
el formato cloze de Moodle. El método por_partes() devuelve, por
variante, un par (enunciado, cuestiones) por parte:
from qbank import *
p = ProblemaTipo([
Supuesto("", unoDe(v("A"), v("B"))), # axioma: exactamente una es verdadera
"Exactamente una de las proposiciones $A$ y $B$ es verdadera, y la verdadera es ",
[Supuesto("$A$.", v("A")),
Supuesto("$B$.", v("B"))],
" Sobre $A$:", # parte 1: tres huecos
[Cuestion("$A$ es verdadera.", v("A")), Cuestion("$A$ es falsa.", -v("A"))],
[Cuestion("$\\lnot A$ es verdadera.", -v("A")), Cuestion("$\\lnot A$ es falsa.", v("A"))],
[Cuestion("$A \\to B$ es verdadera.", v("A") >> v("B")),
Cuestion("$B \\to A$ es verdadera.", v("B") >> v("A"))],
"Sobre $B$:", # texto tras las cuestiones: parte 2
[Cuestion("$B$ es verdadera.", v("B")), Cuestion("$B$ es falsa.", -v("B"))],
[Cuestion("$\\lnot B$ es verdadera.", -v("B")), Cuestion("$\\lnot B$ es falsa.", v("B"))],
[Cuestion("$A \\land \\lnot B$ es verdadera.", v("A") & -v("B")),
Cuestion("$\\lnot A \\land B$ es verdadera.", -v("A") & v("B"))],
], export={"last_choice": True}) # + «Ninguna de las anteriores» en cada parte
for etiqueta, partes in p.por_partes():
for enunciado, cuestiones in partes:
...
Cada parte tiene tres huecos (una lista sortea una Cuestion, así que
una sola lista daría una única opción) más la opción por defecto. Las
negaciones -v("A") se deducen gracias al axioma unoDe; sin él,
lint da ERROR negacion. Este ejemplo pasa lint sin avisos (128
variantes).
(ProblemaMultiParte y SubPregunta siguen existiendo por
compatibilidad, pero están deprecados y devuelven un ProblemaTipo
equivalente.)
Preguntas paramétricas con setup
El parámetro setup (código Python, como cadena) se ejecuta antes de
cada variante; sus variables se interpolan en los textos con
@{variable} y están disponibles en ns para semánticas y
precondiciones escritas como lambda:
p = ProblemaTipo(
["Sean $a = @{a}$ y $b = @{b}$. Entonces:",
[Cuestion("$a + b = @{suma}$", True),
Cuestion("$a + b = @{suma_falsa}$", False)],
[Cuestion("$a + b > 10$", "lambda ns: ns['a'] + ns['b'] > 10"),
Cuestion("$a + b < 10$", "lambda ns: ns['a'] + ns['b'] < 10")],
Cuestion("$a \\cdot b = @{producto}$", True)],
setup="import numpy as np\n"
"a, b = (int(x) for x in np.random.randint(1, 10, 2))\n"
"suma, suma_falsa, producto = a + b, a + b + 1, a * b",
seed=1, export={"last_choice": True, "instances": 15},
)
Con seed la pregunta es reproducible e instances fija cuántas
instancias (sorteos del setup) se generan (aquí 4 variantes × 15 = 60,
el mínimo que recomienda lint con setup). seed siembra
numpy.random y random; usa np.random en el setup, porque los
scripts de la vista profe solo siembran numpy.
Persistencia en formato JSON
Guarda y carga problemas (ProblemaTipo, ProblemaVF) sin reescribir
el código Python:
from qbank import load_problema, save_problema
p = load_problema('mi_problema.json')
save_problema(p, 'copia.json')
Para bancos de múltiples problemas: load_banco / save_banco.
Editor visual en Jupyter
ProblemaTipoEditor es un formulario interactivo (requiere
ipywidgets) que permite construir, previsualizar y guardar problemas
sin escribir código:
from qbank import ProblemaTipoEditor
editor = ProblemaTipoEditor() # editor vacío
editor = ProblemaTipoEditor('p.json') # cargar desde fichero
p = editor.to_problema() # obtener el ProblemaTipo
Ver Manual.org si los widgets se muestran como texto en lugar del formulario.
Exportación a AMC y Moodle
Formato AMC (LaTeX)
Las funciones de exportación no leen el campo export del problema:
pásales last_choice=, instances= y cols= tú mismo. Los scripts de
un banco creado con qbank-init sí lo leen.
AMCblock y AMC_multipart son funciones de bajo nivel que devuelven
un bloque LaTeX listo para escribir al fichero. Una pregunta multi-parte
necesita AMC_multipart (un único questionmult con un bloque de
casillas por parte): un AMCblock por parte daría identificadores
duplicados y la segunda parte sin las hipótesis.
with open("preguntas.tex", "w") as f:
for etiqueta, partes in p.por_partes():
if len(partes) > 1:
f.write(AMC_multipart("MiCuestionario", etiqueta, "", partes, last_choice=True))
else:
f.write(AMCblock("MiCuestionario", etiqueta, partes[0][0], partes[0][1],
last_choice=True))
Vista profe (todas las cuestiones de cada hueco, con marcas; usa
AMC_multipart cuando hace falta):
import os
os.makedirs("exportaciones", exist_ok=True)
n = QuizAMCProfe("MiCuestionario", "exportaciones", p, last_choice=True)
print(f"{n} bloque(s) escritos")
Formato Moodle (XML vía LaTeX)
import os
os.makedirs("exportaciones", exist_ok=True)
# Vista alumno
n = QuizMoodle("MiCuestionario", "exportaciones/", p, last_choice=True)
print(f"{n} variante(s) escritas")
# Vista profe (fracciones de puntuación visibles); escribe también
# <nombre>.tex, así que necesita otro nombre para no pisar la del alumno
QuizMoodleProfe("MiCuestionarioProfe", "exportaciones/", p, last_choice=True)
# Con paquete LaTeX adicional y texto de la opción por defecto personalizados
QuizMoodle("MiCuestionario-b", "exportaciones/", p, last_choice=True,
aux_latex=r"\usepackage{nacal-moodle}",
last_choice_text="Ninguna de las otras opciones es correcta")
QuizMoodle y QuizMoodleProfe devuelven un int con el número de
variantes escritas, útil para logging en scripts de build.
Para preguntas ProblemaVF: QuizVFMoodle.
Vista de lectura
QuizLectura escribe un documento LaTeX para leer y revisar las
preguntas (no para el examen): la tabla de mundos, un ejemplo por mundo
con el enunciado en un recuadro, las alternativas agrupadas por hueco
con su V/F y su exp, y las que la precondición excluye con su motivo.
Con "lectura": {"cubrir": ["ctx"]} en el JSON, un ejemplo por cada
valor de esas variables del setup. En un proyecto de qbank-init,
make lectura LECCION=N da un PDF por lección.
QuizLectura("L-03-lectura", "build/lectura", [p1, p2, p3],
aux_latex=r"\usepackage[spanish]{babel}")
Exportación a código Python editable
save_problema_py genera un fichero .py con la definición del
problema como listas editables:
from qbank import save_problema_py
save_problema_py(p, 'mi_problema.py')
El fichero resultante puede abrirse con cualquier editor, modificarse y ejecutarse directamente con Python.
Comprobación automática de preguntas (lint)
lint revisa un problema (objeto, dict o ruta a JSON) sin modificar
nada ni compilar LaTeX y devuelve una lista de
Aviso(nivel, codigo, mensaje, donde) con nivel ERROR, AVISO o
INFO:
from qbank import lint, tabla_mundos
for a in lint("preguntas/L-01-FuncionMonotona.json"):
print(a)
print(tabla_mundos("preguntas/L-01-FuncionMonotona.json")) # Cuestion × mundo
Desde la línea de órdenes (código de salida 1 si hay algún ERROR):
python -m qbank.lint preguntas/*.json [--instances N] [--seed S] [--tabla] [--quiet] [--patrones [N]]
qbank-lint preguntas/*.json --quiet # equivalente; omite INFO y ficheros OK
Comprueba, entre otras cosas, la garantía de mezcla (1 < correctas <=
opciones-1= en toda variante, contando la opción por defecto de
last_choice), el número de variantes, negaciones -v('X') que nunca
podrían deducirse por falta de un unoDe(...), interpolaciones @{var}
sin resolver, etiquetas huérfanas, la estructura del JSON y caracteres
que rompen la exportación a Moodle. La tabla de mundos muestra el valor
de cada Cuestion en cada combinación de Supuesto (V, F o - si
la rechaza su precond), para localizar distractores constantes. Con
--patrones (o lint(..., patrones=True); make patrones en un
proyecto) busca además patrones memorizables: lo que un alumno podría
acertar sin leer el enunciado tras ver varias variantes (una opción casi
siempre verdadera, una cifra que separa las verdaderas de las falsas, la
frase larga o la cifra grande siempre falsa…). Es más lento y sus avisos
son indicios que hay que leer.
Documentación
Todo se puede leer sin clonar el repositorio en mbujosab.github.io/calcprop-qbank (HTML y PDF). En este orden:
- Tres cuadernos Jupyter, que se abren en MyBinder sin instalar nada:
Tutorial.ipynb(MyBinder) — cinco ejemplos progresivos ejecutables: verdadero/falso literales, mundos,precond, interpolación@{var}y semántica lambda.Demo.ipynb(MyBinder) — los mismos ejemplos, recorriendo la API Python y el editor visual.Ejemplos.ipynb(MyBinder) — el flujo completo en Python con un ejemplo de econometría: construir la pregunta, revisarla, guardarla en JSON y exportarla a Moodle y AMC.
GuiaDiseno.org(impresa enGuiaDiseno.pdf) — cómo diseñar buenas preguntas: la mecánica de listas y mundos, el patrón de tres huecos + opción por defecto,setup, redacción de cuestiones y explicaciones, trampas de LaTeX/Moodle y checklist.Manual.org(Manual.pdf) — referencia completa de la API y del formato JSON.- Prompt genérico para una
IA — si
escribes las preguntas con un asistente:
qbank-initdeja enprompts/este prompt (qué leer, proceso y reglas) y otro con huecos para las decisiones de tu banco, más unCLAUDE.mdde entrada. CalcPropQuiz.orgyCalcPropExport.org— código fuente comentado (programación literate) de los módulos_quiz,_json,_widgets,_lint,_exporty_plantilla. Son la fuente desrc/qbank/: los.pyse generan conorg-babel-tangleo, sin Emacs, conpython tools/orgtangle.py write;tests/test_literate.pycomprueba que coinciden.
Tests
pytest
Autoría y licencia
Copyright (C) 2020-2026 Andrés Bujosa, Marcos Bujosa (_quiz)
Copyright (C) 2020-2026 Marcos Bujosa (_export, _json, _widgets)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See the LICENSE file for details.
Metadata
Release files for calcprop-qbank 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| calcprop_qbank-0.8.0.tar.gz | 105.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| calcprop_qbank-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 209.0 kB
Release files / calcprop_qbank-0.8.0.tar.gz
| Download URL | calcprop_qbank-0.8.0.tar.gz |
|---|---|
| Size | 105.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b4028c12816d7b930707d2b718f053886722e6516f9df607b2d9577bc8a9522a
|
|
BLAKE2b-256 checksum How to use checksums |
fa18b548012659267a003f2f291b171d4064d4562788af944b6e128a4f701cd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.2
|
Release files / calcprop_qbank-0.8.0-py3-none-any.whl
| Download URL | calcprop_qbank-0.8.0-py3-none-any.whl |
|---|---|
| Size | 103.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
54deb0aef9fca3e0ef0521b7ef6719672614ddfd9a63f59b2b1c3d8706c4920e
|
|
BLAKE2b-256 checksum How to use checksums |
13489296ff6a435e891c271cfe5670874691fefbdf0f2328d7fa11ae1641cbe2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.2
|
Security for calcprop-qbank 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.