GitHunt
HL

hlfernandez/markdown-question-bank

๐Ÿ“ Markdown Question Bank

License: MIT

A flexible, multilingual, and scriptable question bank system for building quizzes and generating printable PDFs from Markdown.


๐Ÿ“‹ Table of contents


๐Ÿ“ฆ Features

  • ๐Ÿ—‚๏ธ Organize questions by topic using folders
  • ๐ŸŒ Multilingual support for questions and answers
  • ๐Ÿ”€ Shuffle answers and questions for randomized quizzes
  • ๐Ÿท๏ธ Group questions by topic or distribute evenly
  • ๐Ÿ–จ๏ธ Generate printable PDFs with custom headings and styles
  • ๐Ÿงฉ Jinja2 templating for dynamic PDF headings

๐Ÿ› ๏ธ Getting Started

๐Ÿ Conda Environment

conda env create -f environment.yml
conda activate markdown-question-bank

๐Ÿ—‚๏ธ Questions Format

The bank of questions consists of one or several Markdown files that can be optionally organized in subfolders that act as topics. Within each file, questions are written in Markdown and separated by ---. The right answer is marked with an [X] at the beginning, right after the list symbol. Here is a sample question:

What is the result of `len("Python")`?

- 5
- [X] 6
- 7

โ„น๏ธ Metadata

Optionally, a question may include a metadata table after the list of possible choices. For instance, the following question will be associated to Difficulty = Low and TeachScore = 2.

What is the result of `len("Python")`?

- 5
- [X] 6
- 7

| Difficulty  | TeachScore |
|-------------|------------|
| Low         | 2          |

These metadata keys can be used later for excluding questions with the --exclude-metadata option or to implement other custom filterings or groupings.

๐Ÿ“„ Code Appendices

Several questions may refer to the same piece of code. To avoid repeating it across them and having long quizzes, code appendices can be created at the end of each Markdown file as follows:

# Appendices

## Code 1

```python
def misterio(x):
    return x[::-1]

print(misterio("abc"))
```

And then the appendix is referred in the question as follows:

Given the Python code from [this appendix](#code-1), indicate what its behavior and console output would be:

- [X] The function reverses the string.
  The output would be: `cba`.
- The function removes the first letter and prints the remaining string.
  The output would be: `bc`.
- The function transforms the string to uppercase.
  The output would be: `ABC`.

This way, if the question is included in the bank and sampled for building a quiz, the corresponding appendix will be added at the end of the quiz.


๐Ÿ“Š Bank Information

Use the bank_summary.py command to get a summary of the bank:

export PYTHONPATH=src
python bank_summary.py \
  --folder-path test_data/bank \
  --num-alternatives 3

๐Ÿงฒ Filters

This command allows two filters (also available in the generate_quiz.py command):

  • --exclude-topic: Topic(s) to exclude from the question bank. Can be specified multiple times.
  • --exclude-metadata: Metadata fields to exclude from the question bank. Can be specified multiple times. It must follow the following format: <language>:<field_name>:<value>.

๐Ÿ—๏ธ Building Quizzes

Show the command help with:

export PYTHONPATH=src
python generate_quiz.py --help

Interesting flags:

  • --equal-questions-per-topic: Distributes the total number of questions as evenly as possible across all topics. If a topic does not have enough available questions, the remaining questions are taken from other topics with more questions.
  • --group-by-topic: Groups questions by topic. Otherwise questions appear in a random order.

Below are provided some examples of this command.

โœจ Example 1: Without Topics

Generate two models with 6 questions each. Each model will have the same subsample of questions from the bank in the same order. The answers will have a different order in each model (because of --shuffle-answers).

export PYTHONPATH=src
python generate_quiz.py \
  --folder-path test_data/all \
  --outdir test_output \
  --num-models 2 \
  --num-questions 6 \
  --num-alternatives 3 \
  --shuffle-answers \
  --num-cols 3 \
  --lang GL \
  --seed 1234

๐Ÿ—ƒ๏ธ Example 2: With Topics

Generate two models with 6 questions each, grouped by topic (because of --group-by-topic):

export PYTHONPATH=src
python generate_quiz.py \
  --folder-path test_data/bank \
  --outdir test_output \
  --num-models 2 \
  --num-questions 6 \
  --num-alternatives 3 \
  --shuffle-answers \
  --group-by-topic \
  --num-cols 3 \
  --lang GL \
  --seed 1234

๐Ÿ–จ๏ธ Building PDFs

After building your quizzes in an output directory (e.g., outdir), you can create PDFs using generate_pdf.sh (via pandoc, which is included in the conda environment).

You will need:

  • ๐Ÿ“ The input directory where all generated markdown files are located.
  • ๐Ÿท๏ธ A heading file to include in all generated PDFs.
  • ๐ŸŽจ A CSS file to customize the output.
./generate_pdf.sh -d test_output -h test_files/head.md -c test_files/style.css

A PDF file for each markdown quiz will be placed alongside it.

๐Ÿงช Advanced: Jinja2 Headings

If you want your heading file to be dynamic, you can use Jinja2 in your heading files. The filename variable will be available inside your Jinja2 heading template (see test_files/head.jinja2):

./generate_pdf.sh -d test_output -h test_files/head.jinja2 -c test_files/style.css

๐Ÿ‘จโ€๐Ÿ’ป Developers

๐Ÿ”„ Updating Version

The project includes a script to update the version across all relevant files:

./update_version.sh <new_version>

For example:

./update_version.sh 1.2.0

๐Ÿงช Running Tests

To run the test suite:

conda activate markdown-question-bank
pytest

Or to run a specific test file:

conda activate markdown-question-bank
pytest tests/test_parser_bank.py -s

๐Ÿ‘ฅ Contributors

Made with contrib.rocks.


๐Ÿค Contributing

Pull requests and suggestions are welcome! Feel free to open an issue or submit a PR.


hlfernandez/markdown-question-bank | GitHunt