Skip to content

← Final exam model papers

Final Exam Model Paper 3

Theory paper: 50 marksTime: 2 hours

Practical exam: 25 marksTime: 1 hour 30 minutes

All questions are compulsory.

A full practice run of the Grade 10 final exam: the theory paper (50 marks, 2 hours) and, below it, the practical exam (25 marks, 1 hour 30 minutes, at a computer, marked by the school). In the theory paper, work out on paper what each program does, as in the exam hall: its programs have no Run button. In the practical exam, the programs run on this page.

A practice paper by Science Playground, made to its own course’s grid; not an official exam paper.

Write your answers on paper first. Then open each answer and its marking scheme, and give yourself one mark for each point you got. In multiple choice, your first choice counts.

Section A

10 × 1 = 10

1. Choose the correct option for each of the following questions.

a.

What is a module’s interface?

Pick an answer to see why.

b.

In a volleyball league in Jhapa, each pair of teams plays at most once. This dictionary shows which teams have played each other so far:

played = {    "Birtamod": ["Damak", "Mechinagar", "Kankai"],    "Damak": ["Birtamod", "Arjundhara", "Kankai", "Shivasatakshi"],    "Mechinagar": ["Birtamod", "Arjundhara"],    "Arjundhara": ["Damak", "Mechinagar", "Shivasatakshi"],    "Kankai": ["Birtamod", "Damak"],    "Shivasatakshi": ["Damak", "Arjundhara"],}

How many matches have been played?

Pick an answer to see why.

c.

What does NULL in a field of a database mean?

Pick an answer to see why.

d.

The basketball coach of a school in Chitwan must pick one player to take the free throws. Her records for the season:

Home games Away games All games
Rita 9 of 10 30 of 60 39 of 70
Sapana 50 of 60 4 of 10 54 of 70

Which judgment is right?

Pick an answer to see why.

e.

A shop in Janakpur uses a billing app whose server is in a data center abroad, 4,000 km away along the fiber-optic cables. Light in a fiber travels about 200,000 km a second. What is the least time a request can take to reach the server and its answer to come back?

Pick an answer to see why.

f.

What is a shell?

Pick an answer to see why.

g.

What is fine-tuning a language model?

Pick an answer to see why.

h.

Kiran starts a very long chat by telling a chatbot: “Always answer in Nepali.” Many pages later, it begins to answer in English. What is the most likely reason?

Pick an answer to see why.

i.

A municipality’s old program adds up the taxes it collects, in paisa, in a signed 32-bit number, whose biggest value is 2,147,483,647 (Rs 2,14,74,836.47). This year the taxes pass that amount. What could the program show as the total?

Pick an answer to see why.

j.

Bina says: “AI tools write programs now, so there is no point in my learning computing.” Which judgment is best?

Pick an answer to see why.

Section B

10 × 2 = 20

Write short answers to the following questions.

2.
[2]

For Holi, a shop in Kalaiya, Bara, has this program for its prices:

discount = 10     # percent, all year def holi_price(price):    discount = 25    return price - price * discount // 100 print(holi_price(400), discount)
  • i.Write the output.
  • ii.Why is discount still 10 after the function has run?
Answer

i. Inside the function, discount is 25, so the price is 400 − 400 × 25 // 100 = 400 − 100 = 300. Outside, discount is still 10:

Output

300 10

ii. Line 4 makes a new local variable called discount inside the function. It shadows the global one: inside the function, Python finds the local name first. The global discount is never changed, and the local one disappears when the function returns.

Marking scheme
  • i. 300 10.
  • ii. The assignment inside makes a local variable that shadows the global one, so the global discount isn’t changed.
3.
[2]

A student in Bharatpur draws the lanes of her tole as a graph, for a program that finds the fewest turns from one house to another: a node for each corner, an edge for each lane between two corners.

  • i.Name two things about the real lanes that the graph leaves out, and say why it can leave them out.
  • ii.A friend wants to use the same graph to find the shortest walk, in meters. What must be added to it, and why?
Answer
  • i.Any two: the lanes’ shapes and bends, their widths, what they are made of, the shops and houses along them, their lengths. This is abstraction: the graph keeps only what the question needs, which corners are joined to which, as Euler kept only which land each bridge joined. Everything left out makes no difference to which corners can be reached, or in how many steps.
  • ii.Each edge needs a weight, the lane’s length in meters: it becomes a weighted graph. Without weights every lane counts as one step, so a search finds the fewest lanes, which may be a longer walk than a route with more, shorter lanes.
Marking scheme
  • i. Two things left out (shapes, widths, lengths, buildings), because only which corners are joined matters (abstraction).
  • ii. Weights, the lanes’ lengths, because without them every lane counts the same.
4.
[2]

A ward office in Dolakha keeps a register of the shops in the ward. Write a CREATE TABLE statement for a table shops with these fields:

  • license_no, a whole number, the primary key;
  • name, text, which must always be filled in;
  • ward, a whole number from 1 to 9;
  • phone, text, never the same for two shops.
Answer
CREATE TABLE shops (  license_no INTEGER PRIMARY KEY,  name TEXT NOT NULL,  ward INTEGER CHECK (ward BETWEEN 1 AND 9),  phone TEXT UNIQUE);

The phone number is TEXT, not a number: nobody adds phone numbers, and as a number a leading 0 or a + would be lost.

Marking scheme
  • license_no INTEGER PRIMARY KEY and name TEXT NOT NULL.
  • ward with CHECK (ward BETWEEN 1 AND 9) (or >= 1 AND <= 9), and phone TEXT UNIQUE.
5.
[2]

Three toles’ groups sang at a Teej song competition, and three judges gave each group a score. The scores are in teej.csv:

teej.csv

group,judge,scoreThado Tole,1,78Thado Tole,2,82Thado Tole,3,80Bhimsen Tole,1,85Bhimsen Tole,2,88Bhimsen Tole,3,79Pipal Chautari,1,70Pipal Chautari,2,74Pipal Chautari,3,72

Write a program with pandas that prints each group’s mean score, the highest first. Write what it prints.

Answer
import pandas as pd scores = pd.read_csv("teej.csv")means = scores.groupby("group")["score"].mean()print(means.sort_values(ascending=False))

Output

groupBhimsen Tole      84.0Thado Tole        80.0Pipal Chautari    72.0Name: score, dtype: float64

Bhimsen Tole: (85 + 88 + 79) ÷ 3 = 84. Thado Tole: (78 + 82 + 80) ÷ 3 = 80. Pipal Chautari: (70 + 74 + 72) ÷ 3 = 72.

Marking scheme
  • groupby("group")["score"].mean() after reading the file with pd.read_csv.
  • Sorted with sort_values(ascending=False), giving Bhimsen Tole 84, Thado Tole 80, Pipal Chautari 72.
6.
[2]

While Suresh, in Dhanusha, is searching how to fix his slow laptop, a pop-up fills the screen: “WARNING! Your computer is infected. Call Microsoft Support now on this number.” When he calls, a friendly man asks him to install a program so that he can “fix it from here”.

  • i.Why is this help really a trap?
  • ii.What should Suresh do?
Answer
  • i.Real companies don’t phone you about a virus, and they don’t advertise support numbers in pop-ups; fake “support lines” pay for pop-ups and adverts like this one. The program he is asked to install would let a stranger control his screen, and with it his files, accounts and money apps; next the man would ask for passwords, codes or a payment.
  • ii.Not install anything and hang up; close the pop-up (or the browser) without calling, and never give a password or a code sent to his phone. Then fix the slow laptop the usual way, looking first at what is using the disk and memory, or ask a teacher or someone he trusts.
Marking scheme
  • i. It is a fake support line: real companies don’t warn you by pop-up or phone, and the program would give a stranger control of his computer.
  • ii. Install nothing, give no password or code, close it; get help from someone he trusts.
7.
[2]

Bina’s terminal shows:

bina@lab-pc:~/school/class10/project$

Her home folder holds a folder school, with folders class9 and class10 inside it. The file marks.csv is in class10, and notes.txt is in class9.

  • i.Write a command that shows what is in marks.csv, using a relative path.
  • ii.Write a command that shows what is in notes.txt, using an absolute path.
Answer

i. marks.csv is one level up from project, so:

cat ../marks.csv

ii. An absolute path starts from the very top, /, and Bina’s home folder is /home/bina:

cat /home/bina/school/class9/notes.txt

(cat ~/school/class9/notes.txt names the same file from the home folder.)

Marking scheme
  • i. cat ../marks.csv.
  • ii. cat /home/bina/school/class9/notes.txt (the full path from /).
8.
[2]

Sagar asks a chatbot, “What is the capital of Madhesh Province?” It answers correctly, “Janakpur.” He types: “Are you sure? I think it is Birgunj.” The chatbot replies: “You’re right, I apologize. The capital is Birgunj.”

  • i.From how chatbots are trained, explain why it changed its answer.
  • ii.How should Sagar ask, and check, to get a trustworthy answer?
Answer
  • i.Chatbots are trained on people’s ratings of their answers, and people tend to rate answers that agree with them more highly. So a model can learn to agree too easily, even with a wrong idea. It has no way to look the fact up; it predicts the reply that sounds most welcome after “I think it is Birgunj”.
  • ii.Ask a neutral question, without saying what he thinks the answer is, and don’t treat its answer as checked: check the fact in a reliable source, such as the province government’s website or a textbook.
Marking scheme
  • i. It learned to agree: people’s ratings rewarded answers that agree with them (it predicts the welcome answer, not the checked one).
  • ii. Ask without giving his own answer, and check in a reliable source.
9.
[2]

A shop’s Holi offer is: “20% off any bill of Rs 1,000 or more.” Nabin asks an AI tool to write the function, and gets:

def holi_bill(total):    if total > 1000:        return total * 0.8    return total

He tries holi_bill(1500), gets 1200.0, and says the function is correct. Judge his check, and say how he should test it.

Answer

His check is not enough. One test of an easy case can’t show that the code does what the offer says. Tests should come from the requirement, including its edge: “Rs 1,000 or more” means a bill of exactly Rs 1,000 gets 20% off. He should test 999 (no discount), 1000 (800) and 1001, and one ordinary value. holi_bill(1000) gives 1000, so the test finds the mistake: the AI wrote > where the offer needs >=. He must understand the code he uses, not just see it run once.

Marking scheme
  • His check isn’t enough: one easy test doesn’t show the code meets the requirement.
  • Tests from the requirement at the edge (999, 1000, 1001), which would show that 1000 gets no discount because of > instead of >=.
10.
[2]

Write any two steps historians use to trace the history of a technology.

Answer

Any two: fix the dates from more than one source, and say where sources disagree and what each counts; prefer sources from the time (a newspaper report of the day, a law’s own text) to later summaries; ask what made each step possible, and what held it back (money, laws, needs, the land, disasters, other technologies); ask who gained and who was left out at each step; ask what the technology did to society and the environment, intended or not.

Marking scheme
  • One step: fix dates from several sources, prefer sources from the time, ask what made each step possible or held it back, who gained or was left out, or its effects.
  • A second, different step.
11.
[2]

A newspaper column says: “In Kenya, mobile money lifted 2 in every 100 households out of poverty. So if everyone in Nepal used a mobile wallet, poverty here would end.” Judge this claim.

Answer

The claim goes far beyond its evidence. First, the Kenyan figure comes from one study whose methods other researchers have seriously questioned; economists still debate how much M-Pesa reduced poverty. Second, even taken at face value, it is 2 households in 100, not the end of poverty. Third, Nepal isn’t Kenya: M-Pesa worked where most people had no bank account and families sent money home by hand. And even in Kenya some were left out: people with no phone, far from any agent, or whose savings were eaten by fees. A fair claim would say what mobile money might help with, for whom, and how we would find out.

Marking scheme
  • The evidence is weak or too small for the claim: one disputed study, and only 2 in 100 households.
  • Another reason: Nepal’s situation differs, or some people are left out (no phone, far from agents, fees).

Section C

5 × 4 = 20

Write long answers to the following questions.

12.
[4]

A shop in Ilam gives change in notes of Rs 1,000, 500, 100, 50, 20, 10 and 5, and Rs 1 coins, always as few notes and coins as possible.

  • i.Write a module, change.py, with a function change_notes(paid, price) that returns the change as a dictionary from each note (or coin) to how many of it, leaving out those not used, with a docstring. If the customer paid less than the price, it must raise a ValueError. (2)
  • ii.Add tests at the end of the module that run only when change.py itself is run: an exact payment, an ordinary one, and too little paid. (1)
  • iii.Write a separate program that imports the module and prints the change, one line for each note, for a customer who pays Rs 2,000 for a bill of Rs 765. (1)
Answer

i. and ii.

change.py

"""Change for the shop's customers.""" NOTES = [1000, 500, 100, 50, 20, 10, 5, 1]    # rupees, the biggest first  def change_notes(paid, price):    """The change, as {note: how many}, in as few notes as possible.    Raises ValueError if paid is less than price."""    if paid < price:        raise ValueError("paid less than the price")    left = paid - price    change = {}    for note in NOTES:        if left >= note:            change[note] = left // note            left = left % note    return change  if __name__ == "__main__":    print(change_notes(500, 500), "should be {}")    print(change_notes(100, 37), "should be {50: 1, 10: 1, 1: 3}")    try:        change_notes(100, 120)        print("no error: wrong")    except ValueError:        print("ValueError, as it should be")

iii.

import change for note, count in change.change_notes(2000, 765).items():    print("Rs", note, "×", count)

Output

Rs 1000 × 1Rs 100 × 2Rs 20 × 1Rs 10 × 1Rs 5 × 1

The change is 2,000 − 765 = Rs 1,235: one 1,000, then 235 left: two 100s, then 35: one 20, one 10, one 5. Taking the biggest note that fits each time gives the fewest notes for Nepal’s notes. Any clear names are fine.

Marking scheme
  • i. A function change_notes(paid, price) with a docstring that returns a dictionary (it doesn’t print), using the biggest note first (// and %, or repeated subtraction).
  • i. raise ValueError when paid is less than the price.
  • ii. Tests under if __name__ == "__main__": with an exact payment, an ordinary one and too little paid (caught with try/except), with what each should give.
  • iii. A program that imports change and prints the notes for Rs 2,000 and Rs 765: 1,000 × 1, 100 × 2, 20 × 1, 10 × 1, 5 × 1.
13.
[4]

During Chhath, volunteers in Janakpur carry messages on foot between the ponds and the temple. The paths they use are in a list (simplified for this question):

paths = [    ("Janaki Mandir", "Gangasagar"),    ("Janaki Mandir", "Dhanushsagar"),    ("Gangasagar", "Dhanushsagar"),    ("Gangasagar", "Ratnasagar"),    ("Dhanushsagar", "Argaja"),    ("Ratnasagar", "Argaja"),    ("Argaja", "Bihari Kund"),    ("Ratnasagar", "Police post"),]
  • i.Write the graph as a dictionary of neighbors. (1)
  • ii.Write each place’s degree. Show that the degrees add up to twice the number of paths, and say why. (1)
  • iii.Search in rings from Janaki Mandir. In how few paths can a message reach Bihari Kund, and the Police post? Write each route. (2)
Answer

i. Each path goes both ways, so it is written in both places’ lists:

ponds = {    "Janaki Mandir": ["Gangasagar", "Dhanushsagar"],    "Gangasagar": ["Janaki Mandir", "Dhanushsagar", "Ratnasagar"],    "Dhanushsagar": ["Janaki Mandir", "Gangasagar", "Argaja"],    "Ratnasagar": ["Gangasagar", "Argaja", "Police post"],    "Argaja": ["Dhanushsagar", "Ratnasagar", "Bihari Kund"],    "Bihari Kund": ["Argaja"],    "Police post": ["Ratnasagar"],}
  • ii.Janaki Mandir 2, Gangasagar 3, Dhanushsagar 3, Ratnasagar 3, Argaja 3, Bihari Kund 1, Police post 1. Together 16 = 2 × 8 paths, because each path has two ends, so it is counted once in each of the two places’ lists.
  • iii.1 path away: Gangasagar, Dhanushsagar. 2 paths away: Ratnasagar (from Gangasagar), Argaja (from Dhanushsagar). 3 paths away: Police post (from Ratnasagar), Bihari Kund (from Argaja).
  • Bihari Kund: 3 paths, Janaki Mandir → Dhanushsagar → Argaja → Bihari Kund.
  • Police post: 3 paths, Janaki Mandir → Gangasagar → Ratnasagar → Police post.
Marking scheme
  • i. A dictionary with every path in both places’ lists.
  • ii. The degrees (2, 3, 3, 3, 3, 1, 1), adding up to 16 = 2 × 8, because each path is counted at both ends.
  • iii. Bihari Kund in 3 paths, Janaki Mandir → Dhanushsagar → Argaja → Bihari Kund.
  • iii. Police post in 3 paths, Janaki Mandir → Gangasagar → Ratnasagar → Police post.
14.
[4]

A shop in Kalaiya keeps its Holi stock in a table, stock:

item_id name price qty
1 Abir red, packet 30 120
2 Abir green, packet 30 80
3 Pichkari, small 150 40
4 Pichkari, large 450 12
5 Water balloons, 100 120 60
6 Abir pink, packet 35 0
  • i.What does SELECT name FROM stock WHERE name LIKE 'Abir%' AND qty > 0; give? (1)
  • ii.What does SELECT name, price FROM stock WHERE price BETWEEN 100 AND 200 ORDER BY price; give? (1)
  • iii.The shopkeeper meant to raise the large pichkari’s price to Rs 500, but typed UPDATE stock SET price = 500;. What happened? Write what he should have typed. (1)
  • iv.What does SELECT COUNT(*), SUM(qty) FROM stock WHERE price < 100; give? (1)
Answer
  • i.The names that start with “Abir” and are in stock: Abir red, packet and Abir green, packet. The pink has qty 0.
  • ii.Prices from 100 to 200, both ends included, the cheapest first:
name price
Water balloons, 100 120
Pichkari, small 150
  • iii.With no WHERE, the UPDATE changed every row: every item now costs Rs 500. He should have typed UPDATE stock SET price = 500 WHERE item_id = 4;.
  • iv.The three packets of abir cost less than Rs 100: 3 rows, with 120 + 80 + 0 = 200 packets in all.
COUNT(*) SUM(qty)
3 200
Marking scheme
  • i. Abir red, packet and Abir green, packet.
  • ii. Water balloons, 100 (120) and Pichkari, small (150), in that order.
  • iii. Every row’s price became 500; UPDATE stock SET price = 500 WHERE item_id = 4;.
  • iv. 3 and 200.
15.
[4]

Kamala runs a tea shop at Ilam bazaar. She wants to know how often she will need more than her usual 15 liters of milk in a day, using about 1 liter for every 8 cups. Her nephew writes a simulation of 30 days:

import random random.seed(4)busy_days = 0for day in range(30):    cups = round(random.gauss(100, 15))    if cups / 8 > 15:        busy_days = busy_days + 1print(busy_days, "days in 30 needed more than 15 liters")
  • i.What does random.gauss(100, 15) give, each time it is called? (1)
  • ii.What does line 7 check? How many cups make a busy day? (1)
  • iii.Why should the nephew run the 30 days many times, for example 1,000 times, before telling her how often it happens? (1)
  • iv.How could Kamala check whether the simulation is a good model of her shop? (1)
Answer
  • i.A random number from the normal distribution (the bell shape) with its middle at 100 and a standard deviation of 15: most days near 100 cups, about two in three between 85 and 115, and a few much busier or quieter.
  • ii.It works out the liters of milk that day’s cups need (1 liter for 8 cups) and checks whether that is more than 15 liters. 15 × 8 = 120, so a day of more than 120 cups is a busy day.
  • iii.One run of 30 days is one possible month; another month could have more or fewer busy days just by chance. Running it many times and looking at the results together (their average and their spread) shows what is usual, and how much it can vary.
  • iv.Compare it with real records: count the cups she really sold on past days, and see whether their middle and spread look like 100 and 15 (and whether busy days really are as common as the simulation says). The model also leaves things out, such as festivals, market days and the tourist season, that she could check for.
Marking scheme
  • i. A random number from a normal (bell-shaped) distribution, centered on 100 with a standard deviation of 15.
  • ii. Whether the milk needed (cups ÷ 8) is over 15 liters: more than 120 cups.
  • iii. One run is one chance month; many runs show what is usual and how much it varies.
  • iv. Compare its results with her real sales records (and what the model leaves out).
16.
[4]

Read the given passage and answer the questions that follow.

When tickets for a home cricket series went on sale online, the ticket company’s website ran on one server. Within minutes of opening, tens of thousands of fans tried to buy at once; the server slowed, then stopped, and nobody could buy anything for two hours. For the next series, the company rented servers from a cloud provider: three servers behind a load balancer, with more added automatically when many visitors arrive. The load balancer checks each server every few seconds, and if one stops answering, sends its visitors to the others.

  • i.What is a single point of failure? What was it the first time? (1)
  • ii.What does a load balancer do? (1)
  • iii.What are a health check and failover, in this passage? (1)
  • iv.What is scaling, and why does the cloud make it easy? (1)
Answer
  • i.A part whose failure stops the whole system. The first time it was the one server: when it stopped, nobody could buy.
  • ii.It stands in front of several servers doing the same job and passes each request to one of them, sharing out the work.
  • iii.The health check is the load balancer asking each server every few seconds whether it is working; failover is switching visitors to the working servers when one fails.
  • iv.Scaling is growing or shrinking a system to fit the work. In the cloud, servers are rented and can be added in minutes for the busy hours and removed afterwards, paying only for what is used, instead of buying enough servers for the busiest hour and leaving them idle.
Marking scheme
  • i. A part whose failure stops everything; the one server.
  • ii. It shares the requests out among several servers.
  • iii. Health check: asking each server whether it is working; failover: switching to working servers when one fails.
  • iv. Scaling is growing or shrinking to fit the work; the cloud rents servers in minutes, paying for use.

Practical exam

25 marks

Marked by the school. Work alone at a computer, with no internet and no AI, in front of the teacher, for 1 hour 30 minutes. Write and run each program, show it working, and answer the teacher’s questions. On this page, press Edit on a program to type in it, and Run to run it.

P1.
[7]

The half marathon. At the half marathon in Charikot, Dolakha, each runner is put in a category by age: under 16, Junior; 16 to 39, Open; 40 to 59, Veterans; 60 or more, Seniors. Children under 10 may not run.

At the computer, make two files in one folder:

  • race.py, a module with a function category(age) that returns the category’s name, or "Too young" for a child under 10, with tests at the end that run only when race.py itself is run;
  • main.py, which imports it, asks the ages of 4 runners, and prints each runner’s category.

Run race.py, then main.py with 15, 40, 72 and 8. (On this page, try your function in the box below first.)

# Try your category function heredef category(age):    return ""
Answer

race.py

"""Categories for the half marathon."""  def category(age):    """The category's name for a runner of this age, or "Too young"."""    if age < 10:        return "Too young"    if age < 16:        return "Junior"    if age < 40:        return "Open"    if age < 60:        return "Veterans"    return "Seniors"  if __name__ == "__main__":    # tests on both sides of every edge    for age, should in [(9, "Too young"), (10, "Junior"), (15, "Junior"),                        (16, "Open"), (39, "Open"), (40, "Veterans"),                        (59, "Veterans"), (60, "Seniors")]:        print(age, category(age), "should be", should)

Run by itself, race.py prints its tests:

Output

9 Too young should be Too young10 Junior should be Junior15 Junior should be Junior16 Open should be Open39 Open should be Open40 Veterans should be Veterans59 Veterans should be Veterans60 Seniors should be Seniors

main.py

import race for runner in range(1, 5):    age = int(input(f"Runner {runner}: age? "))    print("Category:", race.category(age))

Output

Runner 1: age? 15Category: JuniorRunner 2: age? 40Category: VeteransRunner 3: age? 72Category: SeniorsRunner 4: age? 8Category: Too young
Marking scheme
  • race.py has a function category(age) that returns the name (it doesn’t print it).
  • Under 10 gives “Too young”, checked first.
  • The four categories with the right edges: under 16, 16–39, 40–59, 60 or more.
  • The checks in an order that works (youngest first with <, or with and on both ends).
  • Tests under if __name__ == "__main__": on both sides of the edges (such as 15 and 16, 39 and 40, 59 and 60), which don’t run when it is imported.
  • main.py imports race and prints the returned category for each runner, asking with int(input(…)).
  • Runs both in front of the teacher, and 15, 40, 72, 8 give Junior, Veterans, Seniors, Too young.
P2.
[6]

The ward office. Ward 4 of a municipality in Ilam records the services people ask for. This program makes its database and gives you ask(), which prints what a query gives. Run it, then add one ask() for each question:

  • i.The request_id and day of the requests still waiting that were made from 2026-09-02 to 2026-09-04, the earliest first. (2)
  • ii.How many requests there were for each service_id, the most first. (2)
  • iii.The name of every service with its number of requests, including services nobody has asked for. (2)
import sqlite3 db = sqlite3.connect(":memory:")db.execute("CREATE TABLE services (service_id INTEGER PRIMARY KEY, name TEXT, fee INTEGER)")db.execute("""CREATE TABLE requests (request_id INTEGER PRIMARY KEY,              service_id INTEGER REFERENCES services(service_id),              day TEXT, status TEXT)""")db.executemany("INSERT INTO services VALUES (?, ?, ?)", [    (1, "Recommendation letter", 200),    (2, "Birth registration", 0),    (3, "Relationship certificate", 300),    (4, "House map approval", 1500),    (5, "Business registration", 500),])db.executemany("INSERT INTO requests VALUES (?, ?, ?, ?)", [    (1, 1, "2026-09-01", "done"),    (2, 2, "2026-09-01", "done"),    (3, 1, "2026-09-02", "waiting"),    (4, 5, "2026-09-02", "done"),    (5, 1, "2026-09-03", "done"),    (6, 3, "2026-09-03", "waiting"),    (7, 2, "2026-09-04", "done"),    (8, 1, "2026-09-04", "done"),    (9, 5, "2026-09-05", "waiting"),    (10, 5, "2026-09-05", "done"),])  def ask(sql):    """Print each row the query gives, then a blank line."""    for row in db.execute(sql):        print(row)    print()  ask("SELECT * FROM services")
Answer

The same program, with these three lines in place of the last one:

import sqlite3 db = sqlite3.connect(":memory:")db.execute("CREATE TABLE services (service_id INTEGER PRIMARY KEY, name TEXT, fee INTEGER)")db.execute("""CREATE TABLE requests (request_id INTEGER PRIMARY KEY,              service_id INTEGER REFERENCES services(service_id),              day TEXT, status TEXT)""")db.executemany("INSERT INTO services VALUES (?, ?, ?)", [    (1, "Recommendation letter", 200),    (2, "Birth registration", 0),    (3, "Relationship certificate", 300),    (4, "House map approval", 1500),    (5, "Business registration", 500),])db.executemany("INSERT INTO requests VALUES (?, ?, ?, ?)", [    (1, 1, "2026-09-01", "done"),    (2, 2, "2026-09-01", "done"),    (3, 1, "2026-09-02", "waiting"),    (4, 5, "2026-09-02", "done"),    (5, 1, "2026-09-03", "done"),    (6, 3, "2026-09-03", "waiting"),    (7, 2, "2026-09-04", "done"),    (8, 1, "2026-09-04", "done"),    (9, 5, "2026-09-05", "waiting"),    (10, 5, "2026-09-05", "done"),])  def ask(sql):    """Print each row the query gives, then a blank line."""    for row in db.execute(sql):        print(row)    print()  ask("""SELECT request_id, day FROM requests       WHERE status = 'waiting' AND day BETWEEN '2026-09-02' AND '2026-09-04'       ORDER BY day""")ask("SELECT service_id, COUNT(*) FROM requests GROUP BY service_id ORDER BY COUNT(*) DESC")ask("""SELECT services.name, COUNT(requests.request_id) FROM services       LEFT JOIN requests ON requests.service_id = services.service_id       GROUP BY services.service_id""")

Output

(3, '2026-09-02')(6, '2026-09-03') (1, 4)(5, 3)(2, 2)(3, 1) ('Recommendation letter', 4)('Birth registration', 2)('Relationship certificate', 1)('House map approval', 0)('Business registration', 3)

(The requests are made up for this paper.)

Marking scheme
  • i. WHERE status = 'waiting' with the dates BETWEEN '2026-09-02' AND '2026-09-04' (or >= and <=).
  • i. ORDER BY day, giving requests 3 and 6.
  • ii. GROUP BY service_id with COUNT(*).
  • ii. ORDER BY COUNT(*) DESC: 1 (4), 5 (3), 2 (2), 3 (1).
  • iii. A LEFT JOIN from services to requests, ON requests.service_id = services.service_id, grouped by service.
  • iii. COUNT(requests.request_id), so that House map approval shows 0.
P3.
[6]

Fewest clicks. A municipality’s website has pages that link to other pages. A link goes only one way, so the dictionary below lists, for each page, the pages it links to. Write a function fewest_steps(graph, start, goal) that returns the route with the fewest clicks as a list of pages, or None if there is no way. Then print the route and the number of clicks:

  • from Home to Ward 4;
  • from Tax to Home;
  • from Home to Flood notice (print “Can’t get there” if there is no way).
site = {    "Home": ["Notices", "Services", "Contact"],    "Notices": ["Home", "Budget"],    "Services": ["Tax", "Ward offices"],    "Tax": ["Services", "Budget"],    "Ward offices": ["Ward 4"],    "Ward 4": ["Contact"],    "Contact": [],    "Budget": ["Notices"],    "Flood notice": ["Home"],} # Write fewest_steps here
Answer
site = {    "Home": ["Notices", "Services", "Contact"],    "Notices": ["Home", "Budget"],    "Services": ["Tax", "Ward offices"],    "Tax": ["Services", "Budget"],    "Ward offices": ["Ward 4"],    "Ward 4": ["Contact"],    "Contact": [],    "Budget": ["Notices"],    "Flood notice": ["Home"],}  def fewest_steps(graph, start, goal):    """The route from start to goal with the fewest steps, or None."""    came_from = {start: None}    to_visit = [start]    while len(to_visit) > 0:        page = to_visit.pop(0)        if page == goal:            route = []            while page is not None:                route.append(page)                page = came_from[page]            route.reverse()            return route        for nxt in graph[page]:            if nxt not in came_from:                came_from[nxt] = page                to_visit.append(nxt)    return None  for start, goal in [("Home", "Ward 4"), ("Tax", "Home"), ("Home", "Flood notice")]:    route = fewest_steps(site, start, goal)    if route is None:        print(start, "to", goal + ": Can't get there")    else:        print(start, "to", goal + ":", " → ".join(route), "(" + str(len(route) - 1), "clicks)")

Output

Home to Ward 4: Home → Services → Ward offices → Ward 4 (3 clicks)Tax to Home: Tax → Budget → Notices → Home (3 clicks)Home to Flood notice: Can't get there

Last monsoon’s Flood notice links to Home, but no page links to it any more, so it can’t be reached by clicking.

Marking scheme
  • A function with the graph, start and goal, that starts its list (and its notes) with the start.
  • A queue: takes from the front with pop(0) and adds to the back with append().
  • Marks a page as seen (in came_from) when it is added, so none is added twice.
  • Stops at the goal and follows the notes back to the start, then reverses them.
  • Prints each route and the number of clicks (one less than the pages): 3 and 3.
  • Returns None when the list runs out, and the program prints “Can’t get there” for Flood notice.
P4.
[6]

Viva. The teacher asks each student about their own work, for example:

  • i.Why does category return the name instead of printing it? Why did you test both 39 and 40? (2)
  • ii.In your third query, why does House map approval show 0? What would it show with a plain JOIN and COUNT(*)? (2)
  • iii.Home to Tax takes 2 clicks, but Tax to Home takes 3. Why can the two be different on this website, but never on a map of two-way roads? (2)
Answer
  • i.A returned value can be used by the program that calls it: main.py prints it here, but another program could count the runners in each category or write them in a file. A function that printed would decide that for every user. 39 and 40 are the two sides of an edge between Open and Veterans, where a <= written for a < would put a runner in the wrong category.
  • ii.The LEFT JOIN keeps every service, even with no matching request, and fills the request’s fields with NULL; COUNT(requests.request_id) counts only the fields that aren’t NULL, so it gives 0. A plain JOIN would drop House map approval altogether, since no request matches it; it wouldn’t appear in the list at all.
  • iii.The links are one-way: the graph is directed. Home → Services → Tax uses links that exist only in that direction; to go back, Tax must use its own links, Tax → Budget → Notices → Home. On a map of two-way roads (an undirected graph), every route can be walked backwards, so the fewest steps are the same both ways.
Marking scheme
  • i. Returning lets the calling program use the value as it needs (print, count, save).
  • i. 39 and 40 are either side of an edge, where mistakes hide.
  • ii. LEFT JOIN keeps services with no requests, and COUNT of a field skips the NULL, giving 0.
  • ii. A plain JOIN would leave House map approval out of the results.
  • iii. The links go one way (a directed graph), so the way back uses different links.
  • iii. On two-way roads every route can be walked backwards, so the fewest steps are equal.

Your score

0 / 50 theory

0 / 25 practical

29 questions not marked yet

Section A–Full marks 10
Section B–Full marks 20
Section C–Full marks 20
Practical exam–Full marks 25

Marks lost, by unit

Mark a question to see your score here.

Score 0 of 0 0 of 29 marked Time left: 2:00:00