Ace-inline filter demo
This page demonstrates the capabilities of the new and experimental Ace inline Moodle filter. This filter operates on the rendered HTML of a page, so its use requires that teachers be able to edit content in HTML. See the filter documentation for details.
The original HTML of (most of) this page can be inspected by importing the description 'question' demoaceinline.xml into the question bank on your own Moodle server and opening it for edit in HTML mode. Or you can simply open that link directly in your browser, if you're able to read the XML itself and identify the relevant embedded HTML within it (essentially, all the blue text).
To revert any edited code to its original version just reload the page.
Note: the Ace inline filter requires a new version of CodeRunner that provides a sandbox web service (version 4.2.1 or later).
Highlighting code
The simplest use of the filter is to let the Ace editor do syntax colouring of code. This is achieved by putting the code into a HTML <pre> element with the class ace-highlight-code. The default language is python; if you want another language you should add to the pre element an attribute like data-lang="java". Here are two examples, the first in Python, the second in Java.
A python example
print("Some squares") for i in range(10): print(i, i ** 2)
A Java example
public class hello { public static void main(String[] args) { System.out.println("Hello world!"); } }
Note: for strict HTML5 compliance, all non-standard element attributes must be prefixed with 'data-', which is why the language attribute is called 'data-lang' rather than just 'lang'. However, if you don't care about such compliance, you can drop the prefix; the code will still work but will not pass HTML5 validator checks. All code in this test/demo question use the full 'data-' form for attributes.
Options for highlighting
Line numbers are not shown by default, but an attribute data-start-line-number can be added to set the line number to use on the first line. Its default value is none, resulting in no line numbers being displayed. If non-none, the usual value is '1', but other values can be used in special cases, for example if code is broken into two separate highlighted segments.
There is also an optional attribute data-font-size to set the font size within the Ace editor.
The following example shows the Java hello world program with line numbers starting at 5 and a font-size of 16 pts.
public class hello { public static void main(String[] args) { System.out.println("Hello world!"); } }
Making code interactive
If the class of the HTML <pre> element containing the code is instead set to ace-interactive-code, a Try it! button is added below the Ace editor panel. When clicked, the code is sent to the CodeRunner sandbox (usually a Jobe server) for execution and the result is displayed below the button. The code can be edited and re-run an arbitrary number of times.
Here are firstly the same two examples from above, but with class ace-interactive-code, and then C and C++ hello world programs. An extra blank line is being displayed in these cases by moving the terminating '</pre>' onto a new line rather than putting it hard up against the last non-whitespace character in the code. This makes it clear that all the editable code is being displayed, but you might prefer not to do this - your call.
A python example
print("Hello squares") for i in range(10): print(i, i ** 2)
A Java example
public class hello { public static void main(String[] args) { System.out.println("Hello world!"); } }
Hello world in C
#include <stdio.h> int main() { puts("Hello world!"); }
Hello world in C++
#include <iostream> using namespace std; int main() { cout << "Hello world" << endl; }
Providing standard input (the new approved way)
Using stdin in conjunction wiht ace-interactive-code samples is problematic if users are expecting an interactive prompt-read-prompt-read style of standard input. This really isn't possible with this filter because clicking Try it! simply sends a job to the jobe server for execution and then displays the results. However, the author can provide the user with a text area into which they can enter any standard input (in general, multiple lines) which are then read on demand by the program. The data-stdin-taid attribute of the <pre> element specifies the text area (or other HTML element with a val() method) to be used in this way. For example:
Standard input:
while 1: try: print(input()) except EOFError: break
If the author wishes to provide fixed uneditable standard input they can simply hide the textarea.
Providing standard input (the old deprecated way)
If there is no data-stdin-taid attribute, the question author can provide a standard input string using the attribute data-stdin. Since HTML5 allows multiline literal strings, the stdin stream can be multiline too.
The following example shows a Python program that reads standard input until EOF. The stdin attribute here is
Chaos reigns within. Reflect, repent, and reboot. Order shall return.
while 1: try: print(input()) except EOFError: break
Providing files
Files can uploaded into the working directory of the program in two different ways. Both require the author to add additional HTML elements to the page.
- Named strings, or pseudo-files, can be defined by the
data-file-taids
attribute. This is a JSON object mapping filenames (fixed by the question author) to the IDs of textareas on the same page. The user can enter text into the textareas to define the pseudo-file contents prior to clicking Try it! The question author can hide the textareas if they wish to fix the file contents, e.g. as support files for the run. - Actual files can be uploaded by the user via a standard <input type="file"> element by the
data-file-upload-id
attribute, which is the HTML element ID of the <input> element.
If both attributes are defined, files uploaded via the uploader override any identically named pseudo-files.
Pseudo file example: an HTML encoder
Copy the text that you wish to HTML encode into the text area, then click Try it!
from html import escape with open('raw.txt') as infile: raw = infile.read() if len(raw.strip()) == 0: print("NO INPUT SUPPLIED!") else: print(escape(raw))
File upload example: an HTML encoder
Select a single file of type .txt, then click Try it!
import os from html import escape text_files = [filename for filename in os.listdir() if filename.endswith('.txt')] if len(text_files) == 0: print("No '.txt' files found!") elif len(text_files) != 1: print("Please upload only a single .txt file") else: with open(text_files[0]) as infile: raw = infile.read() if len(raw.strip()) == 0: print("EMPTY FILE!") else: print(escape(raw))
The complete set of ace-interactive-code options
The complete set of ace-interactive-code elements is as follows. Attributes in bold have not been introduced above.
A simple data-html-output example
The following example shows how the data-html-output option works. It's only rarely useful in isolation but in conjunction with data-code-mapper and/or data-prefix and data-suffix it can be used by question authors to provide arbitrary output. The following example also changes the button name.
print("<h2>I'm a heading</h2>") print("<ol><li>Bullet 1</li><li>Bullet 2</li></ol>")
A simple use of code-mapper
In this case we define (using a <script> element) a function called doubler that returns its parameter concatenated onto itself. The <pre> element that wraps the following code has the attribute data-code-mapper set to 'doubler'.
print("Hello world!")
A simple use of data-prefix
In the above example showing the use of stdin, the input() function was called and the result printed. But if students are using input() interactively, they expect to see what they enter from the keyboard echoed to the screen. To achieve such an effect in Python, prefix code could be added that simulates this echoing. For example, here's a program that asks the student for their name and age and prints a message telling them their age next year.The data-stdin attribute of the <pre> element has been set to contain the two lines 'jeremy' and '11'.
name = input("What's your name? ") age = int(input("How old are you? ")) print(f"Hi {name}, next year you'll be {age + 1}")
Displaying matplotlib images
By using the data-code-mapper option (and/or data-prefix and data-suffix) in conjunction with data-html-output it is possible, with some difficulty, to define an ace-interactive-code block that will let students run matplotlib code and display both the textual output from the question and any output images. Here is an example, which also shows the use of the dark-theme mode and the use of the data-max-lines attribute (set to 15). Inspect the HTML at your peril!
import matplotlib.pyplot as plt import numpy as np # Generate some text output print("I am a line of output") print("And so am I") # Now the graph plotting axes = plt.axes() xs = np.linspace(0, 720, 200) ys = np.sin(2 * np.pi * xs / 360) axes.plot(xs, ys) axes.set_title("y = sin(x)") axes.set_xlabel('x (degrees)') axes.set_ylabel('y') axes.grid() plt.show()
Using Ace-interactive-filter for applets
The latest version of the ace-interactive-filter plugin allows you to upload files and to read the contents of various textareas or other HTML elements (anything with a jquery val method) as if they were files. You can also hide the actual code. Consequently it's possible to use the Ace inline filter to embed executable code rather like traditional applets, using any language of your choice. [Which of course, should be Python.]
This approach to applets is, in most cases, much weaker than using JavaScript directly. For example you can't do any event handling or manipulate the DOM directly. But it has the big advantages of:
- Language familiarity (for those who are more familiar with Python than JavaScript)
- The power of Python for more advanced tasks, such as processing CSV files, plotting graphs, numerical analysis, etc.
For example:
Plot grade histogram
Upload a .csv file using the file selector below. The csv file must have a single header row and there must be a grade column containing only grades (E, D, C-, C+, ...). Enter the required column name into the input field beside the file selector and click the Plot histogram button.
Bug alert: the filename cannot contain spaces or other non-alphanumeric characters except '-', '_' and '.'.
Select CSV file:
Column name:
import matplotlib.pyplot as plt import os import csv GRADES = ['E', 'D', 'C-', 'C', 'C+', 'B-', 'B', 'B+', 'A-', 'A', 'A+'] def main(): csv_files = [name for name in os.listdir() if name.endswith('.csv')] if len(csv_files) != 1: print(f"I expected to be given exactly one .csv file. Instead I got {len(csv_files)}") return filename = csv_files[0] with open("column_name") as infile: column_name = infile.read().strip() if column_name == '': print("You haven't specified the name of the grades column") return print(f"Processing {filename}") counts = {grade: 0 for grade in GRADES} with open(filename, encoding='utf-8-sig') as infile: rdr = csv.DictReader(infile) for row in rdr: try: grade = row[column_name].strip() except KeyError: print(f"File {filename} doesn't have a column {column_name}") print("Columns: ", list(row.keys())) return if grade in GRADES: counts[grade] += 1 axes = plt.axes() axes.bar(counts.keys(), counts.values()) axes.set_title(f"Grade distribution, column {column_name} from file {filename}") num_grades = sum(counts.values()) print(f"{num_grades} grades processed") print(f"Pass rate = { 100 * (num_grades - counts['D'] - counts['E']) / num_grades : .1f}%") main()