๐ Markdown Question Bank
A flexible, multilingual, and scriptable question bank system for building quizzes and generating printable PDFs from Markdown.
๐ Table of contents
- ๐ฆ Features
- ๐ ๏ธ Getting Started
- ๐๏ธ Questions Format
- ๐ Bank Information
- ๐๏ธ Building Quizzes
- ๐จ๏ธ Building PDFs
- ๐จโ๐ป Developers
- ๐ฅ Contributors
- ๐ค Contributing
๐ฆ 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 --helpInteresting 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.cssA 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
pytestOr 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.