Title: Python Full Demo Quiz
Course: Informatik J1 - Relationale Datenbanken (BPE6) - CODERUNNER

The following question asks the students to write a Python3 class to a given specification. This can be done with any standard Python3 question type in which the student's code is followed by the per-test-case code. This particular example uses our COSC121 question type, which enforces pylint compatibility.

For those unfamiliar with Python classes, a possible solution is hidden in the following white panel - click and drag over it to see the code.

class Car:
    """A class that represents a car with a model, a year
       and a speed, plus accelerate and brake methods to
       alter the speed, plus a honk_horn method.
    """
    def __init__(self, model, year, speed=0):
        """Initialiser"""
        self.model = model
        self.year = year
        self.speed = speed

    def accelerate(self):
        """Speed goes up by 5."""
        self.speed += 5

    def brake(self):
        """Speed goes down by 5"""
        self.speed = max(0, self.speed - 5)

    def honk_horn(self):
        """Print a beep-beep message"""
        print("{} goes 'beep beep'".format(self.model))

Exercise: My first car

Write a class named Car that has the following data attributes:

- model (car's model, e.g. "Toyota")
- year (car's manufacturing year , e.g "2000")
- speed (car's current speed, in km/h)

The Car class should have an __init__ method (i.e., an initialiser) that accepts the car's model, year, and speed as arguments. These values should assigned to the object's model, year, and speed data attributes. If speed isn't specified, then it should automatically be set to 0.

The class should also have the following methods:

- accelerate This method should add 5 to the speed data attribute each time it is called.
- brake This method should subtract 5 from the speed data attribute each time it is called. The speed should not go below 0, so if brake() is called when the speed is already 0, the speed should stay 0.
- honk_horn This method should print {model} goes 'beep beep', where {model} is the object's model.

This question uses a capability of our standard python3_cosc121 question type that allows the question author to specify, via template parameters, Python constructs that must not appear in the submitted code (for in this case) and also constructs that must appear (while in this case).

The question

Rewrite the following code, which uses for loops, so that it uses while loops instead. Your function must not use any for loops and must remain pylint compliant.

def print_names(people):
    """ A function to print a list of people's names. The parameter
        'people' is a list of people, where each person is represented
        as a list of their names.
    """
    for person in people:
        to_print = ""
        for name in person:
            to_print += name + " "
        print(to_print)

Testing GUI programs

Testing programs that involve any form of graphical output is fundamentally difficult in any environment. CodeRunner is no exception. However, we have made a couple of reasonably successful forays into the area.

1. In Matlab we've written question asking students to plot graphs. These are graded either by replacing the graph-plotting functions with our own and checking the data passed to them or by inspecting the graph object generated by the standard built-in plotting functions.
2. In Python3 we've written a mock tkinter (or a very tiny subset of it -- the subset we teach at the end of COSC121) that is able to simulate rudimentary event handling and can then output a textual representation of the state of the tkinter GUI. The output is checked using the regular expression grader rather than the normal exact match grader; we check for major behavioural aspects, such as whether an expected value appears after a particular button is clicked, rather than the precise look and feel.

This question involves the second of those question types.

Because tkinter is such a complex and ill-specified beast, students fairly frequently write programs that appear to work correctly but are marked wrong by CodeRunner's tkinter question type. For example, they might use tkinter features that we do not implement or they might use strange layouts with empty rows and column that collapse to nothing on screen. Mostly, though, students who program using just the methods and techniques that we teach in the course get marked right and we have found tkinter questions useful in both labs and exams. We never apply a resubmission penalty to tkinter questions and in exams we tell students to mark any question that is inexplicably being graded wrong with a flag and we then mark in by hand.

Below is an example of a tkinter question, used in a COSC121 lab. A possible answer is in the following textarea, which you can enlarge if you wish to peek at it.

The question

You are to write a currency conversion application that converts from NZ dollars to one of a number of known currencies.

Your application should look like the following: the image on the left shows the application when it first starts, and the image on the right shows the state after an amount of money has been entered and the currency changed. The application should use a grid with four rows and two columns. The text used in all labels must be exactly as shown, but the precise choice of font sizes, grid padding values and similar layout parameters is over to you.

The label displaying the currency conversion  should display the string '$0.00 NZD is 0.00 Albanian Lek' when the application is first launched and should be updated whenever the NZD amount or the currency changes.

[Image] | [Image]

[Your images may show uneven blue highlighting in the combobox; this is a Tk bug and can be ignored.]

Your application should start with the following skeleton:

'''A GUI-based currency convertor program'''

from tkinter import *
from tkinter.ttk import *

# The format for the label displaying
DISPLAY = "${0:.2f} NZD is {1:.2f} {2}"

class CurrencyConverterGUI(object):

    def __init__(self, window, currencies):
        pass  # Your code goes here


    def convert(self, *rest):
        '''Called when the text entry or the combo box changes.
           Displays the converted currency in the display label.
        '''
        pass  # Your code goes here



def main():
    '''The main function'''
    european_currencies = {"Albanian Lek": 87.09,
                           "British Pound Sterling": 0.49,
                           "Euro":0.62,
                           "Hungarian Forint": 193.26,
                           "Kazakhstani Tenge": 143.74,
                           "Norwegian Krone": 5.09,
                           "Swiss Franc": 0.75,
                           "Turkish Lira": 1.79}
    window = Tk()
    converterGUI = CurrencyConverterGUI(window, european_currencies)
    window.mainloop()

main()

The following illustrates a trivial "write a program" question. This question does not use the pylint style checker, so a standard "script" approach, with global code is acceptable. A simple solution might be:

name = input("What is your name? ")
print("Welcome to CodeRunner, " + name + ".")
print("May the force be with you.")


Write a python3 program that uses the input() function (with a prompt string as shown in the example below) to read a person's name from "standard input" (e.g. the keyboard), and then prints a two-line "Welcome to CodeRunner" message incorporating that name as shown in the example table below. Don't forget to include the periods at the end of the message lines and make sure you have exactly one space after the comma.

This question uses exact matching, so white space matters. If you can't see why your answer is being marked wrong, click the Show differences button.

Enforcing pylint compliance

In our introductory programming course, we usually run student-submitted code through the pylint style checker (see http://www.pylint.org/) before running it. To achieve this we use special extended question types that enforce pylint compliance and various other constraints, such as a limit on how many global constants can be used. Here is the previous question again, but this time requiring a pylint-compliant solution. You'll see that there's now a Precheck button, which gives you an unlimited number of penalty-free submissions to pylint before you click Check to see if your code passes the runtime checks.

A possible solution to the question is hidden in the following white box; click and drag over it to see the hidden text.

'''This solution is pylint compliant'''
def main():
    '''The main function'''
    name = input("What is your name? ")
    print("Welcome to CodeRunner, " + name + ".")
    print("May the force be with you.")

main()

The question

Write a pylint-compliant python3 program that uses the input() function (with a prompt string as shown in the example below) to read a person's name from "standard input" (e.g. the keyboard), and then prints a two-line "Welcome to CodeRunner" message incorporating that name, as shown in the example table below. Don't forget to include the period at the end of the message and make sure you have exactly one space after the comma. [This question uses exact matching, so white space matters.]

A function that prints

A common problem for students learning Python is the distinction between functions that return a value ("true" functions) and functions that print output and (preferably) don't return a value ("procedures"). This is an example of the latter.

Write a Python3 function print_sqrs_1_to_n(n) that takes a single positive integer as a parameter and prints a table of all integers and their squares from 1 up to and including n, formatted as below. The output from your function, when called with the code in the Test column, should be exactly as shown in the Result column.

The penalty regime for this question is 5, 15, 25, ... i.e., there is a 5% penalty for the first wrong submission but the penalty thereafter rises by 10% for each wrong submission.

A more-challenging function

Sequences of numbers in which there are frequent "runs" of a particular number repeating several times can often be more compactly represented by use of what is called "run length encoding". A list is run-length encoded by representing it as a list of pairs (2-tuples), where each pair is a number and the length of the "run" of that number, where the length is 1 if a number occurs once, 2 if it occurs twice in a row, etc. Write a function run_length_encode(nums) that returns the run-length encoded representation of the list of integers, nums.

And now a more-significant example of a write-a-program question. This one is not pylint checked.

Normally in assignment questions of this sort we would show the students only the first failing test case, but in this tutorial introduction all are shown.

Shapes

Welcome to Skool-z-Kool, a non-profit organisation that intends to develop simple Python programs for use by school children. The programs will have a dual role: they will be used to teach the younger children simple educational concepts, but will also be used as example programs for older children learning to program in Python.

As an enthusiastic volunteer, you have been invited to write a program to introduce young children to some different 2D geometric shapes. Your mentor has suggested that as a pilot study, you write a program dealing with just two shapes: squares and triangles. Both shapes will be drawn on the terminal as a pattern of asterisks (ie, *'s). The program first asks the user what shape is to be drawn, then asks whether the shape should be drawn filled or unfilled, and finally asks for the size. The required shape is then drawn and the program exits.

Input

The program reads three responses from the user (i.e. "standard input") as follows:

1. The shape to draw is requested from the user with the prompt "Enter shape to draw: ". Note that the prompt has a space after the colon.

The only two acceptable inputs, which must be treated case-insensitively, are "square" and "triangle". Any other response results in the error message "Invalid input. Try again." and the prompt for input is repeated. This continues until a valid response is received.

2. The program then prompts the user with "Filled or unfilled? " (Note the space after the question mark).

The user must enter (case-insensitively) either "filled" or "unfilled"; anything else results in the error message "Invalid input. Try again." and the prompt for input is repeated, continuing until valid input is received.

3. The program lastly prompts the user for "Size: " (with a space after the colon).

A response in the range 1 to 100 inclusive is expected. Anything else gives the error message "Invalid input. Try again." and repeats until a valid input is received.

Output details

Apart from the prompts and error messages as above, the output from the program is either

1. a square of asterisks of the given size, unfilled or filled as in the examples below, or
2. a right-angled triangle with height and base-width of the given size and a vertical left-hand edge, unfilled or filled, as shown below.

The output shape must be preceded by one extra blank line, following the final size prompt line.

For example, a typical run of the program might be:

Enter shape to draw: sqare
Invalid input. Try again.
Enter shape to draw: square
Filled or unfilled? unfilled
Size: 4

****
*  *
*  *
****


Note that in the example output above, the input that is typed by the interactive user is shown in underlined italics. In the examples and test output it is shown in plain text.

The penalty regime for this question is 10%, 20%, ..., i.e. there is a cumulative 10% penalty for each wrong failing submission, including the first.

Design hint

You are strongly encouraged to write separate functions for reading the input (possibly multiple such functions), for displaying a square and for displaying a triangle. Your main function can then call those functions.

Sample Output

This is a variant of the Skool-z-Kool (a.k.a. "Shapes") program in which the students are provided with a skeleton version of the code. The behaviour of the program as a whole is tested, and also the behaviour of the functions specified in the skeleton (albeit somewhat superficially). pylint checking is also turned on.

In this question, students are shown only those test cases in the example table plus just the first failing test case, in order to reduce the amount of output displayed for wrong answers.

The advantage of this all-in-one approach is that students get a clearer view of the program as a whole, right from the start. The disadvantage is the increased complexity of the result table presented to the student. Also, students will not get any marks until they have the whole program working (although the question author could turn off "all-or-nothing" if they chose). If the question is broken into three separate questions that test the support functions followed by a question testing the main function, students will earn marks at each step they get working.

Shapes

Welcome to Skool-z-Kool, a non-profit organisation that intends to develop simple Python programs for use by school children. The programs will have a dual role: they will be used to teach the younger children simple educational concepts, but will also be used as example programs for older children learning to program in Python.

As an enthusiastic volunteer, you have been invited to write a program to introduce young children to some different 2D geometric shapes. Your mentor has suggested that as a pilot study, you write a program dealing with just two shapes: squares and triangles. Both shapes will be drawn on the terminal as a pattern of asterisks (ie, *'s). The program first asks the user what shape is to be drawn, then asks whether the shape should be drawn filled or unfilled, and finally asks for the size. The required shape is then drawn and the program exits.

Input

The program reads three responses from the user (i.e. "standard input") as follows:

1. The shape to draw is requested from the user with the prompt "Enter shape to draw: ". Note that the prompt has a space after the colon.

The only two acceptable inputs, which must be treated case-insensitively, are "square" and "triangle". Any other response results in the error message "Invalid input. Try again." and the prompt for input is repeated. This continues until a valid response is received.

2. The program then prompts the user with "Filled or unfilled? " (Note the space after the question mark).

The user must enter (case-insensitively) either "filled" or "unfilled"; anything else results in the error message "Invalid input. Try again." and the prompt for input is repeated, continuing until valid input is received.

3. The program lastly prompts the user for "Size: " (with a space after the colon).

A response in the range 1 to 100 inclusive is expected. Anything else gives the error message "Invalid input. Try again." and repeats until a valid input is received.

Output details

Apart from the prompts and error messages as above, the output from the program is either

1. a square of asterisks of the given size, unfilled or filled as in the examples below, or
2. a right-angled triangle with height and base-width of the given size and a vertical left-hand edge, unfilled or filled, as shown below.

The output shape must be preceded by one extra blank line, following the final size prompt line.

For example, a typical run of the program might be:

Enter shape to draw: sqare
Invalid input. Try again.
Enter shape to draw: square
Filled or unfilled? unfilled
Size: 4

****
*  *
*  *
****


Note that in the example output above, the input that is typed by the interactive user is shown in underlined italics. In the examples and test output it is shown in plain text.

The penalty regime for this question is 10%, 20%, ..., i.e. there is a cumulative 10% penalty for each wrong failing submission, including the first.

Program Design

Your program must be of the following form.

"""A solution to the "shapes" question.
   Author: your-name-goes-here
   Date: current-date-goes=here"""

# **** OTHER SUPPORT FUNCTIONS GO HERE IF YOU WISH ****

def get_word(prompt, valid_responses):
    """Read text from the user, using the given prompt.
       The response must be in valid_responses; if not,
       an 'Invalid input. Try again.' message is issued and the
       user is re-prompted.
    """
   # *** YOUR CODE GOES HERE ***


def draw_square(size, filling):
    """ Draw a square, filled or unfilled according to whether filling has
        the value "filled" or not, of the given size.
    """
   # *** YOUR CODE GOES HERE ***


def draw_triangle(size, filling):
    """ Draw a triangle, filled or unfilled according to whether filling has
        the value "filled" or not, of the given size.
    """
    # *** YOUR CODE GOES HERE ***


def main():
    """Ask the user for a shape, its size and whether it's
       filled. Draw the shape.
    """
    # *** YOUR CODE GOES HERE ***


main()

Sample Output

Write a sqr(n) function

Write a Python3 function sqr(n) that returns the square of its numeric parameter n.

For those who don't know Python, one possible answer is:

def sqr(n):
    return n * n

Copy that answer into the answer box and click Check to see how CodeRunner questions behave. Then try changing the answer in various ways and re-checking.

Note that CodeRunner questions are expected to be run in a quiz using Moodle's adaptive behaviour, in which students can check each question as they submit it. A flexible penalty mechanism allows the question author to specify varying re-submission penalties. In this question the first wrong submission is free, but thereafter the penalties are 10%, 20%, 30%, ...

A variant on sqr(n)

The standard write-a-function Python3 question type executes the student's code first followed by the code shown in the Test column of the results table. This makes it easy for students to test their functions prior to submission (assuming they're sitting at a computer with their usual programming tools available): they just copy the test code after their program and run the program.

An alternative, favoured by some question authors, is to print the result of the function call, showing the student only the actual call as though they had typed it into the Python shell. This question presents the results that way, using a customised template. The question is otherwise identical to the previous one, viz:

Write a Python3 function sqr(n) that returns the square of its numeric parameter n.

As before, a possible answer is:

def sqr(n):
    return n * n

Write-a-program questions

Write-a-function questions are convenient because we can specify the input data and the output result without the complexities of having to specify the formats of input and output files. All the same, for larger tasks like assignments, "write a program" questions are the norm. Typically these ask students to read from standard input and write to standard output. Learner programmers think of these as keyboard and screen but on a quiz server data is of course taken from files. For testing such programs, the question author supplies standard input and specifies the expected output just as for write-a-function programs. The question author can also attach support files to the question; these are loaded into the execution environment at run time and can be used for input data or additional code, libraries etc, depending on the type of question.

A problem with running program that take their standard input from a file rather than the keyboard is that the input data does not get echoed to the screen. This is confusing for learner programmers. Our standard Python program question type replaces the usual Python input() function with one that does echo its input to standard output, so that the student sees the same output as when they run from the keyboard.

Welcome to the CodeRunner Demo Quiz

This quiz is a tutorial introduction to CodeRunner questions from simple write-a-function or write-a-program questions through assignment level questions into advanced customised questions to deal with special situations like GUI programming.

Python3 is used throughout this quiz. If you're not a Python3 programmer you can still look through the range of questions that can be asked. With few exceptions, similar questions can be asked in all supported languages (Python3, Python2, Java, C, C++, PHP, JavaScript, Octave, Pascal) though you might have to write your own prototypes for many of those.

You can do this quiz as many times as you like. When you submit the whole quiz at the end, you can review your answers and see the author's sample solutions. This is a standard Moodle feature; the quiz administrator can choose whether or not to display so-called "right answers", and various other information at various phases during the quiz.

We start with write-a-function questions, for which the student must submit a function satisfying the given specs. Since the student's code is run before the test code is run, the student's submission can always include additional support functions if necessary, so the specified function can be almost arbitrarily demanding, such "write a function that compiles a given source file in language X".

All the questions in this quiz are included in the CodeRunner repository at https://github.com/trampgeek/CodeRunner.

[Note: this quiz is work-in-progress. If new questions get added, all existing submissions will have to be deleted, so you won't necessarily be able to return to the site at a later date and see your answers. If you want to keep them, copy them to your own machine.]

Other question types

The above questions have asked students to write a function (possibly with additional support functions) or to write a whole program. The remaining questions show a few other forms of question. Essentially you can ask any question you like subject to two requirements:

1. The student submission is a single piece of text (usually, but not necessarily, program code)
2. You, the question author, are able to write a program that can assess the correctness of that submission.

A note on templates

The preceding Skool-z-Kool question used the basic Python3 question type, but with the built-in input function replaced by one that echoes the characters read from standard input. This is achieved by customising the question's template in the question-authoring interface. The template is a way to specify how the student's submission, the per-test-case code and any other testing code are combined to define the program to be run in the sandbox. For the above question, the template was just

__saved_input__ = input
def input(prompt=""):
    result = __saved_input__(prompt)
    print(result)
    return result

{{STUDENT_ANSWER}}

{{TEST.testcode}}


The effect is just to re-define the input function, run the student code and then run the test code.

If you find yourself using such a template repeatedly, you would normally make it into a new question type, such as python3_with_input, by defining the question as a prototype. See the CodeRunner documentation for details.

Improving Skool-z-Kool

A problem with the preceding version of Skool-z-Kool is that it while it suggests that students should write the code with support functions, they are not forced to do so. Beginner programmers often need more encouragement than that!

The first improvement we can make to the question is to use pylint to check the submission before running it, as was done with the second version of print_name. In our introductory programming course we turn on pylint checking almost from the start. Our python_cosc121 question type, used for most questions in the course, allows us to configure pylint differently for each question by using template parameters. In a question like Skool-z-Kool we would typically limit the length of any one function to around 10 - 20 lines, which pretty much forces students to use functions.

Particularly earlier on in a course we might wish to constrain the students even further, so that they are forced to use a particularly functional decomposition. That of course requires that we specify the support functions they must use and also that we must test those functions separately. A common way of achieving that is to precede the main program test question with a set of write-a-function questions, one for each support function we wish them to write. An alternative approach is that taken by the following variant of Skool-z-Kool, which presents the students with a skeleton of the program. They then have to fill in all the blanks to get the program working. Note that in our teaching environments students always have a full Python development environment available to them while doing CodeRunner questions, even in tests and exams. They are expected to develop and test their code before submitting it to the quiz server.