Randomized Questions

To add randomization to your questions, you will need to modify both question.html and server.py.

question.html

PrairieLearn utilizes Mustache in question.html files to indicate which text should be randomized. in general, any text enclosed by double curly braces {{ ... }} is replaced by text you have defined in server.py. You can find the official Mustache documentation here.

Setting a static parameter

Anywhere in question.html file, add {{params.x}} where x is the name of the parameter. You may have multiple parameters with the same name, or the same parameter multiple times. Parameters can be within the question description, code blocks, answer options, or HTML attributes. To add a full HTML tag that will render properly, use triple curly braces {{{params.x}}} instead.

Setting a conditional parameter

Use the Mustache tags {{#params.condition}} and {{/params.condition}} where condition is the name of the parameter. The text between these tags will only appear if the condition parameter is True as defined in server.py.

Setting a list of formatted parameters

Use the Mustache tags {{#params.list}} and {{/params.list}} where list is the name of the parameter. The text between these tags should contain the format of the HTML that you would like to be repeated for each element in the list. The randomized parameters within {{#params.list}} and {{/params.list}} should use the format {{x}} where x is the name of the parameter (note you do not include params. before the name of the parameter).

server.py

server.py should have this general format:

import random

def generate(data):
    # Generate random values first
    # Then set data["params"] to their randomized value

Generate random values

Random integer

Use x = random.randint(i, j) to generate a random integer i <= x <= j.

Random floating-point number

Use x = random.random() to generate a random float 0 <= x < 1. You can use arithmetic to modify this value to change the range.

Random element from list

Use x = random.choice(list) to set x to a random element from list. For example, random.choice(["A", "B", "C"]) will return either "A", "B", or "C".

Random set of elements from list

Use x = random.sample(list, k) to set x to a list of k random elements from list. For example, random.choice(range(1, 11), 4) will return a list of 4 random unique integers between 1 and 10, inclusive.

Set data[“params”]

Set data["params"]["x"] to the random value of the parameter, where x is the name of the parameter you defined in question.html. Note that if you are setting an attribute, provide it as a string (e.g. "true" instead of True). If the parameter is a list, then it should be assigned to a list of dictionaries where each dictionary contains keys as defined by the Mustache tags without the params., and the randomized values you assigned.

Example

The question lengthOfCityString gives the student a string containing the name of a randomly selected city, and they are asked to input the length of that string.

Here is question.html for this question:

<pl-question-panel>
    <markdown>Consider the following code:</markdown>
    <pl-code language="java">String city = "{{params.city}}";</pl-code>
    <markdown>What is `city.length()`?</markdown>
</pl-question-panel>

<pl-integer-input answers-name="ans" placeholder="Type answer here"></pl-integer-input>

You can see that the city parameter will be randomly generated and inserted into the code block. The randomly generated answer does not need to be included as a Mustache parameter in question.html since pl-integer-input answers are defined in server.py.

Here is server.py for this question:

import random

def generate(data):
    cities = ["Tokyo", "New York", "London", "Paris", "Shanghai", "Dubai", "Sydney", "Rome", "Berlin", "Moscow", "Beijing", "Singapore", "Hong Kong", "Seoul", "Madrid", "Toronto", "Mexico City", "Cairo", "Buenos Aires", "Istanbul", "Delhi", "Mumbai", "Jakarta", "Bangkok", "Manila", "Lagos", "Kinshasa", "Dhaka", "Karachi", "Lima", "Bogota", "Santiago", "Kuala Lumpur", "Riyadh", "Tehran", "Baghdad", "Lahore", "Chongqing", "Chennai", "Bengaluru", "Hyderabad", "Osaka", "Nagoya", "Ahmedabad", "Shenzhen", "Guangzhou", "Tianjin", "Chengdu", "Wuhan", "Nanjing", "Hangzhou", "Harbin", "Dalian", "Sapporo", "Busan", "Pyongyang", "Addis Ababa", "Nairobi", "Cape Town", "Alexandria", "Casablanca", "Algiers", "Accra", "Abidjan", "Dakar", "Luanda", "Douala", "Khartoum", "Tunis", "Tripoli", "Damascus", "Amman", "Beirut", "Jerusalem", "Tel Aviv", "Ankara", "Izmir", "Kyiv", "Warsaw", "Prague", "Budapest", "Bucharest", "Vienna", "Lisbon", "Dublin", "Brussels", "Amsterdam", "Copenhagen", "Stockholm", "Oslo", "Helsinki", "Zurich", "Geneva", "Barcelona", "Milan", "Munich", "Hamburg"]
    city = random.choice(cities)
    data["params"]["city"] = city
    data["correct_answers"]["ans"] = len(city)

First, I selected one random city from the list of cities. Then, I set data["params"]["city"] to this value, and set data["correct_answers"]["ans"] to the length of the string, dynamically setting the correct answer. For other types of questions, you may have to edit data["params"] instead of data["correct_answers"].

Here is what the question looks like (each student will see a random city):

Image