Skip to content

Commit 96cf7ee

Browse files
committed
setup and clarifications for lesson 2 exercise 2
1 parent 5333d33 commit 96cf7ee

File tree

12 files changed

+132
-71
lines changed

12 files changed

+132
-71
lines changed

Lesson2-Exercise2-Delete/solution/2.2-Resolution.md

Lines changed: 0 additions & 26 deletions
This file was deleted.

Lesson2-Exercise2-Delete/solution/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1+
import uuid
2+
13
class ExpenseTracker:
24
def __init__(self):
35
self.expenses = []
4-
self.total = 0
5-
self._next_id = 1
66

7-
def add_expense(self, amount, category):
8-
expense_id = self._next_id
7+
@property
8+
def total(self):
9+
return sum([e["amount"] for e in self.expenses])
10+
11+
# ------------- Create -------------
12+
def add_expense(self, amount, category=None):
13+
expense_id = str(uuid.uuid4())
914
expense = {"id": expense_id, "amount": amount, "category": category}
1015
self.expenses.append(expense)
11-
self.total += amount
12-
self._next_id += 1
1316
return expense_id
1417

18+
# ------------- Read -------------
19+
def list_expenses_by_category(self, category):
20+
return [e for e in self.expenses if e.get("category") == category]
21+
1522
def get_expense(self, expense_id):
1623
return next((e for e in self.expenses if e["id"] == expense_id), None)
1724

25+
# ------------- Delete -------------
1826
def delete_expense(self, expense_id):
19-
# Locate the expense to ensure it exists before removal
2027
expense_to_delete = self.get_expense(expense_id)
21-
if expense_to_delete:
22-
# Subtract the amount from total to maintain integrity
23-
self.total -= expense_to_delete["amount"]
24-
self.expenses.remove(expense_to_delete)
28+
self.expenses.remove(expense_to_delete)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Lesson2-Exercise2-Delete/solution/tests/test_tracker.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@
55
def tracker():
66
return ExpenseTracker()
77

8+
def test_add_expense_stores_amount(tracker):
9+
tracker.add_expense(50)
10+
assert tracker.expenses[0]['amount'] == 50
11+
12+
def test_add_expense_with_category(tracker):
13+
tracker.add_expense(10, category="food")
14+
assert tracker.expenses[0]['category'] == "food"
15+
16+
def test_list_expenses_by_category_returns_only_requested_category(tracker):
17+
# Arrange
18+
tracker.add_expense(10.00, category="food")
19+
tracker.add_expense(20.00, category="travel")
20+
tracker.add_expense(5.00, category="food")
21+
22+
# Act
23+
results = tracker.list_expenses_by_category("food")
24+
25+
# Assert
26+
assert isinstance(results, list)
27+
# Only "food" expenses should be returned
28+
assert all(item["category"] == "food" for item in results)
29+
# Amounts for the returned items should match the two "food" entries
30+
assert sorted(item["amount"] for item in results) == [5.00, 10.00]
31+
832
def test_delete_expense(tracker):
933
# Arrange: Add an expense to delete
1034
expense_id = tracker.add_expense(20, "entertainment")

Lesson2-Exercise2-Delete/starter/2.2-Description.md

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1-
# Purpose of this Folder
1+
# Exercise: Deleting Expenses
22

3-
This folder should contain the starter code and instructions for the exercise.
3+
## Goal
4+
5+
Complete the TDD cycle to implement a `delete_expense` method.
6+
7+
Our expense tracker has Create and Read functionality, now let's add Delete!
8+
9+
Files to work with:
10+
11+
- `tests/test_tracker.py`
12+
- `src/tracker.py`
13+
14+
## UUIDs
15+
16+
In order to locate a specific expense for deletion, each expense needs to have an identifier (ID). We have already implemented this using Python's built-in `uuid` module.
17+
18+
## Instructions
19+
20+
### Step 1 - Review the Existing Code
21+
22+
The provided code has some more advanced techniques that you may not have encountered before.
23+
24+
`test_tracker.py` uses a pytest **fixture** to avoid repetitive setup code. You do not need to understand fully how this works, but read through the code to see how the `tracker` variable is passed in as an argument to our tests.
25+
26+
`tracker.py` uses the Python `@property` decorator. This allows us to calculate a `total` value whenever this attribute is requested. To external code, the `total` method is indistinguishable from a regular attribute of an `ExpenseTracker` instance.
27+
28+
### Step 2 - Red: Write the Test for Deletion
29+
30+
In `test_tracker.py`, implement `test_delete_expense(tracker)`:
31+
- The `tracker` has been created using the fixture, so you do not need to instantiate a new `tracker` object
32+
- Add an expense and capture the ID
33+
- Call `tracker.delete_expense(expense_id)`
34+
- Assert `tracker.total` is updated to 0
35+
- Assert `tracker.get_expense(expense_id)` returns `None`
36+
37+
### Step 3 - Green: Implement Deletion
38+
39+
In `tracker.py`, implement the `delete_expense` method. Use the simplest possible code to pass the test.
40+
41+
Note that you do not need to change any logic in order to retrieve the `total`. If an expense has been removed from the `expenses` list, its amount will automatically be deducted from the total.
42+
43+
### (Optional) Step 4 - Refactor
44+
45+
Think about edge cases, such as attempting to delete a non-existent expense. Should this produce an error? Should it return `None`? Guide your (optional) refactor with tests.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1+
import uuid
2+
13
class ExpenseTracker:
24
def __init__(self):
35
self.expenses = []
4-
self.total = 0
5-
self._next_id = 1
66

7-
def add_expense(self, amount, category):
8-
expense_id = self._next_id
9-
expense = {
10-
"id": expense_id,
11-
"amount": amount,
12-
"category": category
13-
}
7+
@property
8+
def total(self):
9+
return sum([e["amount"] for e in self.expenses])
10+
11+
# ------------- Create -------------
12+
def add_expense(self, amount, category=None):
13+
expense_id = str(uuid.uuid4())
14+
expense = {"id": expense_id, "amount": amount, "category": category}
1415
self.expenses.append(expense)
15-
self.total += amount
16-
self._next_id += 1
1716
return expense_id
1817

18+
# ------------- Read -------------
19+
def list_expenses_by_category(self, category):
20+
return [e for e in self.expenses if e.get("category") == category]
21+
1922
def get_expense(self, expense_id):
20-
# Helper to find a specific expense by ID
2123
return next((e for e in self.expenses if e["id"] == expense_id), None)
24+
25+
26+
# ------------- Delete -------------

0 commit comments

Comments
 (0)