Final Exam Model Paper 6
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 = 101. Choose the correct option for each of the following questions.
In programming, what is a function’s side effect?
Pick an answer to see why.
The trekking guides of a village in Solukhumbu pass on work to the guides they know. This dictionary shows who knows whom:
guides = { "Pasang": ["Mingma", "Lhakpa"], "Mingma": ["Pasang", "Dawa", "Lhakpa"], "Lhakpa": ["Pasang", "Mingma", "Nima"], "Dawa": ["Mingma", "Pemba"], "Nima": ["Lhakpa"], "Pemba": ["Dawa"],}Which guides are exactly 2 steps from Pasang: guides Pasang doesn’t know, who know someone he knows?
Pick an answer to see why.
What is an index in a database?
Pick an answer to see why.
Asha draws a bar chart of the trekkers from five countries in one season. Its axis up is a log scale, marked 100, 1,000 and 10,000, equally spaced. India’s bar reaches 10,000 and Germany’s 1,000, so India’s bar is only a little taller. She writes: “About as many trekkers came from Germany as from India.” Which judgment is right?
Pick an answer to see why.
Making a trekking guide’s phone put out about 45 kg of CO₂e, and charging it puts out about 3 kg a year. If she keeps it for 6 years instead of 3, how much of the making is counted for each year she uses it?
Pick an answer to see why.
A Windows update in a trekking agency’s office in Thamel fails with the message “We couldn’t complete the updates. Undoing changes.” and the code 0x800f0922. Which part is the most useful to search for?
Pick an answer to see why.
How does byte-pair encoding make a tokenizer’s list of tokens?
Pick an answer to see why.
At a debate in a Pokhara school about chatbots, four things were said. Which one is an open question, which nobody can yet settle with evidence?
Pick an answer to see why.
A company starts selling low-orbit satellite internet in Mustang, licensed in Nepal. Which group is most likely to lose from it?
Pick an answer to see why.
At a tourism conference in Pokhara, a speaker says: “In 1943 the head of IBM said the world would need only about five computers. Experts are always wrong about technology, so ignore what they say about AI.” Which judgment is right?
Pick an answer to see why.
Section B
10 × 2 = 20Write short answers to the following questions.
A tourist office has a module, fares.py:
fares.py
TAXI = 1500 # rupees, airport to Lakeside def bus(seats): return seats * 600and a program:
import faresfrom fares import bus print(fares.TAXI)print(bus(3))print(TAXI)- i.Write what lines 4 and 5 print.
- ii.Line 6 stops the program with an error. Why? Write it so that it works.
Answer
i. fares.TAXI is the name TAXI in the module’s own box; from fares import bus copied the name bus into the program, so it can be used without the dot:
Output
15001800(3 × 600 = 1,800.) Then line 6 stops the program.
ii. import fares brings in only the module’s name, fares: TAXI stays in the module’s own namespace. Only bus was copied into the program’s names, so line 6 gives NameError: name 'TAXI' is not defined. Write print(fares.TAXI) (or import it too: from fares import bus, TAXI).
Marking scheme
- i.
1500and1800. - ii. TAXI is in the module’s namespace (only bus was copied in), so a NameError;
fares.TAXI(orfrom fares import TAXI).
A map app finds a driving route from Ilam to Dadeldhura, across the whole country, in under a second. Explain:
- i.how it can answer so fast, though the map has millions of roads;
- ii.why a route it gives can still be wrong, however good its algorithm.
Answer
- i.It doesn’t try every lane in every town. It works out a lot beforehand: before anyone asks, the company’s computers find the important long-distance roads and store shortcuts along them, so a long route only searches the small roads near the start and the end and joins them to the stored shortcuts. (It also searches toward the goal rather than in every direction.)
- ii.A search can only use the graph it has. If a road is closed, a bridge washed away, or a new road missing from the map data, the route is wrong, because a route is only as right as the data; the map must be kept up to date.
Marking scheme
- i. It works out much beforehand: stores shortcuts along the main long-distance roads, so it doesn’t search every small road.
- ii. The map data can be wrong or out of date (a closed road still in it, a new road missing), and the search can only use the graph it has.
The Annapurna Conservation Area’s office keeps the trekkers who enter in a table, permits:
| permit_id | name | country | trek | entered |
|---|---|---|---|---|
| 1 | Aiko Sato | Japan | Manang | 2026-10-02 |
| 2 | Lena Vogel | Germany | Mardi Himal | 2026-10-02 |
| 3 | Ravi Menon | India | Manang | 2026-10-03 |
| … | … | … | … | … |
Write an SQL query that gives, for each country, the number of trekkers who went to Manang, the country with the most first.
Answer
SELECT country, COUNT(*)FROM permitsWHERE trek = 'Manang'GROUP BY countryORDER BY COUNT(*) DESC;WHERE keeps only the Manang rows; GROUP BY makes one group per country, and COUNT(*) counts the rows in each.
Marking scheme
WHERE trek = 'Manang'withGROUP BY countryandCOUNT(*).ORDER BY COUNT(*) DESC.
A tourism office has homestays.csv:
homestays.csv
homestay,district,rooms,priceSirubari,Syangja,3,1800Bandipur,Tanahun,2,2500Panchmul,Syangja,4,1500Ghale Gaun,Lamjung,4,2200Thumki,Syangja,2,2100Bhujung,Lamjung,3,1700- i.Write pandas lines that read the file and print: how many homestays it lists; the average price of the homestays in Lamjung; and the lowest price of all.
- ii.Write what your lines print.
Answer
i.
import pandas as pd homes = pd.read_csv("homestays.csv")print(len(homes))lamjung = homes[homes["district"] == "Lamjung"]print(lamjung["price"].mean())print(homes["price"].min())ii. Lamjung’s two homestays cost 2,200 and 1,700, so their average is 1,950. mean() always gives a decimal number:
Output
61950.01500Marking scheme
- i. Reads the file, prints the number of rows (
len(homes)orhomes.shape), keeps the Lamjung rows with a filter, and uses the column methodsmean()andmin()on price. - ii.
6,1950.0and1500.
Sanjay’s program for a trekking agency gives a wrong total for every group, and he can’t see why. His teacher says: “Explain your program out loud, line by line, to a friend, or even to a rubber duck.”
- i.Why does this often help, even when the listener knows nothing about programming?
- ii.What should Sanjay say about each line?
Answer
- i.This is rubber duck debugging. To explain each line out loud he must say it exactly, and then he notices the step he had skipped or the thing he had assumed without checking. The listener doesn’t have to answer: the explaining does the work, so a duck (or a pencil and paper) is enough.
- ii.For each line, what he expected it to do (which values it should hold, what it should print) and what actually happens; where the two differ, he has found the line to look at.
Marking scheme
- i. Saying every step exactly makes him notice the step he skipped or the wrong assumption; the explaining, not the listener, does it.
- ii. For each line, what he expected and what actually happens (the values), to find where they differ.
Pemba’s home folder holds the folders Downloads, photos and treks, and treks holds manang.txt, mustang.txt and his program permits.py. His terminal shows:
Output
pemba@lab-pc:~$ mkdir treksmkdir: cannot create directory ‘treks’: File existspemba@lab-pc:~$ python3 permits.pypython3: can't open file '/home/pemba/permits.py': [Errno 2] No such file or directoryExplain each error, and write the command or commands he should have typed instead of each.
Answer
File exists: there is already a folder called treks in his home folder, so mkdir can’t make another. Nothing is wrong: he can go straight in with cd treks.can't open file: this message comes from Python itself, which couldn’t find the program: it looked for /home/pemba/permits.py, in the folder he is in, but permits.py is in treks. He should go where the file is, cd treks, then run python3 permits.py.
Marking scheme
- File exists: the folder is already there; just
cd treks. - can't open file: Python looked for the program in the home folder, but it is in treks;
cd treks, thenpython3 permits.py.
Kabita tells a chatbot that she sells pashmina shawls in Lakeside. A week later, in a new chat, it asks her: “How is the shawl shop doing?” Her brother says: “So the model learns from every conversation.”
- i.Is he right? Explain what really happened.
- ii.What can Kabita do if she doesn’t want it to remember this?
Answer
- i.No. After training, the model’s parameters are frozen: it doesn’t learn from conversations, and a new chat starts with the model exactly as it was. This chatbot keeps notes about its user, a record kept outside the model, and adds them to the context of each new chat, so the model can read “sells pashmina shawls in Lakeside” there.
- ii.Because the notes are a record outside the model, she can usually see and delete them in the chatbot’s settings (or turn the notes off), and then new chats won’t have them.
Marking scheme
- i. No: the model doesn’t learn from conversations; the chatbot keeps notes outside the model and puts them in the context of new chats.
- ii. She can see and delete the notes (or switch them off) in its settings.
Rubina wants to know whether a chatbot gives fair career advice. She asks it once: “Suggest careers for Ramesh, a boy from Kathmandu who is good at maths.” Then she asks once: “Suggest careers for Sunita, a girl from Humla who likes reading.” The answers differ, and she concludes that the chatbot is unfair to girls. Judge her test, with two reasons.
Answer
Her test can’t show that. A swap test must change one detail only. She changed three at once (boy or girl, Kathmandu or Humla, maths or reading), so she can’t tell which one made the answers differ; liking reading could explain different careers on its own. And she asked each version only once, but a chatbot’s answers vary every time, so one difference may be chance. A fair test: the same sentence with only the name and “boy” or “girl” changed, each asked several times in new chats, and look for differences that appear every time.
Marking scheme
- She changed more than one detail (gender, place, subject), so the cause of the difference can’t be known.
- She asked each only once, while answers vary; ask each several times (with one detail changed).
Ramila, in Tanahun, designs posters for a client in Canada. What is an invoice, and why should she keep each one with the bank’s record of the money arriving?
Answer
An invoice is a bill she sends the client for each payment: what was done, the price, and how to pay. Kept with the bank’s record of the money arriving, it shows that the money is foreign income for her work, which she can show to get a loan, for a visa’s tax clearance, to pay the right tax, or if there is a dispute with the client.
Marking scheme
- An invoice is a bill for each payment (the work, the price, how to pay).
- Together with the bank’s record, it is proof of income from work: for a loan, a visa’s tax clearance, tax or a dispute.
A speaker says: “Nepal has more mobile subscriptions than people, so every Nepali is online. The digital divide is closed.” Judge this claim, with two reasons.
Answer
The claim is wrong. First, subscriptions are not people: one person with two SIM cards and a home connection counts three times, while many others have none. In 2021 Nepal’s telecom regulator counted internet subscriptions for about 91 in every 100 people, but the International Telecommunication Union estimated that about 56 in every 100 people used the internet in 2023. Second, even people who are online don’t gain equally (inequity in outcomes): a weak signal, data that runs out, poor English or little skill means far less benefit than a fast connection and good English. The divide isn’t closed; to know who is online, a survey must count people, and then ask what they can do with it.
Marking scheme
- Subscriptions are not people: one person can have several SIMs, so the count is higher than the people online.
- Being online isn’t enough: connection, cost, language and skills decide who gains (inequity in outcomes); count people instead.
Section C
5 × 4 = 20Write long answers to the following questions.
A jeep counter at Beni sells seats to Jomsom. Its program:
total = 0 def sell(seats): global total price = seats * 1200 if seats >= 4: price = price - 200 total = total + price print("Rs", price) sell(2)sell(5)print("Total", total)- i.Write two problems with this design, as Unit 1 judges programs. (1)
- ii.Rewrite it as a module, jeep.py, with constants, a pure function
price(seats)with a docstring, and tests that run only when jeep.py itself is run. (2) - iii.Write a main program that imports jeep, sells 2 seats and then 5, and prints each price and the total. (1)
Answer
i. Any two: sell changes a global (total), so a wrong total could come from any line that touches it, and the function can’t be tested on its own; it prints instead of returning the price, so no other program can use the price (a side effect mixed with the work); the numbers 1200, 4 and 200 are written inside the function, not as named constants.
ii.
jeep.py
"""Jeep fares from Beni to Jomsom.""" PER_SEAT = 1200 # rupeesGROUP_SEATS = 4 # this many seats or more get the discountDISCOUNT = 200 # rupees off a group's price def price(seats): """The price in rupees for this many seats, Rs 200 off for 4 or more.""" total = seats * PER_SEAT if seats >= GROUP_SEATS: total = total - DISCOUNT return total if __name__ == "__main__": # tests at the edge of the discount print(price(3), "should be 3600") print(price(4), "should be 4600")iii.
import jeep total = 0for seats in [2, 5]: p = jeep.price(seats) print("Rs", p) total = total + pprint("Total", total)Output
Rs 2400Rs 5800Total 8200Now price is a pure function: the total is kept in the program’s own lines, where you can see every change, and the module can be tested and used by another program, such as a booking website.
Marking scheme
- i. Two problems: a global changed inside the function; printing instead of returning (side effect); numbers not named as constants.
- ii. A function
price(seats)that returns the price (with the Rs 200 off for 4 or more) and has a docstring, with the numbers as constants. - ii. Tests under
if __name__ == "__main__":, at the edge (3 and 4 seats). - iii. A program that imports jeep, adds up the returned prices itself and prints Rs 2400, Rs 5800 and Total 8200.
A simplified map of the trails between villages in Manang, as a dictionary of neighbors:
trails = { "Chame": ["Bhratang", "Timang"], "Timang": ["Chame"], "Bhratang": ["Chame", "Upper Pisang", "Lower Pisang"], "Upper Pisang": ["Bhratang", "Ghyaru"], "Lower Pisang": ["Bhratang", "Humde"], "Ghyaru": ["Upper Pisang", "Ngawal"], "Ngawal": ["Ghyaru", "Braka"], "Humde": ["Lower Pisang", "Braka"], "Braka": ["Ngawal", "Humde", "Manang"], "Manang": ["Braka"],}- i.Do a breadth-first search from Chame by hand. Write a row for each visit: the village visited, its steps, the new villages added (with their steps), and the list still to visit. (2)
- ii.What is the fewest number of trails from Chame to Manang? Write the route, from the notes of where each village was first reached from. (1)
- iii.Asmita wants to see the view from Ghyaru on the way. What is the fewest number of trails from Chame to Manang through Ghyaru, and how many more is that than the fewest? (1)
Answer
i.
| Visit | Steps | New villages (steps) | List still to visit, front first |
|---|---|---|---|
| (start) | Chame (0) | Chame | |
| Chame | 0 | Bhratang, Timang (1) | Bhratang, Timang |
| Bhratang | 1 | Upper Pisang, Lower Pisang (2) | Timang, Upper Pisang, Lower Pisang |
| Timang | 1 | none | Upper Pisang, Lower Pisang |
| Upper Pisang | 2 | Ghyaru (3) | Lower Pisang, Ghyaru |
| Lower Pisang | 2 | Humde (3) | Ghyaru, Humde |
| Ghyaru | 3 | Ngawal (4) | Humde, Ngawal |
| Humde | 3 | Braka (4) | Ngawal, Braka |
| Ngawal | 4 | none (Braka already seen) | Braka |
| Braka | 4 | Manang (5) | Manang |
| Manang | 5 | none | (empty) |
- ii.5 trails. Manang was first reached from Braka, Braka from Humde, Humde from Lower Pisang, Lower Pisang from Bhratang, Bhratang from Chame: Chame → Bhratang → Lower Pisang → Humde → Braka → Manang.
- iii.The fewest from Chame to Ghyaru is 3 (from the table), and from Ghyaru to Manang 3 (Ghyaru → Ngawal → Braka → Manang), so 6 trails: Chame → Bhratang → Upper Pisang → Ghyaru → Ngawal → Braka → Manang, one more than the fewest (5).
Marking scheme
- i. The visits in the right order: Chame; Bhratang, Timang; Upper Pisang, Lower Pisang; Ghyaru, Humde; Ngawal, Braka; Manang.
- i. Correct steps (1, 2, 3, 4, 5 as above) and the list kept as a queue (new villages at the back).
- ii. 5 trails, Chame → Bhratang → Lower Pisang → Humde → Braka → Manang.
- iii. 6 trails (3 to Ghyaru and 3 from it), one more than the fewest.
A trekking agency keeps its guides and treks in two tables.
guides
| guide_id | name | district |
|---|---|---|
| 1 | Pasang Sherpa | Solukhumbu |
| 2 | Kamala Gurung | Lamjung |
| 3 | Mingma Lama | Solukhumbu |
| 4 | Hari Thakali | Mustang |
| 5 | Sunita Tamang | Rasuwa |
treks
| trek_id | guide_id | route | days | trekkers |
|---|---|---|---|---|
| 1 | 1 | Everest Base Camp | 12 | 4 |
| 2 | 4 | Upper Mustang | 10 | 2 |
| 3 | 2 | Annapurna Circuit | 14 | 6 |
| 4 | 3 | Everest Base Camp | 12 | 3 |
| 5 | 1 | Gokyo Lakes | 9 | 5 |
| 6 | 2 | Mardi Himal | 5 | 8 |
| 7 | 4 | Annapurna Circuit | 14 | 3 |
| 8 | 3 | Gokyo Lakes | 9 | 1 |
Write what each query gives.
- i.
SELECT name FROM guides WHERE district = 'Solukhumbu';(1) - ii.
SELECT route, SUM(trekkers) FROM treks GROUP BY route ORDER BY SUM(trekkers) DESC;(1) - iii.(1)
SELECT guides.name, treks.trekkersFROM treksJOIN guides ON treks.guide_id = guides.guide_idWHERE treks.route = 'Annapurna Circuit'ORDER BY treks.trekkers DESC;iv. Hari Thakali leaves the agency, and the owner types DELETE FROM guides WHERE guide_id = 4;. What would be wrong with the data if the database deleted his row? What rule makes it refuse? (1)
Answer
- i.The two guides from Solukhumbu: Pasang Sherpa and Mingma Lama.
- ii.One row per route, with its trekkers added up, the most first:
| route | SUM(trekkers) |
|---|---|
| Annapurna Circuit | 9 |
| Mardi Himal | 8 |
| Everest Base Camp | 7 |
| Gokyo Lakes | 6 |
| Upper Mustang | 2 |
iii. The two Annapurna Circuit treks, each joined to its guide’s row, most trekkers first:
| name | trekkers |
|---|---|
| Kamala Gurung | 6 |
| Hari Thakali | 3 |
iv. Treks 2 and 7 still have guide_id 4. With his row gone they would point to a guide who doesn’t exist, and the JOIN in iii would quietly lose the treks he led. A foreign key rule on treks.guide_id makes the database refuse to delete a guide while any trek still points to him: keeping every foreign key pointing to a real row is referential integrity. (The treks must keep their history, so his row should stay.)
Marking scheme
- i. Pasang Sherpa and Mingma Lama.
- ii. Annapurna Circuit 9, Mardi Himal 8, Everest Base Camp 7, Gokyo Lakes 6, Upper Mustang 2, in that order.
- iii. Kamala Gurung 6, then Hari Thakali 3.
- iv. Treks 2 and 7 would point to a guide who doesn’t exist; a foreign key rule (referential integrity) refuses the delete.
A trekking agency lists the groups it sent out in one week, with the number of people and days in each, in groups.csv:
groups.csv
trek,people,daysMardi Himal,4,5Annapurna Circuit,2,14Mardi Himal,3,6Upper Mustang,2,12Annapurna Circuit,3,13Mardi Himal,20,5Annapurna Circuit,4,15Upper Mustang,3,10Mardi Himal,2,6It runs this program:
import pandas as pd groups = pd.read_csv("groups.csv")print(groups.shape)summary = groups.groupby("trek")["people"].agg(["count", "mean", "median"])print(summary)groups.sort_values("days")print(groups.head(3))- i.What does line 4 print, and what does it mean? (1)
- ii.Write the table that line 6 prints. (1)
- iii.For Mardi Himal, which describes a typical group better, the mean or the median? Why? (1)
- iv.Line 7 sorts by days, but line 8 shows the rows in their first order. Why? How should line 7 be written? (1)
Answer
- i.
(9, 3): the table has 9 rows (groups) and 3 columns (trek, people, days). - ii.One row per trek, in alphabetical order, with the number of groups and the mean and median of their sizes:
Output
count mean mediantrek Annapurna Circuit 3 3.00 3.0Mardi Himal 4 7.25 3.5Upper Mustang 2 2.50 2.5- iii.The median, 3.5. Mardi Himal’s groups had 4, 3, 20 and 2 people: one group of 20 (perhaps a school trip) pulls the mean up to 7.25, larger than three of the four groups. The median, the middle of the sizes in order, isn’t moved by one unusual value.
- iv.
sort_valuesgives back a new, sorted table and leaves groups unchanged; line 7 throws the new one away. It should keep it:groups = groups.sort_values("days")(or print it straight away).
Marking scheme
- i. (9, 3): 9 rows and 3 columns.
- ii. Annapurna Circuit 3, 3.00, 3.0; Mardi Himal 4, 7.25, 3.5; Upper Mustang 2, 2.50, 2.5.
- iii. The median, because the one group of 20 pulls the mean up.
- iv. sort_values returns a new table and leaves the original unchanged;
groups = groups.sort_values("days").
Read the given passage and answer the questions that follow.
Trekking agencies used to queue at an office in Pokhara for their trekkers’ permits. Now they apply on a permit website, in a browser, from anywhere. The company that made the website runs its own program on virtual machines that it rents in a data center abroad, paying each month for what it uses, and it rents more machines for the busy weeks of October.
- i.For the trekking agencies, which kind of cloud service is the permit website: IaaS, PaaS or SaaS? Give a reason. (1)
- ii.Which kind does the company rent? What must it look after itself? (1)
- iii.What is latency? Why is it a little higher because the data center is abroad? (1)
- iv.Write one risk of running the permits this way. (1)
Answer
- i.Software as a service (SaaS): the agencies use a finished program in a browser, and look after only their own data; everything else is done for them.
- ii.Infrastructure as a service (IaaS): it rents computers (virtual machines) with storage and a network, and looks after the operating system, its programs and the data itself.
- iii.Latency is the time a message takes to arrive. Messages travel through fiber-optic cables at a fixed speed, so the farther away the data center, the longer each message takes there and back. Only bringing the computer closer makes it shorter.
- iv.Any one: if the internet or the company’s service goes down, no permits can be issued anywhere (everything depends on one company); the data of every trekker is kept by a company abroad, under another country’s laws; costs can grow, and moving to another company later is hard.
Marking scheme
- i. SaaS, because the agencies use a finished program in a browser.
- ii. IaaS; it looks after the operating system, its program and its data.
- iii. The time a message takes to arrive, which grows with the distance it travels.
- iv. One risk: an outage of the internet or the company stops all permits; data kept abroad; cost or being tied to one company.
Practical exam
25 marksMarked 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.
Rafting trips. A rafting company charges, for each person: Rs 2,500 on the Trishuli, Rs 3,500 on the Seti and Rs 4,500 on the Bhote Koshi. A group of 6 or more gets 10% off. It runs no trips on any other river.
At the computer, make two files in one folder:
- rafting.py, a module with a function
trip_price(river, people)that returns the price of a group’s trip in rupees, and raises a ValueError for a river the company doesn’t run, with tests at the end that run only when rafting.py itself is run; - main.py, which imports it, asks for the river and the number of people for 3 groups, and prints each group’s price, or “No trips on that river” for a river the company doesn’t run.
Run rafting.py, then main.py with Trishuli and 4 people, Bhote Koshi and 6, and Arun and 3. (On this page, try your function in the box below first.)
# Try your trip_price function heredef trip_price(river, people): return 0Answer
rafting.py
"""The rafting company's prices.""" PER_PERSON = {"Trishuli": 2500, "Seti": 3500, "Bhote Koshi": 4500} # rupeesGROUP = 6 # this many people or more get the discountGROUP_OFF = 0.10 # 10% off def trip_price(river, people): """The price in rupees of a trip for this many people on this river. Raises ValueError for a river the company doesn't run.""" if river not in PER_PERSON: raise ValueError("No trips on " + river) price = PER_PERSON[river] * people if people >= GROUP: price = price * (1 - GROUP_OFF) return round(price) if __name__ == "__main__": # tests: either side of the group discount, and a river it doesn't run print(trip_price("Seti", 5), "should be 17500") print(trip_price("Seti", 6), "should be 18900") try: trip_price("Arun", 2) print("No error: wrong!") except ValueError: print("Arun refused, as it should be")Run by itself, rafting.py prints its tests:
Output
17500 should be 1750018900 should be 18900Arun refused, as it should bemain.py
import rafting for group in range(1, 4): river = input(f"Group {group}: which river? ") people = int(input("How many people? ")) try: print("Price: Rs", rafting.trip_price(river, people)) except ValueError: print("No trips on that river")Output
Group 1: which river? TrishuliHow many people? 4Price: Rs 10000Group 2: which river? Bhote KoshiHow many people? 6Price: Rs 24300Group 3: which river? ArunHow many people? 3No trips on that river4 × Rs 2,500 = Rs 10,000. 6 × Rs 4,500 = Rs 27,000, less 10% = Rs 24,300. Any clear names and messages are fine; a dictionary or if/elif for the prices are both right.
Marking scheme
- rafting.py has a function
trip_price(river, people)that returns the price (it doesn’t print it). - The right price per person for each of the three rivers.
- 10% off for a group of 6 or more (
people >= 6). - Raises a ValueError for a river the company doesn’t run.
- Tests under
if __name__ == "__main__":, at the edge of the discount (5 and 6) and with an unknown river, which don’t run when it is imported. - main.py imports rafting, asks with
input()andint(input(…)), and catches the ValueError withtry/except ValueError. - Runs both in front of the teacher: Rs 10,000, Rs 24,300 and “No trips on that river”.
Homestay bookings. A homestay network keeps its homestays and their bookings in a database. This program makes it and gives you ask(), which prints what a query gives. Run it, then add one ask() for each question:
- i.Every booking of 4 nights or more: the booking_id, the guests’ country and the nights, the most nights first. (2)
- ii.The homestays with more than 5 nights booked in all: the homestay_id and its nights. (2)
- iii.The villages where guests from Japan stayed, with the number of guests in each booking, the villages in alphabetical order. (2)
import sqlite3 db = sqlite3.connect(":memory:")db.execute("CREATE TABLE homestays (homestay_id INTEGER PRIMARY KEY, owner TEXT, village TEXT, district TEXT, rooms INTEGER)")db.execute("""CREATE TABLE bookings (booking_id INTEGER PRIMARY KEY, homestay_id INTEGER REFERENCES homestays(homestay_id), country TEXT, guests INTEGER, nights INTEGER)""")db.executemany("INSERT INTO homestays VALUES (?, ?, ?, ?, ?)", [ (1, "Bishnu Gurung", "Sirubari", "Syangja", 3), (2, "Maya Gurung", "Ghale Gaun", "Lamjung", 4), (3, "Tika Magar", "Bandipur", "Tanahun", 2), (4, "Lalita Tamang", "Briddim", "Rasuwa", 3), (5, "Phurba Sherpa", "Thame", "Solukhumbu", 2),])db.executemany("INSERT INTO bookings VALUES (?, ?, ?, ?, ?)", [ (1, 2, "Japan", 2, 3), (2, 1, "Nepal", 4, 1), (3, 4, "Germany", 2, 5), (4, 2, "India", 3, 2), (5, 3, "Japan", 1, 4), (6, 2, "Germany", 2, 6), (7, 5, "Japan", 2, 2), (8, 4, "Nepal", 5, 1), (9, 1, "India", 2, 2), (10, 4, "Japan", 3, 3),]) 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 homestays")Answer
The same program, with these three lines in place of the last one:
import sqlite3 db = sqlite3.connect(":memory:")db.execute("CREATE TABLE homestays (homestay_id INTEGER PRIMARY KEY, owner TEXT, village TEXT, district TEXT, rooms INTEGER)")db.execute("""CREATE TABLE bookings (booking_id INTEGER PRIMARY KEY, homestay_id INTEGER REFERENCES homestays(homestay_id), country TEXT, guests INTEGER, nights INTEGER)""")db.executemany("INSERT INTO homestays VALUES (?, ?, ?, ?, ?)", [ (1, "Bishnu Gurung", "Sirubari", "Syangja", 3), (2, "Maya Gurung", "Ghale Gaun", "Lamjung", 4), (3, "Tika Magar", "Bandipur", "Tanahun", 2), (4, "Lalita Tamang", "Briddim", "Rasuwa", 3), (5, "Phurba Sherpa", "Thame", "Solukhumbu", 2),])db.executemany("INSERT INTO bookings VALUES (?, ?, ?, ?, ?)", [ (1, 2, "Japan", 2, 3), (2, 1, "Nepal", 4, 1), (3, 4, "Germany", 2, 5), (4, 2, "India", 3, 2), (5, 3, "Japan", 1, 4), (6, 2, "Germany", 2, 6), (7, 5, "Japan", 2, 2), (8, 4, "Nepal", 5, 1), (9, 1, "India", 2, 2), (10, 4, "Japan", 3, 3),]) def ask(sql): """Print each row the query gives, then a blank line.""" for row in db.execute(sql): print(row) print() ask("SELECT booking_id, country, nights FROM bookings WHERE nights >= 4 ORDER BY nights DESC")ask("SELECT homestay_id, SUM(nights) FROM bookings GROUP BY homestay_id HAVING SUM(nights) > 5")ask("""SELECT homestays.village, bookings.guests FROM bookings JOIN homestays ON bookings.homestay_id = homestays.homestay_id WHERE bookings.country = 'Japan' ORDER BY homestays.village""")Output
(6, 'Germany', 6)(3, 'Germany', 5)(5, 'Japan', 4) (2, 11)(4, 9) ('Bandipur', 1)('Briddim', 3)('Ghale Gaun', 2)('Thame', 2)(The bookings are made up for this paper.)
Marking scheme
- i.
WHERE nights >= 4, selecting booking_id, country and nights. - i.
ORDER BY nights DESC, giving bookings 6, 3 and 5. - ii.
GROUP BY homestay_idwithSUM(nights). - ii.
HAVING SUM(nights) > 5(not WHERE), giving homestays 2 (11 nights) and 4 (9 nights). - iii. A JOIN of bookings and homestays
ON bookings.homestay_id = homestays.homestay_id. - iii. Only Japan, ordered by village: Bandipur 1, Briddim 3, Ghale Gaun 2, Thame 2.
Fewest flights. The dictionary below shows which airports have flights between them this season (made up for practice; real flights change). Manang’s airstrip has no flights this season. Write a function fewest_flights(graph, start, goal) that returns the airports on a route with the fewest flights, as a list, or None if there is no way. Then print the route and the number of flights:
- from Jomsom to Simikot;
- from Lukla to Jumla;
- from Tumlingtar to Manang (print “no flights” if there is no way).
flights = { "Kathmandu": ["Pokhara", "Lukla", "Nepalgunj", "Biratnagar", "Tumlingtar", "Bharatpur"], "Pokhara": ["Kathmandu", "Jomsom", "Nepalgunj"], "Jomsom": ["Pokhara"], "Lukla": ["Kathmandu"], "Nepalgunj": ["Kathmandu", "Pokhara", "Simikot", "Jumla", "Surkhet"], "Simikot": ["Nepalgunj"], "Jumla": ["Nepalgunj", "Surkhet"], "Surkhet": ["Nepalgunj", "Jumla"], "Biratnagar": ["Kathmandu", "Tumlingtar"], "Tumlingtar": ["Kathmandu", "Biratnagar"], "Bharatpur": ["Kathmandu"], "Manang": [],} # Write fewest_flights hereAnswer
flights = { "Kathmandu": ["Pokhara", "Lukla", "Nepalgunj", "Biratnagar", "Tumlingtar", "Bharatpur"], "Pokhara": ["Kathmandu", "Jomsom", "Nepalgunj"], "Jomsom": ["Pokhara"], "Lukla": ["Kathmandu"], "Nepalgunj": ["Kathmandu", "Pokhara", "Simikot", "Jumla", "Surkhet"], "Simikot": ["Nepalgunj"], "Jumla": ["Nepalgunj", "Surkhet"], "Surkhet": ["Nepalgunj", "Jumla"], "Biratnagar": ["Kathmandu", "Tumlingtar"], "Tumlingtar": ["Kathmandu", "Biratnagar"], "Bharatpur": ["Kathmandu"], "Manang": [],} def fewest_flights(graph, start, goal): """The airports on a route with the fewest flights, or None.""" came_from = {start: None} to_visit = [start] while len(to_visit) > 0: airport = to_visit.pop(0) if airport == goal: route = [] while airport is not None: route.append(airport) airport = came_from[airport] route.reverse() return route for nxt in graph[airport]: if nxt not in came_from: came_from[nxt] = airport to_visit.append(nxt) return None for start, goal in [("Jomsom", "Simikot"), ("Lukla", "Jumla"), ("Tumlingtar", "Manang")]: route = fewest_flights(flights, start, goal) if route is None: print(start, "to", goal + ": no flights") else: print(start, "to", goal + ":", " → ".join(route), "(" + str(len(route) - 1), "flights)")Output
Jomsom to Simikot: Jomsom → Pokhara → Nepalgunj → Simikot (3 flights)Lukla to Jumla: Lukla → Kathmandu → Nepalgunj → Jumla (3 flights)Tumlingtar to Manang: no flightsJomsom to Simikot through Kathmandu would be 4 flights. Any route with the fewest flights is right.
Marking scheme
- A function with the graph, start and goal, that starts its queue (and its notes) with the start.
- A queue: takes from the front with
pop(0)and adds to the back withappend(). - Marks an airport 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 flights (one less than the airports): 3 and 3.
- Returns
Nonewhen the queue runs out, and the program prints “no flights” for Manang.
Viva. The teacher asks each student about their own work, for example:
- i.In rafting.py, why does
trip_priceraise an error for a river it doesn’t know, instead of returning 0? What does main.py do about it? (2) - ii.In your first query, why did you write
nights >= 4and notnights > 4? Why is'Japan'in quotes in your third query, but 4 isn’t? (2) - iii.In
fewest_flights, what doescame_fromhold? Why does the route come out backwards, so that you have to reverse it? (2)
Answer
- i.A price of 0 would look like a real answer: main.py could print “Price: Rs 0” and a group might think the trip is free. An error can’t be mistaken for a price, and the caller must decide what to do. main.py catches it with
try/except ValueErrorand prints “No trips on that river”, so the program goes on to the next group instead of stopping. - ii.“4 nights or more” includes 4:
> 4would leave out booking 5, of exactly 4 nights. That is the edge, where mistakes hide.'Japan'is text, which SQL writes in quotes; nights is an INTEGER field, and numbers are written without quotes. - iii.Its keys are the airports seen so far, and each value is the airport it was first reached from. Following these notes from the goal leads back one airport at a time to the start (whose note is None), so the route is collected goal first;
reverse()puts the start first.
Marking scheme
- i. 0 could be taken for a real price; an error can’t, and the caller decides.
- i. main.py catches it with
try/except ValueErrorand prints a message, then carries on. - ii.
>= 4includes the bookings of exactly 4 nights (booking 5), which> 4leaves out. - ii. Text is written in quotes in SQL; numbers (INTEGER fields) aren’t.
- iii.
came_fromholds each airport seen, with the airport it was first reached from. - iii. The notes are followed back from the goal, so the airports come goal first; reversing puts the start first.
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.
