From b7031bf26c0ae61b528283c4182203a08bcbf090 Mon Sep 17 00:00:00 2001 From: Daniele Fiocca Date: Tue, 21 Oct 2025 19:35:38 +0200 Subject: [PATCH 1/4] first solution 001-budget-app --- projects/001-budget-app/budget.py | 85 ++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/projects/001-budget-app/budget.py b/projects/001-budget-app/budget.py index 933cc4e..d202432 100644 --- a/projects/001-budget-app/budget.py +++ b/projects/001-budget-app/budget.py @@ -1,7 +1,90 @@ class Category: + def __init__(self, name: str): + self.name = name + self.ledger = [] + self.budget = 0 + def check_funds(self, amount: float) -> bool: + return self.budget >= amount + + def deposit(self, amount: float, description: str ="") -> None: + self.ledger.append({"amount": amount, "description": description}) + self.budget += amount + + + def withdraw(self, amount: float, description: str ="") -> bool: + if self.check_funds(amount): + self.ledger.append({"amount": -amount, "description": description}) + self.budget -= amount + return True + return False + + + def get_balance(self) -> float: + return self.budget + + def transfer(self, amount: float, category:"Category") -> bool: + if self.check_funds(amount): + self.withdraw(amount, f"Transfer to {category.name}") + category.deposit(amount, f"Transfer from {self.name}") + return True + return False + + + def __str__(self): + title = f"{self.name:*^30}\n" + items = "" + for entry in self.ledger: + description = entry["description"][:23] + amount = f"{entry['amount']:>7.2f}" + items += f"{description:<23}{amount}\n" + total= f"Total: {self.budget:.2f}" + return title + items + total + + + + +def create_spend_chart(categories): + spending = [] + for category in categories: + total_spent = 0 + for entry in category.ledger: + amount = entry["amount"] + if amount < 0: + total_spent += abs(amount) + spending.append(total_spent) + + total = sum(spending) + percentages = [int((sp / total) * 10) * 10 for sp in spending] + + + + chart = "Percentage spent by category\n" + + for i in range(100, -1, -10): + chart += f"{i:>3}| " + for p in percentages: + if p >= i: + chart += "o " + else: + chart += " " + chart += "\n" + + chart += " -" + "---" * len(categories) + "\n" + + max_len = max(len(c.name) for c in categories) + for i in range(max_len): + chart += " " + for c in categories: + if i Date: Wed, 22 Oct 2025 20:17:20 +0200 Subject: [PATCH 2/4] second solution --- projects/001-budget-app/main.py | 25 ------- projects/001-budget-app/python/main.py | 4 ++ .../001-budget-app/python/source/__init__.py | 0 .../python/source/class_config/__init__.py | 0 .../source/class_config}/budget.py | 41 ----------- .../001-budget-app/python/source/index.py | 70 +++++++++++++++++++ .../python/source/tests/__init__.py | 0 .../{ => python/source/tests}/test_module.py | 10 ++- .../python/source/utility/__init__.py | 0 .../source/utility/create_spend_chart.py | 38 ++++++++++ projects/__init__.py | 0 11 files changed, 119 insertions(+), 69 deletions(-) delete mode 100644 projects/001-budget-app/main.py create mode 100644 projects/001-budget-app/python/source/__init__.py create mode 100644 projects/001-budget-app/python/source/class_config/__init__.py rename projects/001-budget-app/{ => python/source/class_config}/budget.py (56%) create mode 100644 projects/001-budget-app/python/source/index.py create mode 100644 projects/001-budget-app/python/source/tests/__init__.py rename projects/001-budget-app/{ => python/source/tests}/test_module.py (96%) create mode 100644 projects/001-budget-app/python/source/utility/__init__.py create mode 100644 projects/001-budget-app/python/source/utility/create_spend_chart.py create mode 100644 projects/__init__.py diff --git a/projects/001-budget-app/main.py b/projects/001-budget-app/main.py deleted file mode 100644 index b2a7b91..0000000 --- a/projects/001-budget-app/main.py +++ /dev/null @@ -1,25 +0,0 @@ -# This entrypoint file to be used in development. Start by reading README.md -import budget -from budget import create_spend_chart -from unittest import main - -food = budget.Category("Food") -food.deposit(1000, "initial deposit") -food.withdraw(10.15, "groceries") -food.withdraw(15.89, "restaurant and more food for dessert") -print(food.get_balance()) -clothing = budget.Category("Clothing") -food.transfer(50, clothing) -clothing.withdraw(25.55) -clothing.withdraw(100) -auto = budget.Category("Auto") -auto.deposit(1000, "initial deposit") -auto.withdraw(15) - -print(food) -print(clothing) - -print(create_spend_chart([food, clothing, auto])) - -# Run unit tests automatically -main(module='test_module', exit=False) \ No newline at end of file diff --git a/projects/001-budget-app/python/main.py b/projects/001-budget-app/python/main.py index e69de29..8a861c2 100644 --- a/projects/001-budget-app/python/main.py +++ b/projects/001-budget-app/python/main.py @@ -0,0 +1,4 @@ +from source.index import menu + +if __name__ == "__main__": + menu() diff --git a/projects/001-budget-app/python/source/__init__.py b/projects/001-budget-app/python/source/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/001-budget-app/python/source/class_config/__init__.py b/projects/001-budget-app/python/source/class_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/001-budget-app/budget.py b/projects/001-budget-app/python/source/class_config/budget.py similarity index 56% rename from projects/001-budget-app/budget.py rename to projects/001-budget-app/python/source/class_config/budget.py index d202432..92812a8 100644 --- a/projects/001-budget-app/budget.py +++ b/projects/001-budget-app/python/source/class_config/budget.py @@ -1,5 +1,4 @@ class Category: - def __init__(self, name: str): self.name = name self.ledger = [] @@ -45,46 +44,6 @@ def __str__(self): -def create_spend_chart(categories): - spending = [] - for category in categories: - total_spent = 0 - for entry in category.ledger: - amount = entry["amount"] - if amount < 0: - total_spent += abs(amount) - spending.append(total_spent) - - total = sum(spending) - percentages = [int((sp / total) * 10) * 10 for sp in spending] - - - - chart = "Percentage spent by category\n" - - for i in range(100, -1, -10): - chart += f"{i:>3}| " - for p in percentages: - if p >= i: - chart += "o " - else: - chart += " " - chart += "\n" - - chart += " -" + "---" * len(categories) + "\n" - - max_len = max(len(c.name) for c in categories) - for i in range(max_len): - chart += " " - for c in categories: - if i3}| " + for p in percentages: + if p >= i: + chart += "o " + else: + chart += " " + chart += "\n" + + chart += " -" + "---" * len(categories) + "\n" + + max_len = max(len(c.name) for c in categories) + for i in range(max_len): + chart += " " + for c in categories: + if i Date: Tue, 28 Oct 2025 19:05:11 +0100 Subject: [PATCH 3/4] review functionality --- .../python/{source => budget_app}/__init__.py | 0 .../python/budget_app/app_function.py | 83 +++++++++++++++++++ .../class_config => budget_app}/budget.py | 0 .../python/budget_app/controller.py | 59 +++++++++++++ .../001-budget-app/python/budget_app/menu.py | 14 ++++ .../tests}/__init__.py | 0 .../tests/test_module.py | 0 projects/001-budget-app/python/main.py | 13 ++- .../001-budget-app/python/source/index.py | 70 ---------------- .../python/source/tests/__init__.py | 0 .../python/source/utility/__init__.py | 0 .../source/utility/create_spend_chart.py | 38 --------- 12 files changed, 167 insertions(+), 110 deletions(-) rename projects/001-budget-app/python/{source => budget_app}/__init__.py (100%) create mode 100644 projects/001-budget-app/python/budget_app/app_function.py rename projects/001-budget-app/python/{source/class_config => budget_app}/budget.py (100%) create mode 100644 projects/001-budget-app/python/budget_app/controller.py create mode 100644 projects/001-budget-app/python/budget_app/menu.py rename projects/001-budget-app/python/{source/class_config => budget_app/tests}/__init__.py (100%) rename projects/001-budget-app/python/{source => budget_app}/tests/test_module.py (100%) delete mode 100644 projects/001-budget-app/python/source/index.py delete mode 100644 projects/001-budget-app/python/source/tests/__init__.py delete mode 100644 projects/001-budget-app/python/source/utility/__init__.py delete mode 100644 projects/001-budget-app/python/source/utility/create_spend_chart.py diff --git a/projects/001-budget-app/python/source/__init__.py b/projects/001-budget-app/python/budget_app/__init__.py similarity index 100% rename from projects/001-budget-app/python/source/__init__.py rename to projects/001-budget-app/python/budget_app/__init__.py diff --git a/projects/001-budget-app/python/budget_app/app_function.py b/projects/001-budget-app/python/budget_app/app_function.py new file mode 100644 index 0000000..c99398d --- /dev/null +++ b/projects/001-budget-app/python/budget_app/app_function.py @@ -0,0 +1,83 @@ +from source.controller import Controller +from source.menu import PrintMenu + +class AppFunction: + def __init__(self): + self.controller = Controller() + self.menu = PrintMenu + + def menu_selection(self): + while True: + self.menu.print_menu() + selection = int(input("Select an option: ")) + match selection: + case 1: + self.create_category() + case 2: + self.add_funds() + case 3: + self.get_withdraw() + case 4: + self.transfer() + case 5: + self.show_category_detail() + case 6: + self.show_graphic_spent() + case 7: + print("Thank you for using this program") + break + case _: + print("Invalid selection") + + def create_category(self): + name = input("Enter category name: ") + try: + self.controller.add_category(name) + except ValueError as e: + print(f"Error: {e}") + + def add_funds(self): + name_category = self.controller.get_category(input("Enter category name: ")) + amount = int(input("Enter amount of funds: ")) + if amount < 0: + print("Amount must be positive") + description = input("Enter description of funds: ") + try: + name_category.deposit(amount, description) + print(f"Deposited: {amount}, Description: {description}") + + except (ValueError, KeyError) as e: + print(f"Error: {e}") + + def get_withdraw(self): + name_category = self.controller.get_category(input("Enter category name: ")) + amount = int(input("Enter amount of withdrawals: ")) + description = input("Enter description of withdrawals: ") + try: + name_category.withdraw(amount, description) + print(f"Withdrawal: {amount}, Description: {description}") + except (ValueError, KeyError) as e: + print(f"Error: {e}") + + def transfer(self): + try: + from_category = self.controller.get_category(input("Enter category name: ")) + to_category = self.controller.get_category(input("Enter category name: ")) + amount = int(input("Enter amount of transfers: ")) + if from_category.transfer(amount, to_category): + print(f"Transferred: {amount} from {from_category.name} to {to_category.name}") + else: + print("Not enough funds to transfer!") + except (ValueError, KeyError) as e: + print(f"Error: {e}") + + def show_category_detail(self): + category_name = self.controller.get_category(input("Enter category name: ")) + if category_name: + print(category_name) + else: + print("Not found") + + def show_graphic_spent(self): + print(self.controller.create_spend_chart()) + diff --git a/projects/001-budget-app/python/source/class_config/budget.py b/projects/001-budget-app/python/budget_app/budget.py similarity index 100% rename from projects/001-budget-app/python/source/class_config/budget.py rename to projects/001-budget-app/python/budget_app/budget.py diff --git a/projects/001-budget-app/python/budget_app/controller.py b/projects/001-budget-app/python/budget_app/controller.py new file mode 100644 index 0000000..e924d0b --- /dev/null +++ b/projects/001-budget-app/python/budget_app/controller.py @@ -0,0 +1,59 @@ +from source.budget import Category + + +class Controller: + def __init__(self): + self.categories = {} + + def add_category(self, name: str) -> Category: + if name not in self.categories: + category = Category(name) + self.categories[name] = category + return category + else: + raise ValueError(f"Category {name} already exists") + + def get_category(self, name: str) -> Category: + if name in self.categories: + return self.categories[name] + else: + raise ValueError(f"Category {name} does not exist") + + def create_spend_chart(self): + spending = [] + for category in self.categories: + total_spent = 0 + for entry in category.ledger: + amount = entry["amount"] + if amount < 0: + total_spent += abs(amount) + spending.append(total_spent) + + total = sum(spending) + percentages = [int((sp / total) * 10) * 10 for sp in spending] + + chart = "Percentage spent by category\n" + + for i in range(100, -1, -10): + chart += f"{i:>3}| " + for p in percentages: + if p >= i: + chart += "o " + else: + chart += " " + chart += "\n" + + chart += " -" + "---" * len(self.categories) + "\n" + + max_len = max(len(c.name) for c in self.categories) + for i in range(max_len): + chart += " " + for c in self.categories: + if i < len(c.name): + chart += c.name[i] + " " + else: + chart += " " + if i < max_len - 1: + chart += "\n" + + return chart \ No newline at end of file diff --git a/projects/001-budget-app/python/budget_app/menu.py b/projects/001-budget-app/python/budget_app/menu.py new file mode 100644 index 0000000..5d80f0d --- /dev/null +++ b/projects/001-budget-app/python/budget_app/menu.py @@ -0,0 +1,14 @@ +class PrintMenu: + @staticmethod + def print_initial_message(): + print("Welcome to budget app") + + @staticmethod + def print_menu(): + print("What would you like to do?") + print("1. Create a new category budget") + print("2. Add founds to your category budget") + print("3. Get a withdraw") + print("4. transfer between categories ") + print("5. Show category balance") + print("6. Show graphic spent") diff --git a/projects/001-budget-app/python/source/class_config/__init__.py b/projects/001-budget-app/python/budget_app/tests/__init__.py similarity index 100% rename from projects/001-budget-app/python/source/class_config/__init__.py rename to projects/001-budget-app/python/budget_app/tests/__init__.py diff --git a/projects/001-budget-app/python/source/tests/test_module.py b/projects/001-budget-app/python/budget_app/tests/test_module.py similarity index 100% rename from projects/001-budget-app/python/source/tests/test_module.py rename to projects/001-budget-app/python/budget_app/tests/test_module.py diff --git a/projects/001-budget-app/python/main.py b/projects/001-budget-app/python/main.py index 8a861c2..8d3b03f 100644 --- a/projects/001-budget-app/python/main.py +++ b/projects/001-budget-app/python/main.py @@ -1,4 +1,13 @@ -from source.index import menu +from budget_app.menu import PrintMenu +from budget_app.app_function import AppFunction + +class StartApp: + @staticmethod + def start(): + PrintMenu.print_initial_message() + app = AppFunction() + app.menu_selection() + if __name__ == "__main__": - menu() + StartApp.start() \ No newline at end of file diff --git a/projects/001-budget-app/python/source/index.py b/projects/001-budget-app/python/source/index.py deleted file mode 100644 index 441c703..0000000 --- a/projects/001-budget-app/python/source/index.py +++ /dev/null @@ -1,70 +0,0 @@ -from source.class_config.budget import Category -from source.utility.create_spend_chart import create_spend_chart - -def menu(): - print("Welcome to Budget App") - categories = {} - while True: - print("What would you like to do?") - print("1. Create a new category budget") - print("2. Add founds to your category budget") - print("3. Get a withdraw") - print("4. transfer between categories ") - print("5. Show category balance") - print("6. Show graphic spent") - - selection = input("What would you like to do? ") - match selection: - case "1": - name = input("Name of category: ") - categories[name] = Category(name) - print(f"Category {name} created") - case "2": - name = input("Name of category to deposit: ") - if name in categories: - amount = float(input("Amount to deposit: ")) - description = input("Description: ") - categories[name].deposit(amount, description) - print(f"Deposited {amount} to {description}") - else: - print("Category not found") - - case "3": - name = input("Name of category to withdraw: ") - if name in categories: - amount = float(input("Amount to withdraw: ")) - description = input("Description: ") - if categories[name].withdraw(amount, description): - print("Withdrawn successfully") - else: - print("Insufficient funds") - else: - print("Category not found") - - case "4": - from_category = input(" Transfer from: ") - to_category = input("Transfer to: ") - if from_category in categories and to_category in categories: - amount = float(input("Amount to transfer: ")) - if categories[from_category].transfer(amount, categories[to_category]): - print(f"Transfer {amount} from {from_category} to {to_category}") - else: - print("Insufficient funds") - else: - print("One or more category not found") - - case "5": - name = input("Name of category: ") - if name in categories: - print(categories[name]) - - case "6": - if categories: - print(create_spend_chart(list(categories.values()))) - - case "7": - print("Thank you for using Budget App") - break - - case _: - print("Invalid selection") \ No newline at end of file diff --git a/projects/001-budget-app/python/source/tests/__init__.py b/projects/001-budget-app/python/source/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/projects/001-budget-app/python/source/utility/__init__.py b/projects/001-budget-app/python/source/utility/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/projects/001-budget-app/python/source/utility/create_spend_chart.py b/projects/001-budget-app/python/source/utility/create_spend_chart.py deleted file mode 100644 index 8a6edab..0000000 --- a/projects/001-budget-app/python/source/utility/create_spend_chart.py +++ /dev/null @@ -1,38 +0,0 @@ -def create_spend_chart(categories): - spending = [] - for category in categories: - total_spent = 0 - for entry in category.ledger: - amount = entry["amount"] - if amount < 0: - total_spent += abs(amount) - spending.append(total_spent) - - total = sum(spending) - percentages = [int((sp / total) * 10) * 10 for sp in spending] - - chart = "Percentage spent by category\n" - - for i in range(100, -1, -10): - chart += f"{i:>3}| " - for p in percentages: - if p >= i: - chart += "o " - else: - chart += " " - chart += "\n" - - chart += " -" + "---" * len(categories) + "\n" - - max_len = max(len(c.name) for c in categories) - for i in range(max_len): - chart += " " - for c in categories: - if i Date: Sat, 1 Nov 2025 12:40:31 +0100 Subject: [PATCH 4/4] Bug fix, review flow of code, refactor code --- .../python/budget_app/app_function.py | 101 +++++++++++------- .../python/budget_app/controller.py | 11 +- .../001-budget-app/python/budget_app/menu.py | 2 - 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/projects/001-budget-app/python/budget_app/app_function.py b/projects/001-budget-app/python/budget_app/app_function.py index c99398d..a6e1fb6 100644 --- a/projects/001-budget-app/python/budget_app/app_function.py +++ b/projects/001-budget-app/python/budget_app/app_function.py @@ -1,5 +1,5 @@ -from source.controller import Controller -from source.menu import PrintMenu +from budget_app.controller import Controller +from budget_app.menu import PrintMenu class AppFunction: def __init__(self): @@ -9,62 +9,88 @@ def __init__(self): def menu_selection(self): while True: self.menu.print_menu() - selection = int(input("Select an option: ")) - match selection: - case 1: - self.create_category() - case 2: - self.add_funds() - case 3: - self.get_withdraw() - case 4: - self.transfer() - case 5: - self.show_category_detail() - case 6: - self.show_graphic_spent() - case 7: - print("Thank you for using this program") - break - case _: - print("Invalid selection") + try: + if (selection := int(input("Select an option: "))) not in [1,2,3,4,5,6,7]: + print("Invalid selection. Try again.") + continue + + match selection: + case 1: + self.create_category() + case 2: + self.add_funds() + case 3: + self.get_withdraw() + case 4: + self.transfer() + case 5: + self.show_category_detail() + case 6: + self.show_graphic_spent() + case 7: + print("Thank you for using this program") + break + + except ValueError: + print("Invalid selection. Try again.") def create_category(self): name = input("Enter category name: ") try: self.controller.add_category(name) + print("Successfully added category '{}'".format(name)) + while (response := input("Category have budget 0, want to add funds?(y,n): ").lower()) not in ["n","no"]: + if response in ["y", "yes"]: + category = self.controller.get_category(name) + self.add_funds(category, "Initial deposit") + break + else: + print("Invalid input. Try again.") except ValueError as e: print(f"Error: {e}") - def add_funds(self): - name_category = self.controller.get_category(input("Enter category name: ")) - amount = int(input("Enter amount of funds: ")) - if amount < 0: - print("Amount must be positive") - description = input("Enter description of funds: ") + def add_funds(self, name_category=None, description=None): try: + if name_category is None: + name_category = self.controller.get_category(input("Enter category name: ")) + + if (amount := int(input("Enter amount of funds: "))) < 0: + print("Amount must be positive.") + return + + if description is None: + description = input("Enter description of funds: ") name_category.deposit(amount, description) print(f"Deposited: {amount}, Description: {description}") except (ValueError, KeyError) as e: print(f"Error: {e}") + def get_withdraw(self): - name_category = self.controller.get_category(input("Enter category name: ")) - amount = int(input("Enter amount of withdrawals: ")) - description = input("Enter description of withdrawals: ") try: - name_category.withdraw(amount, description) - print(f"Withdrawal: {amount}, Description: {description}") + name_category = self.controller.get_category(input("Enter category name: ")) + + + + if name_category.withdraw( + (amount:=int(input("Enter amount of withdrawals: "))), + (description :=input("Enter description of withdrawals: ")) + ): + + print(f"Withdrawal: {amount}, Description: {description}") + else: + print("Not enough funds.") except (ValueError, KeyError) as e: print(f"Error: {e}") def transfer(self): try: - from_category = self.controller.get_category(input("Enter category name: ")) - to_category = self.controller.get_category(input("Enter category name: ")) - amount = int(input("Enter amount of transfers: ")) - if from_category.transfer(amount, to_category): + from_category = self.controller.get_category(input("Transfer from category: ")) + to_category = self.controller.get_category(input("To category: ")) + + + if from_category.transfer(amount :=int(input("Enter amount of transfers: ")), to_category): print(f"Transferred: {amount} from {from_category.name} to {to_category.name}") else: print("Not enough funds to transfer!") @@ -72,8 +98,7 @@ def transfer(self): print(f"Error: {e}") def show_category_detail(self): - category_name = self.controller.get_category(input("Enter category name: ")) - if category_name: + if category_name := self.controller.get_category(input("Enter category name: ")): print(category_name) else: print("Not found") diff --git a/projects/001-budget-app/python/budget_app/controller.py b/projects/001-budget-app/python/budget_app/controller.py index e924d0b..0424157 100644 --- a/projects/001-budget-app/python/budget_app/controller.py +++ b/projects/001-budget-app/python/budget_app/controller.py @@ -1,4 +1,4 @@ -from source.budget import Category +from budget_app.budget import Category class Controller: @@ -20,8 +20,9 @@ def get_category(self, name: str) -> Category: raise ValueError(f"Category {name} does not exist") def create_spend_chart(self): + categories = list(self.categories.values()) spending = [] - for category in self.categories: + for category in categories: total_spent = 0 for entry in category.ledger: amount = entry["amount"] @@ -43,12 +44,12 @@ def create_spend_chart(self): chart += " " chart += "\n" - chart += " -" + "---" * len(self.categories) + "\n" + chart += " -" + "---" * len(categories) + "\n" - max_len = max(len(c.name) for c in self.categories) + max_len = max(len(c.name) for c in categories) for i in range(max_len): chart += " " - for c in self.categories: + for c in categories: if i < len(c.name): chart += c.name[i] + " " else: diff --git a/projects/001-budget-app/python/budget_app/menu.py b/projects/001-budget-app/python/budget_app/menu.py index 5d80f0d..f51e89d 100644 --- a/projects/001-budget-app/python/budget_app/menu.py +++ b/projects/001-budget-app/python/budget_app/menu.py @@ -1,9 +1,7 @@ class PrintMenu: - @staticmethod def print_initial_message(): print("Welcome to budget app") - @staticmethod def print_menu(): print("What would you like to do?") print("1. Create a new category budget")