Book-matched resources · Chapter 08 · Challenges 071–080

Files and Real-World Data

Try the printed challenge before revealing a hint or the example solution. Starter files are available only where the book supplies one.

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.

Start from a blank Python file; no starter supplied for this challenge.Download worked solution .py ↓
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.

Start from a blank Python file; no starter supplied for this challenge.Download worked solution .py ↓
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.

Start from a blank Python file; no starter supplied for this challenge.Download worked solution .py ↓
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.

Start from a blank Python file; no starter supplied for this challenge.Download worked solution .py ↓
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 ↓