This post is about authoring questions in the Numbas e-assessment system. The good thing about Numbas is that you can install a local editor and export questions as scorm objects which can then be imported into Moodle without having to get plugins installed, unlike STACK say.

As an example, suppose you want to create a question which asks students for an example of a set \(X\) with particular properties, say \({0} \in X\) and \({0} \subseteq X\). When you create a “mathematical expression” question part you have to provide a “correct answer” under marking settings, and the marking algorithm compares against that. This is a problem in that we’d like to allow any correct answer.

Whatever correct answer you choose could be displayed to the student at some point, so it must be an actual answer to the question (rather than say making the correct answer true and adding a script to convert the student answer to the boolean result of testing whether it has the required properties).

My solution was to go to scripts and get the following to run instead of the builtin marking algorithm. This code is based on Daniel Mansfield and Laure Helme-Guizon’s work on this question.

var unwrap = Numbas.jme.unwrapValue;
var istype = Numbas.jme.isType;

// the question asks for a set X such that {l1} \subset X and 
// {l1} \in X, where l1 is a variable defined in the question setup

try {
    var ans = this.studentAnswer;

    if (!ans) { // they've submitted an empty box
        return;}

    if (!istype(this.question.scope.evaluate(ans), "set")) {
        // numbas jme objects have a .type attribute, isType compares
        // it with a string you provide
        this.setCredit(0, "Your answer must be a set.");
    } else { // it is a set, proceed with marking
        var issubset = this.question.scope.evaluate("intersection(set(l1), " + ans + ") = set(l1)");
        // I don't know another way to do membership testing in jme :/
        var iselt = this.question.scope.evaluate("intersection(set(set(l1)), " + ans + ") = set(set(l1))");

        if (unwrap(iselt) && unwrap(issubset)) {
            // iselt and is issubset must be unwrapped to js bools, otherwise they are truthy...
            this.setCredit(1, "Good work!");
        } else {
            this.setCredit(0, "Your set doesn't have the right properties."); }}      
} catch(e) {
    this.setCredit(0);  // the student's answer isn't a valid expression, give 0 credit
    this.markingComment(e);
    alert(e);}

You could easily give more helpful feedback and/or partial credit by adding more cases to the conditional at the end.

When I tried this as a gapfill question I kept causing the Numbas javascript to get stuck in a loop for some reason - this is a pity as I prefer the gapfill formatting.

Here’s a link to the compiled question.