Fictional sample data for this chapter
Download only the files named in the relevant book challenge. Some programs write to the current working folder.
Challenge hints & worked solutions
CHALLENGE 071 · BUILD IT
Write a Learning Journal
Create a small digital journal. The user types one line about something they learned; your program saves a title and the entry to journal.txt in the working folder. Make clear that write mode replaces any previous copy.
Reveal a hint (from the printed book)
Use Path("journal.txt").write_text(...) or open(..., "w"). Reject empty input before writing; w mode replaces existing content.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 71 - Write a Learning Journal
from pathlib import Path
entry = input("Journal entry: ").strip()
if entry:
Path("journal.txt").write_text(
"LEARNING JOURNAL\n" + entry + "\n", encoding="utf-8"
)
print("Saved to journal.txt")
else:
print("Nothing saved: entry was blank.")
Save this Python file ↓CHALLENGE 072 · IMPROVE IT
Read Your Journal
A basic journal reader prints the entire file. Improve it to search for a keyword and display only matching journal entries with their original entry numbers. Use the sample journal data shown below, or type your own file with the same layout.
Reveal a hint (from the printed book)
Skip the first heading line. Use enumerate(entries, 1) for original entry numbers and compare keyword.casefold() with each entry.casefold().
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 72 - Read Your Journal
from pathlib import Path
path = Path("journal.txt")
if not path.exists():
print("Journal file not found.")
else:
lines = path.read_text(encoding="utf-8").splitlines()
entries = [line.strip() for line in lines[1:] if line.strip()]
keyword = input("Search term: ").strip().casefold()
matches = [(number, text) for number, text in
enumerate(entries, 1) if keyword and keyword in text.casefold()]
if matches:
for number, text in matches:
print(f"{number}. {text}")
else:
print("No matching entries.")
Save this Python file ↓CHALLENGE 073 · BUILD IT
Add to the To-Do List
Build a to-do-list program that stores each new task on its own line in tasks.txt. A second run must add another task rather than deleting the previous ones.
Reveal a hint (from the printed book)
Choose mode "a" (append), not "w" (replace). A newline after each task makes later reading much easier.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 73 - Add to the To-Do List
from pathlib import Path
task = input("New task: ").strip()
if not task:
print("Nothing added: task was blank.")
else:
with Path("tasks.txt").open("a", encoding="utf-8") as file:
file.write(task + "\n")
print("Task added.")
Save this Python file ↓CHALLENGE 074 · BUILD IT
Count the Log Entries
An activity log contains a mixture of real entries, empty lines and spaces. Count only the non-empty entries. Create activity_log.txt using the short sample below.
Reveal a hint (from the printed book)
The expression bool(line.strip()) is False for a line containing only whitespace.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 74 - Count the Log Entries
from pathlib import Path
path = Path("activity_log.txt")
if not path.exists():
print("Activity log not found.")
else:
lines = path.read_text(encoding="utf-8").splitlines()
count = sum(1 for line in lines if line.strip())
print(f"Entries: {count}")
Save this Python file ↓CHALLENGE 075 · SOLVE IT
Find Positive Feedback
Read a fictional feedback file and count how many non-blank entries contain at least one of these words: helpful, clear, friendly. An entry containing two of the words still counts only once.
Reveal a hint (from the printed book)
Use any(keyword in entry.casefold() for keyword in keywords); append each matching entry only once.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 75 - Find Positive Feedback
from pathlib import Path
keywords = ("helpful", "clear", "friendly")
path = Path("feedback.txt")
if not path.exists():
print("Feedback file not found.")
else:
entries = [line.strip() for line in
path.read_text(encoding="utf-8").splitlines() if line.strip()]
matches = [entry for entry in entries
if any(word in entry.casefold() for word in keywords)]
print(f"Positive entries: {len(matches)}")
Save this Python file ↓CHALLENGE 076 · DEBUG IT
Create a Scores CSV
The provided starter writes three scores using semicolons between fields. A normal Python csv.reader expects commas and reads each entire line as a single field. Fix the writer and verify the CSV structure.
Reveal a hint (from the printed book)
Remove delimiter=";". csv.writer uses commas by default. Use newline="" and re-read with csv.reader.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 76 - Create a Scores CSV
import csv
scores = [("Alex", 84), ("Bea", 91), ("Chris", 76)]
with open("scores.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["name", "score"])
writer.writerows(scores)
with open("scores.csv", newline="", encoding="utf-8") as file:
rows = list(csv.reader(file))
print(f"Records: {len(rows) - 1}")
print(f"First: {rows[1][0]} = {rows[1][1]}")
Save this Python file ↓CHALLENGE 077 · IMPROVE IT
Read the Reading Log
Improve a reading-log loader. Some CSV rows have a missing duration, a non-numeric duration or no title. Treat blank file lines as ignorable and skip malformed records without crashing.
Reveal a hint (from the printed book)
Strip text before validation. Catch ValueError for a bad number; missing fields also need explicit checks. Ignore physically empty CSV rows.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 77 - Read the Reading Log
import csv
records = []
skipped = 0
with open("reading_log.csv", newline="", encoding="utf-8") as file:
for row in csv.DictReader(file):
title = (row.get("title") or "").strip()
duration = (row.get("minutes") or "").strip()
if not title or not duration:
skipped += 1
continue
try:
minutes = int(duration)
except ValueError:
skipped += 1
continue
if minutes <= 0:
skipped += 1
continue
records.append((title, minutes))
print(f"Valid sessions: {len(records)}")
print(f"Total minutes: {sum(minutes for _, minutes in records)}")
print(f"Skipped rows: {skipped}")
Save this Python file ↓CHALLENGE 078 · SOLVE IT
Find Books by Genre
Search a small catalogue of six fictional books. Ask for a genre, find matching records and show no more than the two highest-rated titles. Use the exact CSV sample below.
Reveal a hint (from the printed book)
Filter records first, then sort by a numeric rating in descending order. Only take the first two sorted records.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 78 - Find Books by Genre
import csv
books = []
with open("books.csv", newline="", encoding="utf-8") as file:
for row in csv.DictReader(file):
books.append((row["title"], row["genre"], float(row["rating"])))
wanted = input("Genre: ").strip().casefold()
results = [book for book in books if book[1].casefold() == wanted]
results.sort(key=lambda book: (-book[2], book[0]))
for title, genre, rating in results[:2]:
print(f"{title}: {rating:.1f}")
if not results:
print("No books in that genre.")
Save this Python file ↓CHALLENGE 079 · BUILD IT
What If the File Is Missing?
A simple file reader crashes if the chosen filename cannot be found. Improve it so a user receives a useful message for a missing file, while normal files can still be opened.
Reveal a hint (from the printed book)
Place the file-reading statement inside try; handle FileNotFoundError specifically. A folder path has a different error.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 79 - What If the File Is Missing?
from pathlib import Path
name = input("File to read: ").strip()
try:
text = Path(name).read_text(encoding="utf-8")
except FileNotFoundError:
print(f"File not found: {name}")
except IsADirectoryError:
print("Please choose a file, not a folder.")
else:
print(f"Characters: {len(text)}")
Save this Python file ↓CHALLENGE 080 · CHALLENGE YOURSELF
Build a Study Tracker
Build a compact study tracker that saves each study session (subject and positive whole-number minutes) to a CSV file. A future run must remember previous sessions and display total minutes for every subject.
Reveal a hint (from the printed book)
Use append mode and test whether the CSV file already exists. Read it all back into a dictionary of per-subject totals.
Reveal a worked Python solution
One possible implementation. Check the book's tests and try an extension after comparing.
# Challenge 80 - Build a Study Tracker
import csv
from pathlib import Path
path = Path("study_sessions.csv")
subject = input("Subject: ").strip()
try:
minutes = int(input("Minutes: "))
except ValueError:
print("Minutes must be a whole number.")
else:
if not subject or minutes <= 0:
print("Subject and positive minutes are required.")
else:
need_header = not path.exists() or path.stat().st_size == 0
with path.open("a", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
if need_header:
writer.writerow(["subject", "minutes"])
writer.writerow([subject, minutes])
totals = {}
with path.open(newline="", encoding="utf-8") as file:
for row in csv.DictReader(file):
key = row["subject"].strip()
totals[key] = totals.get(key, 0) + int(row["minutes"])
for key in sorted(totals):
print(f"{key}: {totals[key]} minutes")
Save this Python file ↓