← Back to Python series
🛠
Practice Projects · ★★
OOP · JSON · Search · Sort · CSV export · Functions · OOP · History · Charts · OOP · CSV · Categories · Monthly summary · OOP · JSON · Search · Borrowing system

Project 7 — Contacts Manager · Project 8 — BMI Tracker · Project 9 — Budget Tracker · Project 10 — Library Manager

Build an object-oriented contacts manager with name, phone, email, and tags. Support search, sort, and CSV/JSON export. Track BMI over time with a history log, trend analysis, and an ASCII chart of weight progression. Track income and expenses by category. Generate monthly summaries, spending breakdowns, and savings rate reports — all stored in CSV. Manage a book library with borrow/return tracking, member accounts, overdue detection, and full-text search.

OOPJSONCSVsearchcontactsBMIhistoryASCII chartbudgetcategoriesreportlibraryborrowing
Duration
⏱ 2.5 hours + 2 hours + 2.5 hours + 3 hours
Level
📊 Intermediate Applied
Prerequisite
🎯 Intermediate Week 7
OUTCOME
An OOP contacts app with search, sort, and export features / A BMI tracker with historical trend visualization / A monthly budget tracker with categorized reports / A multi-class library management system with borrowing and search

What you'll learn

  • 1Model a Contact with a dataclass
  • 2Implement ContactBook CRUD operations
  • 3Full-text search across all fields
  • 4Export to CSV and JSON formats
  • 5Calculate BMI and classify the result
  • 6Store daily measurements in JSON
  • 7Plot weight history as an ASCII chart
  • 8Compute 7-day and 30-day trends
  • 9Model transactions with a dataclass
  • 10Categorize income vs. expense transactions
  • 11Compute monthly totals, savings rate, and top expenses
  • 12Load and save all data to CSV
  • 13Design a multi-class system: Book, Member, Library
  • 14Implement borrow/return with due dates
  • 15Detect overdue books and calculate fines
  • 16Full-text search by title, author, or ISBN

Project 7 — Contacts Manager

Build an object-oriented contacts manager with name, phone, email, and tags. Support search, sort, and CSV/JSON export.

Features

  • Contact: name, phones (list), emails (list), tags (list), notes
  • Commands: add, show, edit, delete, search, list, export csv/json, quit
  • Search: case-insensitive full-text across name, email, tags
  • Sort: by name (default), by date added

Sample Session

text
> add
  Name: Alice Johnson
  Phone: 010-1234-5678
  Email: alice@example.com
  Tags (comma-sep): work, python
  Contact saved.

> search python
  [1] Alice Johnson  010-1234-5678  alice@example.com  [work, python]

> export csv contacts.csv
  Exported 1 contacts to contacts.csv

Project 8 — BMI Tracker

Track BMI over time with a history log, trend analysis, and an ASCII chart of weight progression.

BMI Categories

BMICategory
< 18.5Underweight
18.5–24.9Normal
25–29.9Overweight
≥ 30Obese

Sample Output

text
Date: 2024-05-01
Weight (kg): 72.5
Height (cm): 175
BMI: 23.67 — Normal

Weight History (last 7 days):
2024-04-25 |████████████████████ 74.0
2024-04-28 |███████████████████  73.2
2024-05-01 |██████████████████   72.5

Trend: ↓ 1.5 kg over 6 days

Common Mistakes (FAQ)

Q. My BMI comes out as ~7700 — what's wrong?

You used height in centimeters. BMI = kg / m², so convert first: 175 cm → 1.75 m, then bmi = weight / (1.75 ** 2). Dividing by 175² instead of 1.75² is the classic bug.

Q. The program crashes when I type the weight.

float(input()) raises ValueError on non-numeric input. Wrap it in try/except ValueError and re-prompt, so a stray letter or empty line doesn't kill the session.

Q. How do I avoid a ZeroDivisionError?

Guard the height before dividing: if height_m <= 0, ask again. A height of 0 (or a blank that you coerced to 0) is the only way to hit it.

Q. Which category does exactly 25.0 fall into?

Decide your boundaries explicitly: < 18.5 Underweight, 18.5–24.9 Normal, 25–29.9 Overweight, ≥ 30 Obese. Use >= for the lower edge of each band so 25.0 is Overweight, and document it — boundary handling is the most common grading miss.

Q. How do I scale the ASCII bar chart?

Scale each bar to the max weight in the window: bar_len = round(w / max_w * WIDTH), then print '█' * bar_len. Don't scale to a fixed number or short series look identical.

Project 9 — Budget Tracker

Track income and expenses by category. Generate monthly summaries, spending breakdowns, and savings rate reports — all stored in CSV.

Transaction Model

python
@dataclass
class Transaction:
    date: str       # YYYY-MM-DD
    type: str       # income | expense
    amount: float
    category: str   # food, transport, salary, ...
    note: str = 

Commands

  • add <income|expense> <amount> <category> [note]
  • list [--month YYYY-MM]
  • summary [--month YYYY-MM]
  • categories — show all categories with totals
  • export <filename.csv>

Common Mistakes (FAQ)

Q. My totals are off by a cent (e.g. 0.1 + 0.2 = 0.30000000000000004).

Floating-point can't represent money exactly. For display, round(total, 2); for exact arithmetic, store amounts as integer cents (7250 = $72.50) or use decimal.Decimal. Pick one and stay consistent.

Q. Should expenses be negative or positive?

Cleanest: store a positive amount plus a type field ('income'/'expense'), then balance = sum(income) − sum(expense). Mixing signed amounts and a type field double-counts — choose one convention.

Q. How do I filter by month?

With ISO dates (YYYY-MM-DD), a string prefix compare is enough: tx.date[:7] == '2024-05'. No datetime parsing needed for month filtering.

Q. KeyError when totaling a new category.

Use collections.defaultdict(float) (or totals.get(cat, 0)) so a never-seen category starts at 0 instead of raising.

Q. After reloading the CSV, amounts won't add up.

The csv module returns every field as a string — '50' + '20' is '5020'. Convert on load: amount = float(row['amount']). Also write with newline='' on Windows to avoid blank rows.

Project 10 — Library Manager

Manage a book library with borrow/return tracking, member accounts, overdue detection, and full-text search.

Classes

  • Book: isbn, title, author, category, copies_total, copies_available
  • Member: id, name, email, borrowed_books (list of {isbn, due_date})
  • Library: books (dict), members (dict), borrow/return/search methods

Commands

  • book add/list/search <query>
  • member add/list
  • borrow <member_id> <isbn>
  • return <member_id> <isbn>
  • overdue — list all overdue borrowings with fines

Common Mistakes (FAQ)

Q. Should books/members be a list or a dict?

Use a dict keyed by ISBN / member id — lookup is O(1). A list forces an O(n) scan on every borrow/return/search and makes duplicate-key bugs easy to miss.

Q. A book gets borrowed even when no copies are left.

Check copies_available > 0 before decrementing, and return a clear message otherwise. Decrement on borrow, increment on return — and guard return so copies_available never exceeds copies_total.

Q. How do I compute overdue days correctly?

Parse the stored ISO date with date.fromisoformat(due) and compare to date.today(): overdue_days = (date.today() - due).days, overdue when > 0. Don't string-compare dates for arithmetic.

Q. Adding a member/book with an existing id silently overwrites.

dict assignment replaces without warning. Check `if isbn in self.books:` first and reject (or merge copies) so you don't lose the existing record.

Q. json.dump fails on my Book/Member objects.

Objects (and dataclasses) aren't JSON-serializable directly. Convert to plain dicts on save (dataclasses.asdict, or a to_dict()), and reconstruct the objects when loading.

📝 Exercises

Try them yourself first, then open the solution to compare.

Exercise 1

Build the contacts manager

Goal: Implement the contacts manager with all features.

Requirements
  • Contact dataclass with all fields
  • CRUD + search
  • JSON persistence
  • CSV export
Exercise 2

Build the BMI tracker

Goal: Implement measurement recording, BMI calculation, and ASCII chart.

Requirements
  • Record weight+height+date
  • BMI and category
  • JSON history
  • ASCII weight chart
Exercise 3

Build the budget tracker

Goal: Implement all commands with CSV storage.

Requirements
  • Transaction dataclass
  • add/list/summary/categories commands
  • CSV persistence
  • Monthly filtering
Exercise 4

Build the library manager

Goal: Implement Book, Member, and Library classes with all commands.

Requirements
  • 3 classes with clean interfaces
  • Borrow/return with due date tracking
  • Overdue detection (>14 days = overdue)
  • JSON persistence
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗

Comments 0

No comments yet. Be the first.

Only members can comment.

Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy