The Ace-gapfiller UI

The Ace-gapfiller UI described in this page is a replacement for the older GapFiller UI. It allows teachers to set fill-in-the-gap programming questions, such as the following. [You will need to login as trial with password trial to run the question. Then refresh the page.]


Question of this sort can be very useful in introductory programming courses when introducing new concepts.

How the Ace-gapfiller UI works

Like all CodeRunner UI plugins, the Ace-gapfiller UI provides the user with an interface for viewing and maniplating a simpler underlying text representation. For example, the standard Ace-UI replaces a simple text area containing code with a syntax-coloured view of the code plus code-editing capabilities like code-folding, search-and-replace, etc. The underlying pure-text is called the serialisation. Any user can view the underlying serialisation by typing CTRL+ALT+M to switch off all UIs on the page. (Type again to toggle the view back on again).

If you enter some code into the gaps in the above examples and then type CTRL+ALT+M, you'll see that the underlying serialisation in the case of the Ace-gapfiller UI is simply a JSON list of the strings that go into the gaps. The non-editable "wrapper program" is constant for each question instance.

When the user clicks Check the answer that is sent to the Moodle server for evaluation by the CodeRunner question template is simply that list of strings. While that can sometimes be useful in that form, if you want to actually test the code by executing it you - or more specifically the question template code that you as question author write - need to insert the gap contents back into the wrapping code to construct an executable program.

The python3_gapfiller_demo question type

The two demo questions shown above are of a custom question type: python3_gapfiller_demo. You can download the file PROTOYPE_python3_gapfiller_demo.xml and import it onto your system if you want a starting point for writing questions like the above. You may also wish to download the xml exports of the two demo questions above:

These two examples demonstrate the two different modes in which the python3_gapfiller_demo question type works, depending on the setting of the UI parameter ui_source, which is defined in the UI Parameters field of the question-authoring form.

ui_source = "test0"

This is the mode demonstrated by the first of the above questions, on list indexing.

If the UI parameters field is set to the string {"ui_source": "test0"} (a JSON object defining the ui_source attribute to be the string 'test0'), the wrapper program is taken from the testcode field of the first test case. It should be an executable Python3 script in which at least one substring of at most one line of code has been replaced by a "gap descriptor" of the form {[10-20]} specifying a gap of 10 characters that can expand to at most 20 characters when the user types into it. This can be simplified to just {[10]} for a fixed-size gap.

The code that the student is presented with consists of just this test0 code rendered with the Ace editor but with the gap descriptor replaced with an entry field.

Important

  1. In this mode, all other test cases must also contain executable code fragments with exactly the same number of gaps.
  2. In this mode you should not click the Use as example checkbox on any tests, as the gap-descriptor does not contain code until the question is run.
    • The question text should explain to the student that their answer will be tested with other, similar, bits of code.

When the user clicks Check the template code receives the list of strings that the user entered. It then runs each test case in turn, inserting the submitted gap strings into each of the test testcode fields. The inserted gap code is highlighted in the result table for clarification.

ui_source = "globalextra"

This is the mode used in the second of the example questions, on while loops.

If the UI parameters field is set to the string {"ui_source": "globalextra"} (or is left empty, since this is the default), the wrapper program is taken from the globalextra field in the question authoring form.

In this mode, the program that is run for each test consists of the wrapper program, with gap-descriptors replaced by the student's gap strings, followed by the test code. This is more like the standard CodeRunner mode where students see a result table of the test code, expected and got fields. 

In this mode you can still use the Use as example checkboxes, since the tests do not contain gap descriptors.

Use of the Ace-gapfiller with other languages

You can edit the python3_gapfiller_demo prototype to handle other languages as follows:

  1. Change the Question name field to something more suitable e.g. PROTOTYPE_cpp_gapfiller (for C++).
  2. In the Advanced customisation section of the form, change the Question type field to something more appropriate, e.g. cpp_gapfiller
  3. In that same section, change the Ace language field to whatever language the wrapper code is written in, i.e. the language in which the student is being tested. cpp, for example. Do not change the sandbox language (which is the language in which the template is written). It's still python3.
  4. Change the run_one_test function in the template to compile (if necessary) and run the given source program prog with the given standard input, stdin
  5. Save and then change the name of the saved prototype to something more suitable, e.g. PROTOTYPE_cpp_gapfiller.

For a C++ version, for example, the run_one_test function might be something like:

def run_one_test(prog, stdin):
    with open("prog.cpp", "w") as src:
        print(prog, file=src)
    try:
        cppflags = "-Wall -Werror"
        result = subprocess.run(
            f"g++ {cppflags} -o prog prog.cpp".split(),
            capture_output=True,
            text=True
        )
        if result.returncode == 0: # If no compile errors
            result = subprocess.run(
                ["./prog"],
                input=stdin,
                capture_output=True,
                text=True,
            )
        output = result.stdout + ('\n' if result.stdout else '') + result.stderr
    except subprocess.CalledProcessError as e:
        output = e.output.decode('utf-8')  + '\n' if e.output else ''
    return output

Java is similar but there is the additional complication of making sure that the source file name matches the main class name. You will probably have to use a regular expression to find the main class name for use in the filename.

The built-in multilanguage question type contains the Python code for compiling and running many different languages. You can read its template by creating a mutlilanguage question instance and clicking its Customise checkbox.

Parting comment

It is recommended that gap filler questions be used only for simple drill-type questions where the teacher wishes students to demonstrate understanding of a particular language construct. They are not recommended for longer answer questions, where students might see problems in a different but totally valid way. Don't get sucked into believing your answer to an algorithmic problem is the "right" one - you'll frustrate the student and crush their creativity!


Última modificación: miércoles, 11 de junio de 2025, 15:46