Year 9 Digital Technologies · Python project

Mario's Pizzeria Ordering Kiosk

One school brief, delivered the way a real software team would ship it: a requirements spec, a RACI, tested Python, a turtle that draws your pizza, and this website.

Shelly the turtle in a chef hat holding a pizza
28requirements traced (22 functional, 6 quality)
15test cases, all passing (incl. a security test)
$10–$34cheapest to dearest order (v1.0–v1.2)
3versions — v1.0 submission, v1.1 enhanced, v1.2 animated
The brief

What Mario asked for

Mario's Pizzeria needs an automated ordering kiosk. It asks the customer's name, whether they want a Small ($10) or Large ($15) pizza, then three dietary questions: Gluten-Free base (+$3), Dairy-Free cheese (+$2) and Vegetarian toppings (no charge, different toppings). It finishes with a personalised summary and the total.

Fast finishers add a Garlic Bread side (+$4) and a delivery fee (+$5). We built both, plus input checking so typing "medium" asks again instead of crashing.

The one test that matters: Sarah orders a small pizza with a gluten-free base, dairy-free cheese and vegetarian toppings. The brief says the bill must be $15. 10 + 3 + 2 = 15. ✅
====================================
       MARIO'S PIZZERIA KIOSK
====================================
What is your name?
Sarah
Would you like a Small ($10) or
Large ($15) pizza?
small
 --- DIETARY REQUIREMENTS ---
Do you need a Gluten-Free base? (yes/no)
yes
Added $3 for Gluten-Free base.
Do you need Dairy-Free cheese? (yes/no)
yes
Added $2 for Dairy-Free cheese.
Would you like Vegetarian toppings? (yes/no)
yes
Vegetarian toppings selected
(Mushrooms, Peppers, Olives).
 ------------------------------------
Thank you, Sarah!
Your total for your custom pizza
comes to: $15
====================================
How it was built

Six steps, in the order real teams do them

The numbers mean something here: each step depends on the one before it. You can't test code you haven't written, and you shouldn't write code you haven't agreed the rules for.

  1. Read the brief

    Pulled every price and every question out of Mario's brief and wrote them down.

  2. Write the BRS

    Turned the brief into 13 numbered requirements with acceptance criteria and priorities.

  3. Agree the RACI

    Decided who is Responsible, Accountable, Consulted and Informed for each task.

  4. Code it

    pizza_bot.py for the kiosk, pizza_turtle.py so Shelly can draw the order.

  5. Test it

    Fifteen test cases from $10 to $34, including typing rubbish — and hostile code — to see if it breaks.

  6. Ship it

    This site and the browser kiosk, hosted as static files on pizza.locbee.ai.

How we worked

MoSCoW, and the Three Amigos

Two practices borrowed from real teams. Both changed what got built, which is the only reason they are worth naming.

MoSCoW

Every requirement got one of four labels before any code was written. Must means the brief fails without it. Should means it was offered as an extension. Could means we added it because it makes the project better, not because anyone asked. And Won't — the one people forget — means agreed as out of scope for now.

Writing down what you are deliberately not doing is what stops it quietly reappearing later. If time had run short, the order to abandon work in was already agreed, in writing, by the person who marks it.

The Three Amigos

Before a requirement was accepted it was looked at from three directions at once — not three job titles, but three questions:

Business — Mario, who owns the brief: what problem is this solving, and how will we know it's solved? That question produced the $15 worked example everything is measured against.

Development — the student, who has to build it: is this buildable with plain if/else, and can the person writing it explain every line?

Testing — the family, whose job is to break it: what happens when someone types something silly? Nobody asked for input validation. It exists because the testing voice was in the room before the code was written.

Ask the testing question afterwards and you get a bug report. Ask it first and you get a requirement — FR-10.

RACI matrix

Who does what

R = does the work · A = signs it off (exactly one per row) · C = asked for input · I = told when it's done. The student is A/R for everything that gets marked.

TaskStudent DeveloperSponsor (Dad)Mario (Teacher)AI Pair-ProgrammerQA (Family)
Read and understand the briefRCACI
Write the Business Requirements SpecCRARI
Define the RACIIA/RICI
Design the ordering flowA/RCCCI
Code pizza_bot.py (core)A/RCICI
Code the extensionsA/RIICI
Code pizza_turtle.py (Shelly)RCIRI
Test against the $15 exampleRCICA/R
Build the website and web kioskCAIRI
Host on pizza.locbee.aiIA/RICI
Present and submitA/RCIII
v1.1 — Build enhanced.htmlCAIRC
v1.1 — Code pizza_bot_enhanced.pyCAIRI
v1.1 — BRS, RTM, Test Summary, Commissioning docsIAIRC
v1.1 — Run pizza_turtle.py on a real screen (TC-6)RAIIC
Go-live commissioning and production sign-offCA/RCIC
ResponsibleAccountableConsultedInformed
Business requirements

Thirteen things it has to do, nine it may, and six ways it has to behave

From the Business Requirements Specification. Must comes straight from the brief, Should are the extensions, Could are our extras. Optional rows are the v1.1 enhancements in enhanced.html and the v1.2 animated build in deluxe.html — never part of the school submission. FR-01 to FR-13 come from Mario's brief; FR-14 to FR-22 were asked for by the project sponsor after v1.0 was accepted, which is why they are tracked separately. The Quality rows are different in kind: NFR-01 to NFR-06 are non-functional requirements, meaning they do not add a feature but say how the whole thing has to behave — readable, portable, forgiving. They have no MoSCoW priority because you cannot ship half of them. All twenty-eight rows below are traced in the Requirements Traceability Matrix.

IDRequirementAcceptance criteriaPriority
FR-01Friendly welcome headerHeader lines print firstMust
FR-02Ask for and store the customer's nameName appears in the summaryMust
FR-03Small ($10) or Large ($15)"small" → 10, else 15Must
FR-04Gluten-Free base question"yes" adds $3 and prints the messageMust
FR-05Dairy-Free cheese question"yes" adds $2 and prints the messageMust
FR-06Vegetarian toppings questionVeg list or Classic Meat SupremeMust
FR-07Personalised order summarySarah example totals $15Must
FR-08Garlic Bread side"yes" adds $4Should
FR-09Delivery or pickup"delivery" adds $5Should
FR-10Re-ask on invalid answers"medium" re-prompts, no crashShould
FR-11Save the order to last_order.jsonFile contains the whole orderCould
FR-12Shelly draws the pizza with turtleSize, crust, cheese and toppings matchCould
FR-13Browser kiosk with identical pricingSame inputs, same total as PythonCould
FR-14Shelly reacts to every change; toppings animate inVisible reaction per click; reduced-motion respectedOptional
FR-15Email the receiptOpens mail app with receipt filled inOptional
FR-16Text the receiptOpens messages app with receipt filled inOptional
FR-17Order logBrowser log + Download orders.log; Python appends orders.logOptional
FR-18Supreme topping set+$3Optional
FR-19Chicken topping set+$3Optional
FR-20BBQ sauce+$1; sauce drawn dark brownOptional
FR-21Extra Crispy crust+$1; crust darkens with char spotsOptional
FR-22Deluxe animated kiosk — sponsor asked for the site to be brought to lifeSix-stage animated pizza build, animated Shelly, receipt printer; pricing unchanged from v1.1; reduced-motion respectedOptional
NFR-01Readable codePlain if/else and descriptive names; the student can explain every lineQuality
NFR-02PortablePython 3.10+ and nothing to install — turtle and json ship with PythonQuality
NFR-03Brief's question orderQuestions asked in exactly the order Mario's brief lists themQuality
NFR-04Forgiving inputCapitals and stray spaces in answers are accepted, not rejectedQuality
NFR-05Static hostingHTML/CSS/JS only, so it serves from Cloudflare Pages with no server codeQuality
NFR-06Security postureWhat a customer types is treated as text, never code; security headers shipped; no secrets in the filesQuality
The code

Three Python files — two of them below

pizza_bot.py is the school submission: plain if/else, one variable called total_cost, questions in the brief's order. pizza_turtle.py reads the saved order and draws it. Run them with python pizza_bot.py then python pizza_turtle.py. There is a third, pizza_bot_enhanced.py, which adds the v1.1 menu and the order log; it is in the downloads rather than here, because the two above are the ones being marked.

"""
pizza_bot.py  -  Mario's Pizzeria Automated Ordering Kiosk
===========================================================
Asks the customer a few questions, works out the bill, and prints a summary.

Steps (in the order the brief asks for them):
  1. Welcome message
  2. Customer name + pizza size (Small $10 / Large $15)
  3. Dietary checks: Gluten-Free base (+$3), Dairy-Free cheese (+$2), Vegetarian toppings
  4. Extensions: Garlic Bread (+$4), Delivery (+$5)
  5. Final order summary

The finished order is also saved to last_order.json so pizza_turtle.py
can draw it.  (Shelly the turtle is waiting!)
"""

import json


# ---------- helper functions ----------

def ask_yes_no(question):
    """Keep asking until the customer types yes or no. Returns True for yes."""
    while True:
        answer = input(question + " (yes/no)\n").strip().lower()
        if answer in ("yes", "y"):
            return True
        if answer in ("no", "n"):
            return False
        print("Please type yes or no.")


def ask_choice(question, options):
    """Keep asking until the answer is one of the options (e.g. small/large)."""
    while True:
        answer = input(question + "\n").strip().lower()
        if answer in options:
            return answer
        print("Please type one of: " + " or ".join(options))


# ---------- 1. Welcome message ----------

print("====================================")
print("       MARIO'S PIZZERIA KIOSK       ")
print("====================================")

# ---------- 2. Customer details & base pizza ----------

name = input("What is your name?\n").strip().title()

size = ask_choice("Would you like a Small ($10) or Large ($15) pizza?", ["small", "large"])

# Base price setup (if/else)
if size == "small":
    total_cost = 10
else:
    total_cost = 15

# ---------- 3. Dietary & allergy checks ----------

print(" --- DIETARY REQUIREMENTS --- ")

gluten_free = ask_yes_no("Do you need a Gluten-Free base?")
if gluten_free:
    total_cost = total_cost + 3
    print("Added $3 for Gluten-Free base.")

dairy_free = ask_yes_no("Do you need Dairy-Free cheese?")
if dairy_free:
    total_cost = total_cost + 2
    print("Added $2 for Dairy-Free cheese.")

vegetarian = ask_yes_no("Would you like Vegetarian toppings?")
if vegetarian:
    toppings = "Vegetarian toppings (Mushrooms, Peppers, Olives)"
    print("Vegetarian toppings selected (Mushrooms, Peppers, Olives).")
else:
    toppings = "Classic Meat Supreme toppings"
    print("Classic Meat Supreme toppings selected.")

# ---------- 4. Extensions (fast finishers) ----------

print(" --- EXTRAS --- ")

garlic_bread = ask_yes_no("Would you like a side of Garlic Bread ($4)?")
if garlic_bread:
    total_cost = total_cost + 4
    print("Added $4 for Garlic Bread.")

delivery = ask_choice("Is this for delivery or pickup?", ["delivery", "pickup"]) == "delivery"
if delivery:
    total_cost = total_cost + 5
    print("Added $5 delivery fee.")

# ---------- 5. Final order summary ----------

print(" ------------------------------------ ")
print("Thank you, " + name + "!")
print("Pizza size: " + size.title())

dietary_notes = []
if gluten_free:
    dietary_notes.append("Gluten-Free base")
if dairy_free:
    dietary_notes.append("Dairy-Free cheese")
if dietary_notes:
    print("Dietary notes: " + ", ".join(dietary_notes))
else:
    print("Dietary notes: none")

print("Toppings: " + toppings)
if garlic_bread:
    print("Side: Garlic Bread")
print("Delivery or pickup: " + ("Delivery" if delivery else "Pickup"))
print("Your total for your custom pizza comes to: $" + str(total_cost))
print("====================================")

# ---------- save the order for Shelly the turtle ----------

order = {
    "name": name,
    "size": size,
    "gluten_free": gluten_free,
    "dairy_free": dairy_free,
    "vegetarian": vegetarian,
    "garlic_bread": garlic_bread,
    "delivery": delivery,
    "total_cost": total_cost,
}
with open("last_order.json", "w") as f:
    json.dump(order, f, indent=2)

print("Order saved! Run  python pizza_turtle.py  to watch Shelly draw your pizza.")
Downloads

Take it with you