ua-exam
ua-exam is a Python CLI tool designed to automate the creation of physical paper exams from Moodle question banks (GIFT format). It allows instructors to define complex exam structures via a JSON configuration, randomly select questions from hierarchical categories, generate professional LaTeX-ready documents, and support multiple randomized exam copies.
Detailed documentation is available on the GitHub Pages.
Key Features
- Advanced GIFT Parsing:
- Support for
.giftand.txtfiles. - Handles Multiple Choice, True/False, and Open Answer (Essay).
- Support for Multiple Response (partial credits) with multi-answer solution keys.
- Robust handling of questions containing internal blank lines and complex formatting.
- Support for
- Hierarchical Question Bank:
- Recursive scanning of questions folders.
- Automatic indexing by the GIFT
$CATEGORYdirective. - Flexible category matching using a global
category_prefix.
- Smart Randomization & Deduplication:
- Option Shuffling: Choices are randomized for every question to ensure variety.
- Global Deduplication: Prevents the same question from appearing twice in the same exam.
- Reproducibility: Use the seed flag to generate the exact same exam order.
- Multiple Exam Versions (Copies):
- Automatically generate multiple randomized copies of the same exam structure (e.g. Version A, Version B) with sequential seeds.
- Professional LaTeX Layout:
- Native support for LaTeX Math ($...$) and Code Blocks (
code). - Automated university branding (Logo and Header).
- Pre-configured identifying fields (Name, Number, Classification).
- Solutions generated as a CSV file, perfectly mapped to the shuffled choices.
- Native support for LaTeX Math ($...$) and Code Blocks (
- Modular Question Conversion:
- Extensible converter registry to transform GIFT files into other Moodle or LMS formats.
Project Structure
.
├── src/
│ └── ua_exam/
│ ├── __init__.py # Package initialization
│ ├── cli.py # CLI entry point
│ ├── exam_generator.py # Exam generation engine
│ ├── gift_parser.py # GIFT parsing module
│ ├── gift_escaper.py # GIFT escaping/markdown translation module
│ ├── pdf_renderer.py # PDF compilation module
│ ├── converters/ # Modular question format converters
│ └── assets/
│ └── logo_ua_cropped.pdf # Packaged default university logo
├── test/ # Test suite
├── examples/ # Configuration examples
└── assets/
└── logo.svg # Project logo
Installation
Install directly from source or via PyPI using pip or uv:
pip install .
Usage
The ua-exam command-line tool provides three primary subcommands: generate, escape, and convert.
1. Generating Exams (generate)
Generates randomized exam copies from a JSON configuration file.
ua-exam generate -c config.json -s 42 -o out/exam.md --pdf
-c,--config: Path to the JSON configuration file (required).-s,--seed: Base random seed (default: 42).-o,--output: Base output file path (default: exam.md).--pdf: Automatically compile the generated Markdown file into a PDF.
Configuration (config.json)
The config file controls the exam generation logic.
| Field | Description | Default |
|---|---|---|
class |
The name of the course or exam. | "Exam" |
date |
Date string to appear on the header. | Current Date |
lang |
Language for fixed labels (pt or en). |
"pt" |
exam |
Exam type (e.g., "Normal", "Recurso", "Teste 01"). | "Normal" |
logo |
Path to the university logo PDF. | Packaged Logo |
questions_folder |
Root directory of your GIFT/TXT files (used if questions_folders is omitted). |
"." |
questions_folders |
Alternative array of question source folders. Each element can be a string path or an object with path and category_prefix. |
Optional |
category_prefix |
Common prefix for all categories (used globally with questions_folder). |
"" |
target_score |
Total expected score for validation. | 20.0 |
duration |
Exam duration string. | "60 minutos" |
instructions |
General instructions for the exam. | (Generic) |
copies |
Number of randomized copies of the exam to generate. | 1 |
parts |
Array of sections. | Required |
Example Configurations
Example 1: Single Question Folder
{
"class": "Laboratórios de Sistemas e Serviços",
"lang": "pt",
"exam": "Teste 01",
"copies": 3,
"category_prefix": "lss/test01/exam",
"questions_folder": "gift/test01",
"parts": [
{
"part": "Escolha Múltipla",
"classification": 10,
"questions": [
{ "topic": "01-terminal", "quantity": 3 },
{ "topic": "02-virtualizacao", "quantity": 4 }
]
},
{
"part": "Resposta Aberta",
"classification": 10,
"questions": [
{ "topic": "open-questions", "quantity": 4 }
]
}
]
}
Example 2: Multiple Question Folders (with prefixes)
{
"class": "Laboratórios de Sistemas e Serviços",
"lang": "en",
"exam": "Exam",
"copies": 2,
"questions_folders": [
{"path": "gift/test01/en", "category_prefix": "lss/test01/exam"},
{"path": "gift/test02/en", "category_prefix": "lss/test02/exam"}
],
"parts": [
{
"part": "Multiple Choice",
"classification": 10,
"questions": [
{ "topic": "01-terminal", "quantity": 2 },
{ "topic": "02-virtualizacao", "quantity": 2 }
]
}
]
}
2. Escaping and Converting MD to GIFT (escape)
Converts a formatted Markdown file into a Moodle-compatible GIFT file, handling necessary special character escaping.
ua-exam escape -i input.md -o output.gift
3. Modular Question Format Conversion (convert)
Converts GIFT question banks to other question formats (e.g. Moodle XML).
ua-exam convert -i input.gift -f xml -o output.xml
Authors
- Mário Antunes - mariolpantunes
License
This project is licensed under the MIT License - see the LICENSE file for details.
Metadata
Release files for ua-exam 0.2.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 | |
|---|---|---|---|---|
| ua_exam-0.2.0.tar.gz | 23.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ua_exam-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.5 kB
Release files / ua_exam-0.2.0.tar.gz
| Download URL | ua_exam-0.2.0.tar.gz |
|---|---|
| Size | 23.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c18f785e4f4c971a8c69199c4dbd7eccce2e435f3c6ba2808b137f8f388db648
|
|
BLAKE2b-256 checksum How to use checksums |
a40a9ec01797a560228cb5b06822ec96586fa19a6c0921776057731cadff0379
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / ua_exam-0.2.0-py3-none-any.whl
| Download URL | ua_exam-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4dcf8377a47044dfa66a0608baaeb00eb2391ce6c0495d906e318740f8d591f3
|
|
BLAKE2b-256 checksum How to use checksums |
8c7045219a182d1767c27ed1db8d97c1e080f38103943a7b34e4455ff84f4085
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Security for ua-exam 0.2.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.