Skip to main content

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 en GuiaDiseno.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-init deja en prompts/ este prompt (qué leer, proceso y reglas) y otro con huecos para las decisiones de tu banco, más un CLAUDE.md de entrada.
  • CalcPropQuiz.org y CalcPropExport.org — código fuente comentado (programación literate) de los módulos _quiz, _json, _widgets, _lint, _export y _plantilla. Son la fuente de src/qbank/: los .py se generan con org-babel-tangle o, sin Emacs, con python tools/orgtangle.py write; tests/test_literate.py comprueba 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)

Source distribution for calcprop-qbank 0.8.0
File Size Uploaded Provenance
calcprop_qbank-0.8.0.tar.gz 105.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for calcprop-qbank 0.8.0
File Interpreter ABI Platform
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page