← Teacher guide

A quiz app

Students build a quiz app from a list of question dictionaries, make a Radio input for each question with a loop, mark the answers with feedback, then add a question, a perfect-score message and handling for blanks, and make and user-test a quiz of their own.

Apps with Gradio · A quiz app · about 50 minutes · Open the student lesson

Classroom resources

Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.

Students will be able to

Timing

Predict and Run 10 min · Investigate 10 min · Modify 15 min · Make 15 min

Before class

Read the unit primer first. Each question is a dictionary (q["q"], q["choices"], q["answer"]), introduced here only as far as reading one. The inputs list starts empty and a loop appends a gr.Radio per question; mark takes one parameter per question (a1, a2, a3) and puts them in a list to compare by position. A Radio gives the chosen text, or None when nothing is chosen (the third Modify task). The bug task returns inside the loop, so only the first question is ever marked. The info question points at *answers (any number of inputs as a list) as a stretch. The Make ends with user testing: a classmate takes the quiz.

Running the lesson

  1. Predict: trace i through the loop on the board with the two answers.
  2. Run: change a question in the list and run again; the app follows the data.
  3. Investigate: count the inputs, the questions and the parameters: three of each.
  4. Make: pairs swap quizzes and report one confusing thing.

The hard part (on purpose)

Marking by position: answers[i] against questions[i]. Ask students to trace i for two questions on paper.

When students get stuck

What you will seeA question to askThe fix
The score is never more than 1.Is return inside the loop?Move return to after the loop (no indent under for).
TypeError: mark() takes 3 positional arguments but 4 were given.How many questions are there now, and how many parameters?Add a parameter for the new question, and add it to answers.
A right answer is marked wrong.Does the answer exactly match one of the choices, capitals and spaces included?Copy the answer text from the choices list.

Answers to the checks

  1. Predict: What does it print?
    1. The first answer, "8", matches; the second, "54", does not match "56".
  2. Inputs from data: Where do the three Radio inputs come from?
    The loop over questions appends one Radio for each question
    inputs starts empty, and the loop appends a gr.Radio with that question's choices and text. Four questions would make four inputs.
  3. What an answer is: What does mark get as a1 when the first question is answered Mercury?
    "Mercury"
    A Radio gives the chosen text. mark compares it with q["answer"].
  4. Spot the bug: The score is never more than 1, even with every answer right. Click the line that is wrong.
    line 16
    return is indented inside the loop, so the function ends after the first question. Return after the loop, not inside it.
  5. Where this leads: Your teacher wants a quiz with 30 questions. What is the main problem with mark(a1, a2, ..., a30)?
    A parameter per question is a lot to write; Python's *answers takes any number of them as a list
    def mark(*answers): collects every input into one list, so the function works for any number of questions. It is the stretch for this lesson.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › A fourth question

import gradio as gr

questions = [
    {"q": "Which planet is closest to the Sun?", "choices": ["Venus", "Mercury", "Mars"], "answer": "Mercury"},
    {"q": "How many legs does a spider have?", "choices": ["6", "8", "10"], "answer": "8"},
    {"q": "What is 7 x 8?", "choices": ["54", "56", "64"], "answer": "56"},
    {"q": "Which gas do plants take in?", "choices": ["Oxygen", "Carbon dioxide", "Helium"], "answer": "Carbon dioxide"},
]


def mark(a1, a2, a3, a4):
    answers = [a1, a2, a3, a4]
    score = 0
    feedback = ""
    for i in range(len(questions)):
        q = questions[i]
        if answers[i] == q["answer"]:
            score = score + 1
            feedback = feedback + "Right: " + q["q"] + "\n"
        else:
            feedback = feedback + "Not quite: " + q["q"] + " The answer is " + q["answer"] + ".\n"
    return str(score) + " out of " + str(len(questions)), feedback


inputs = []
for q in questions:
    inputs.append(gr.Radio(q["choices"], label=q["q"]))

gr.Interface(
    fn=mark,
    inputs=inputs,
    outputs=[gr.Textbox(label="Score"), gr.Textbox(label="Feedback")],
    title="Quick quiz",
).launch()

Modify › Perfect score

import gradio as gr

questions = [
    {"q": "Which planet is closest to the Sun?", "choices": ["Venus", "Mercury", "Mars"], "answer": "Mercury"},
    {"q": "How many legs does a spider have?", "choices": ["6", "8", "10"], "answer": "8"},
    {"q": "What is 7 x 8?", "choices": ["54", "56", "64"], "answer": "56"},
]


def mark(a1, a2, a3):
    answers = [a1, a2, a3]
    score = 0
    feedback = ""
    for i in range(len(questions)):
        q = questions[i]
        if answers[i] == q["answer"]:
            score = score + 1
            feedback = feedback + "Right: " + q["q"] + "\n"
        else:
            feedback = feedback + "Not quite: " + q["q"] + " The answer is " + q["answer"] + ".\n"
    if score == len(questions):
        feedback = feedback + "Perfect score!"
    elif score == 0:
        feedback = feedback + "Have another go."
    return str(score) + " out of " + str(len(questions)), feedback


inputs = []
for q in questions:
    inputs.append(gr.Radio(q["choices"], label=q["q"]))

gr.Interface(
    fn=mark,
    inputs=inputs,
    outputs=[gr.Textbox(label="Score"), gr.Textbox(label="Feedback")],
    title="Quick quiz",
).launch()

Modify › No answer

import gradio as gr

questions = [
    {"q": "Which planet is closest to the Sun?", "choices": ["Venus", "Mercury", "Mars"], "answer": "Mercury"},
    {"q": "How many legs does a spider have?", "choices": ["6", "8", "10"], "answer": "8"},
    {"q": "What is 7 x 8?", "choices": ["54", "56", "64"], "answer": "56"},
]


def mark(a1, a2, a3):
    answers = [a1, a2, a3]
    score = 0
    feedback = ""
    for i in range(len(questions)):
        q = questions[i]
        if answers[i] is None:
            feedback = feedback + "No answer: " + q["q"] + "\n"
        elif answers[i] == q["answer"]:
            score = score + 1
            feedback = feedback + "Right: " + q["q"] + "\n"
        else:
            feedback = feedback + "Not quite: " + q["q"] + " The answer is " + q["answer"] + ".\n"
    return str(score) + " out of " + str(len(questions)), feedback


inputs = []
for q in questions:
    inputs.append(gr.Radio(q["choices"], label=q["q"]))

gr.Interface(
    fn=mark,
    inputs=inputs,
    outputs=[gr.Textbox(label="Score"), gr.Textbox(label="Feedback")],
    title="Quick quiz",
).launch()

your own quiz › Your quiz

import gradio as gr

questions = [
    {"q": "Which instrument has 88 keys?", "choices": ["Piano", "Organ", "Accordion"], "answer": "Piano"},
    {"q": "How many strings does a violin have?", "choices": ["4", "5", "6"], "answer": "4"},
    {"q": "Which family is the trumpet in?", "choices": ["Woodwind", "Brass", "Percussion"], "answer": "Brass"},
    {"q": "What does forte mean?", "choices": ["Loud", "Soft", "Fast"], "answer": "Loud"},
]


def mark(a1, a2, a3, a4):
    answers = [a1, a2, a3, a4]
    score = 0
    feedback = ""
    for i in range(len(questions)):
        q = questions[i]
        if answers[i] == q["answer"]:
            score = score + 1
        else:
            feedback = feedback + q["q"] + " The answer is " + q["answer"] + ".\n"
    return str(score) + " out of " + str(len(questions)), feedback


inputs = []
for q in questions:
    inputs.append(gr.Radio(q["choices"], label=q["q"]))

gr.Interface(fn=mark, inputs=inputs, outputs=[gr.Textbox(label="Score"), gr.Textbox(label="To learn")], title="Music quiz").launch()

your own quiz › Test it with someone

Open task: any program that does what the step asks.

Stretch and support

Stretch: def mark(*answers): to accept any number of questions, or shuffle each question's choices (import random; random.shuffle).

Support: Give students a fourth question dictionary written out, and ask them only to change the parameters to match.

Exit ticket

Why keep the questions in a list instead of writing each Radio by hand?

One loop makes the inputs and one loop marks them, so adding a question means adding data, not new code.

What each step is for

Where it leads

The optional next lesson stores each question as an object with a check method. Machine learning with Gradio (coming later) puts a trained model behind the same kind of app.