Builds PrairieLearn questions containing auto-graded Jupyter notebooks
Project description
PrairieLearn Notebook Question
plnq generates complete, auto-graded, notebook-based PrairieLearn questions using a specification contained in a single .ipynb file. Specifically, users place
- the problem (i.e., function) descriptions,
- sample input/output,
- test cases,
- PrairieLearn metadata, and
- sample solutions
into a single .ipynb file. plnq then uses this data to generate a complete PrairieLearn question (including info.json, question.html, server.py, all workspace files and all test files.)
Using PrairieLearn terminology, plnq generates a single question: a directory that you would place inside questions. This question can have as many parts (i.e., functions to write) as you want; but, from PrairieLearn's perspective it is a single "question".
plnq assumes that
- a question is a sequence of tasks, where each task asks students to implement a well-defined function, and
- each function is tested using a set of instructor-provided tests.
The result of running plnq is a complete notebook-based, auto-graded PrairieLearn question. You can modify this question (e.g., to add features not supported by plnq); however, be careful when doing this because running plnq again will overwrite these changes. (Or, if you avoid running plnq again, any oversights in the template will need to be fixed "by hand".)
Usage:
Simply run python plnq name_of_template.ipynb name/of/output/directory.
For example, from the root of this repository, run python plnq examples/simple_example_loop_hw.ipynb loc/of/pl/repo/questions/plnq_example. Your chosen PrairieLearn course will now have a new question named plnq_example.
- The output directory is the name of the new directory that will contain the question. This should be unique for each question. (In other words, the output should be
pl-uni-cis500/questions/my_new_questionrather thanpl-uni-cis500/questions) - The output directory is optional and defaults to the name of the template without the
.ipynbextension. plnqis very paranoid about overwriting directories. That's by design. (I have nightmares about accidentally doing something dumb likeplnq new_question.ipynb ~.)
Template configuration
A template is just an .ipynb file.
First Block
The first block is a code block containing several dictionaries:
info contains data placed in info.json including: title, topics, and tags
plnq.info = {
"title": "Writing Functions",
"topic": "functions",
"tags": ["functions", "hw"]
}
After the initial question-level metadata block, the remaining blocks describe the specific tasks. Each task requires two blocks
- A Markdown block containing instructions, and
- A code block containing the solution, example input/output, test cases, and other task-level metadata.
Task Description Block
For the most part, the Markdown block simply contains the text the students read. plnq automatically adds the sample input/output to the end of this block.
Important: This block should contain the function's signature delineated with !!!. (plnq uses this to build the sample input/output.)
# Task 1
Write a function !!!`area_of_triangle(a, b, c)`!!! That uses [Heron's Formula](https://en.wikipedia.org/wiki/Heron%27s_formula) to calculate the area of the triangle with side lengths `a`, `b`, and `c`
Task Code Block
Following each Markdown block, is a code block containing the solution to each task, as well as sample input/output, test cases, and other task-level metadata. The purpose of the solution is to
- sanity-check the assignment (i.e., get the instructor to solve the problem to make sure it isn't more difficult than expected) and
- verify that the expected answers for the examples and tests cases are correct.
Call plnq.add_function to provide the additional metadata. The function takes the name of the function as the first
parameter, followed by several named parameters:
desc: The description that appears in list of functions that students are to write. Specifically, this value is used to set upnames_from_userinserver.py. It is also necessary so that PrairieLearn will export the function to the auto-grader.displayed_examples: Lists the input and expected output for examples that are displayed to the user.test_cases: Lists the cases run when the student clicks "Save and Grade". These are not shown to the user.
plnq.add_function('area_of_triangle',
desc='A function that returns the area of a triangle given the lengths of its sides',
displayed_examples=[
[1, 1, 1, math.sqrt(3)/4],
[3, 4, 5, 6.0]
],
test_cases=[
[2, 2, 2, math.sqrt(3)],
[5, 12, 13, 30.0],
[10, 13, 13, 60.0],
[5, 5, 6, 12.0],
[13, 15, math.sqrt(34), 37.5]
]
)
For displayed_examples and test_cases Each example/test case is a list where the first n-1 values are parameters and the last value is the expected return value.
Comparing observed and expected values
By default, plnq simply uses math.isclose to compare floats and == to compare other values.
When comparing floats, plnq uses the default for isclose (rel_tol=1e-9). To specify a different tolerance, use a FloatAnswer for the expected value:
displayed_examples = {
'final_value_monthly': [
[100, 10, 0.05, FloatAnswer(15528.23, abs_tol=0.01)],
[150, 15, 0.08, FloatAnswer(51905.73, abs_tol=0.01)]
]
}
Additional Configuration
- To ignore a Markdown block (i.e., not include it in the resulting question), add the string
!!!PLNQ.Ignore!!!anywhere in the block. Seeexamples/ignore_blocks.ipynb. By default, code blocks are not passed through to the workspace, so there is no need to explicitly ignore them. - To pass a Markdown block through to the generated workspace unchanged, add the string
!!!PLNQ.PassThrough!!!anywhere in the block. Seeexamples/pass_through.ipynb. The default behavior is that Markdown blocks are scanned for a function signature, and a list of example inputs and outputs is added. If a block contains neither a pass-through marker nor a function signature,plnqwill complain. - To pass a Code block through to the generated workspace, add a comment containing
!!!PLNQ.PassThrough!!!anywhere in the block. This comment will not be included in the generated workspace (so don't put the pass-through marker in a comment that that contains other content you want to keep). Note: The code that searches for the pass-through marker is not sophisticated. Don't put the pass-through marker on the same line as a literal string containing a#.
plnq searches for exact matches to the ignore and pass-through markers. If a block is not being handled properly, double-check the capitalization and the number of exclamation marks. Also, using too many exclamation marks may result in stray characters in the output.
Dev Install
To set up for local development:
- Clone this repo
- Create a virtual python environment:
python -m venv venv - Activate the virtual environment:
source venv/bin/activate - Install this package in edit mode:
pip install -e .
(If you don't want to use activate, you can run python commands out of venv/bin. For example, venv/bin/pip install -e .)
The current minimum version is 3.9. The code uses importlib.resources.files which was not
introduced until Python 3.9.
To verify whether this package will still build and run under Python 3.9
cdto the project root- run
./docker_build - run
./docker_run
This will build a Docker image with Python 3.9, then run the automated tests.
Notes:
- To use libraries inside the description block, put the
importstatement at the top of the description block. plnqgeneratestest.py, the source code for the automated tests. That means that the test parameters and expected values need to be converted into Python literals.plnqusesjson.dumpsto do this. Therefore, only values that are JSON serializable are currently supported.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plnq_gvsu-2.0.0.tar.gz.
File metadata
- Download URL: plnq_gvsu-2.0.0.tar.gz
- Upload date:
- Size: 26.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8ec7741f0acef50e53955489bc1c3ba77aed879f7258db314a2d31a8cf98e4b
|
|
| MD5 |
5a1210c3ce14533e191a0bc1cc6a196a
|
|
| BLAKE2b-256 |
c523795999d7f4039250070af9564c6383173596c908a41fe256d55459effca4
|
File details
Details for the file plnq_gvsu-2.0.0-py3-none-any.whl.
File metadata
- Download URL: plnq_gvsu-2.0.0-py3-none-any.whl
- Upload date:
- Size: 31.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
102e38caaf5219eb89ab35f56f8f5423be6ff76fa2efbb1f0fe3d2b2c32b2048
|
|
| MD5 |
4f24191a6e6ad6cf3a7f3e5e4b92d973
|
|
| BLAKE2b-256 |
3190eb7b278b8c593c13a490d584156195839072bd3c4026b34a05812b072aaf
|