Skip to content

Commit 1bb1a91

Browse files
committed
fix banking system
1 parent 6c86d54 commit 1bb1a91

File tree

2 files changed

+104
-50
lines changed

2 files changed

+104
-50
lines changed

13_object_oriented_programming_advanced.ipynb

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -722,52 +722,46 @@
722722
"source": [
723723
"### Exercise: Banking System\n",
724724
"\n",
725-
"In this exercise, we will implement a very simple banking system where there are two different types of accounts: **Salary Accounts** and **Savings Accounts**.\n",
725+
"In this exercise, we will implement a simple banking system where there are two different types of accounts: **Salary Accounts** and **Savings Accounts**.\n",
726726
"\n",
727727
"We assume the following Classes:\n",
728728
"\n",
729729
"**Account**:\n",
730730
"\n",
731-
"An abstract base class representing a generic bank account with attributes `account_number` and `balance` and abstract methods `credit()` and `get_balance()`.\n",
731+
"An abstract base class representing a generic bank account with attributes `account_number` and `balance`, and abstract methods `credit()` and `get_balance()`.\n",
732732
"It should also contain the method `debit()`, which, if funds are sufficient, should subtract a given amount (parameter) from the account balance.\n",
733733
"Method `debit()` should be common for all derived classes.\n",
734+
"When an Account is created it should always be initialized with **balance equal to 0**.\n",
734735
"\n",
735736
"**SalaryAccount**:\n",
736737
"\n",
737738
"A derived class representing a salary account that contains an additional attribute for `tax_rate` and **overrides** methods `credit()` and `get_balance()`.\n",
738-
"Method `credit()` should set the balance as the `gross_salary` after applying the `tax_rate` to it.\n",
739+
"Method `credit()` should update the `balance` with the **net salary**, so after applying taxes.\n",
739740
"Method `get_balance()` should simply return the account balance.\n",
740741
"\n",
741742
"**SavingsAccount**:\n",
742743
"\n",
743-
"A derived class representing a savings account that contains additional attributes for `interest_rate` and the account's `creation_year`, plus **overrides** methods `credit()` and `get_balance()`.\n",
744+
"A derived class representing a savings account that contains an additional attribute for `interest_rate` and **overrides** methods `credit()` and `get_balance()`.\n",
744745
"Method `credit()` should simply add the given amount to the current balance.\n",
745-
"Method `get_balance()` should return the account balance plus interest, based on the `interest_rate` and the `years_passed` from the account's creation.\n",
746+
"Method `get_balance()` should return the account balance including interest.\n",
746747
"\n",
747748
"<div class=\"alert alert-block alert-warning\">\n",
748749
" <h4><b>Question</b></h4>\n",
749750
" Using <strong>abstraction</strong> in Python, create a banking system based on the entities mentioned above.\n",
750751
" <ul>\n",
751752
" <li>\n",
752-
" <strong>Initialize Accounts:</strong>\n",
753-
" <p>Create a Salary Account and a Savings Account with an initial balance of 0 in each.</p>\n",
753+
" <p>Define classes <code>Account</code>, <code>SalaryAccount</code> and <code>SavingsAccount</code>.</p>\n",
754754
" </li>\n",
755755
" <li>\n",
756-
" <strong>Update Salary Account:</strong>\n",
757-
" <p>Add an amount represented by <code>gross_salary</code> to the Salary Account's balance.</p>\n",
756+
" <p>Create an instance of <code>SalaryAccount</code>, passing as parameters any <code>account_number</code> that you like and the <code>tax_rate</code> that is given in the solution function's parameters.</p>\n",
758757
" </li>\n",
759758
" <li>\n",
760-
" <strong>Transfer Funds:</strong>\n",
761-
" <p>Calculate a transfer amount from the Salary Account to the Savings Account using a given <code>savings_percentage</code>. Deduct this amount from the Salary Account and add it to the Savings Account.</p>\n",
759+
" <p>Create an instance of <code>SavingsAccount</code>, passing as parameters any <code>account_number</code> that you like and the <code>interest_rate</code> that is given in the solution function's parameters.</p>\n",
762760
" </li>\n",
763761
" <li>\n",
764-
" <strong>Project Future Balance:</strong>\n",
765-
" <p>Return the projected balance of the Savings Account after a specified number of years, provided by <code>years_passed</code>.</p>\n",
762+
" <p>Return a <strong>list</strong> containing the two instances you created.</p>\n",
766763
" </li>\n",
767764
" </ul>\n",
768-
"\n",
769-
"The solution function should return the balance of the Savings Account after the given number of years.\n",
770-
"\n",
771765
"</div>"
772766
]
773767
},
@@ -790,11 +784,21 @@
790784
"source": [
791785
"%%ipytest\n",
792786
"from abc import ABC, abstractmethod\n",
793-
"from datetime import datetime\n",
794787
"\n",
795-
"def solution_banking_system(tax_rate: float, interest_rate: float, gross_salary: int, savings_precentage: float, years_passed: int) -> float:\n",
796-
" # Write your solution here\n",
797-
" pass"
788+
"def solution_banking_system(tax_rate: float, interest_rate: float) -> list:\n",
789+
" \"\"\"\n",
790+
" Defines abstract class `Account` with attributes `account_number` and `balance`, and methods `credit()`, `get_balance()`, and `debit()`.\n",
791+
" Implements `SalaryAccount` (with `tax_rate`) and `SavingsAccount` (with `interest_rate`) as derived classes, overriding `credit()` and `get_balance()`.\n",
792+
" Creates and returns instances of `SalaryAccount` and `SavingsAccount` in a list.\n",
793+
"\n",
794+
" Args:\n",
795+
" tax_rate (float): Tax rate for SalaryAccount.\n",
796+
" interest_rate (float): Interest rate for SavingsAccount.\n",
797+
" Returns:\n",
798+
" list: Instances of SalaryAccount and SavingsAccount.\n",
799+
" \"\"\"\n",
800+
"\n",
801+
" return"
798802
]
799803
},
800804
{

tutorial/tests/test_13_object_oriented_programming_advanced.py

Lines changed: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import pathlib
22
from abc import ABC, abstractmethod
3-
from datetime import datetime
43

54
import pytest
65
from numpy import average
@@ -91,9 +90,6 @@ def test_child_eye_color(mother_eye_color, father_eye_color, function_to_test):
9190
def reference_banking_system(
9291
tax_rate: float,
9392
interest_rate: float,
94-
gross_salary: int,
95-
savings_precentage: float,
96-
years_passed: int,
9793
) -> float:
9894
class Account(ABC):
9995
def __init__(self, account_number):
@@ -129,49 +125,103 @@ class SavingsAccount(Account):
129125
def __init__(self, account_number, interest_rate):
130126
super().__init__(account_number)
131127
self.interest_rate = interest_rate
132-
self.creation_year = datetime.now().year
133128

134129
def credit(self, amount):
135130
self.balance += amount
136131

137-
def get_balance(self, years_passed):
138-
interest = self.balance * self.interest_rate * years_passed
139-
return self.balance + interest
140-
141-
salary_account = SalaryAccount("SAL-001", tax_rate)
142-
savings_account = SavingsAccount("SAV-001", interest_rate)
143-
144-
salary_account.credit(gross_salary)
132+
def get_balance(self):
133+
return self.balance + self.balance * self.interest_rate
145134

146-
amount_to_transfer = salary_account.get_balance() * savings_precentage
135+
return [
136+
SalaryAccount("SAL-001", tax_rate),
137+
SavingsAccount("SAV-001", interest_rate),
138+
]
147139

148-
salary_account.debit(amount_to_transfer)
149-
savings_account.credit(amount_to_transfer)
150140

151-
return savings_account.get_balance(years_passed)
141+
def validate_banking_system(solution_result):
142+
assert isinstance(solution_result, list), "Solution must return a list."
143+
assert len(solution_result) == 2, "The list must contain exactly two elements."
144+
assert all(
145+
isinstance(item, object) and type(item).__module__ != "builtins"
146+
for item in solution_result
147+
), "Both elements in the list must be instances of custom classes."
148+
assert all(
149+
"Account" in [base.__name__ for base in type(item).__bases__]
150+
for item in solution_result
151+
), "Both elements in the list must inherit from a class named 'Account'."
152+
assert type(solution_result[0]).__name__ == "SalaryAccount", (
153+
"The 1st class should be an instance of 'SalaryAccount'."
154+
)
155+
assert type(solution_result[1]).__name__ == "SavingsAccount", (
156+
"The 2nd class should be an instance of 'SavingsAccount'."
157+
)
158+
# Check the class attributes: SalaryAccount
159+
try:
160+
attrs = list(vars(solution_result[0]))
161+
except TypeError:
162+
raise SubAssertionError from None
163+
assert len(attrs) == 3, "The class 'SalaryAccount' should have 3 attributes."
164+
assert "account_number" in attrs, (
165+
"The class 'SalaryAccount' should have an attribute called 'account_number'."
166+
)
167+
assert "balance" in attrs, (
168+
"The class 'SalaryAccount' should have an attribute called 'balance'."
169+
)
170+
assert "tax_rate" in attrs, (
171+
"The class 'SalaryAccount' should have an attribute called 'tax_rate'."
172+
)
173+
# Check the class attributes: SavingsAccount
174+
try:
175+
attrs = list(vars(solution_result[1]))
176+
except TypeError:
177+
raise SubAssertionError from None
178+
assert len(attrs) == 3, "The class 'SavingsAccount' should have 3 attributes."
179+
assert "account_number" in attrs, (
180+
"The class 'SavingsAccount' should have an attribute called 'account_number'."
181+
)
182+
assert "balance" in attrs, (
183+
"The class 'SavingsAccount' should have an attribute called 'balance'."
184+
)
185+
assert "interest_rate" in attrs, (
186+
"The class 'SavingsAccount' should have an attribute called 'interest_rate'."
187+
)
188+
# Check that each class has the required methods
189+
required_methods = {"credit", "get_balance"}
190+
for item in solution_result:
191+
class_methods = {
192+
method for method in dir(item) if callable(getattr(item, method))
193+
}
194+
assert required_methods.issubset(class_methods), (
195+
f"The class '{type(item).__name__}' must have the methods: {', '.join(required_methods)}."
196+
)
152197

153198

154199
@pytest.mark.parametrize(
155-
"tax_rate, interest_rate, gross_salary, savings_precentage, years_passed",
200+
"tax_rate, interest_rate",
156201
[
157-
(0.20, 0.05, 10000, 0.3, 2),
158-
(0.18, 0.04, 9300, 0.15, 3),
159-
(0.13, 0.07, 8500, 0.18, 4),
202+
(0.20, 0.05),
203+
(0.18, 0.04),
160204
],
161205
)
162206
def test_banking_system(
163207
tax_rate,
164208
interest_rate,
165-
gross_salary,
166-
savings_precentage,
167-
years_passed,
168209
function_to_test,
169210
):
170-
assert function_to_test(
171-
tax_rate, interest_rate, gross_salary, savings_precentage, years_passed
172-
) == reference_banking_system(
173-
tax_rate, interest_rate, gross_salary, savings_precentage, years_passed
174-
)
211+
solution_result = function_to_test(tax_rate, interest_rate)
212+
reference_result = reference_banking_system(tax_rate, interest_rate)
213+
214+
validate_banking_system(solution_result)
215+
216+
amount = 10000
217+
# test SalaryAccount functions
218+
solution_result[0].credit(amount)
219+
reference_result[0].credit(amount)
220+
assert solution_result[0].get_balance() == reference_result[0].get_balance()
221+
# test SavingsAccount functions
222+
solution_result[1].credit(amount)
223+
reference_result[1].credit(amount)
224+
assert solution_result[1].get_balance() == reference_result[1].get_balance()
175225

176226

177227
#
@@ -255,7 +305,7 @@ def test_store_inventory(function_to_test):
255305

256306

257307
#
258-
# Exercise 3: Music Streaming Service
308+
# Exercise 4: Music Streaming Service
259309
#
260310

261311

0 commit comments

Comments
 (0)