Programming extension for Numbas

This extension adds a code editor input method, and the ability to run code.

There is built-in support for running:

There is a custom part type in code.npt, which marks code entered by the student by evaluating unit tests.

Running code

The extension provides a JME function run_code. Because the code might take a long time to run, it runs asynchronously; the function returns a promise value.

In order to use the results in marking, call run_code from the part's pre_submit note. The rest of the marking algorithm will run once the code has finished running.

Here's an example:

pre_submit: 
    [
        run_code(
            "pyodide", 
            [
                studentAnswer,
                "'x' in locals()",
                "x==1"
            ]
        )
    ]

Pre-loading a language

If the scripts for a language have not already been loaded when you try to run some code, then they are loaded automatically. However, this can take a long time, depending on the speed of the student's internet connection.

You can pre-load a language with the function Numbas.extensions.programming.preload(language, options).

The options argument is optional and takes the following properties:

<dl> <dt><code>packages</code></dt> <dd>A list of names of packages to load.</dd>

<dt><code>files</code></dt>
<dd>A list of names of files uploaded to the question resources directory to load. They are available under the filesystem path <code>/resources</code>.</dd>

<dt><code>context_id</code></dt>
<dd>An identifier for the context the files should be associated with.</dd>

</dl>

For example, if your question will use R with the packages ggplot2 and dplyr and rely on a file called data.csv uploaded as a question resource, put this line in your question's JavaScript preamble:

Numbas.extensions.programming.preload('webr', {packages: ['ggplot2', 'dply'], files: ['data.csv'], context_id: question.number});

The necessary files will start loading as soon as part of the exam loading process, so will usually be ready to use by the time the student submits some code.

The code editor input method

The extension provides the Ace editor as an input method for custom part types. It has three options:

JME functions

run_code(language, codes, context_id)

Run some blocks of code and return the results in a promise.

The blocks are run one after the other, in the same global scope, so variables assigned in one block are available in subsequent blocks.

When run in the pre_submit note, this task adds an entry code_result, a list containing the results of each of the code blocks. The result of a code block is represented in a dictionary of the following form:

variables_as_code(language, variables)

Produce a block of code which assigns a series of variables in the given language. The variables argument is a dictionary mapping variable names to values.

code_block(code,options)

Display some code in a syntax-highlighted code area.

The options argument is an optional dictionary of options, or just the name of the language to use for syntax highlighting. The following options can be set:

language_synonym(runner)

Return the name of the language corresponding to the given runner.

"pyodide" returns "python"; "webr" returns "r".

Marking functions

There are a few functions to produce common marking tests:

Validation functions

There are a few functions to produce common validation tests:

Adding another language

The class Numbas.extensions.programming.CodeRunner contains methods for running code in a particular language. To add a new language, define a new class extending this one, implementing the run_code method.

Then call Numbas.extensions.programming.register_language_runner(name, runner) with the name of the runner and the class. Only one instance of the class will ever be created.

See the PyodideRunner and WebRRunner classes in this extension for examples of how to implement a runner.

Functions specific to R

r_load_files(files)

Read the contents of the given files and return the result in a promise, to be used by a pre-submit task.

When run in the pre_submit note, this task adds an entry r_files, a list containing information about each of the files:

At the moment, only PDF files are recognised as binary. You can register other file types as binary by adding an entry to the JavaScript object Numbas.extensions.programming.mime_types.