diff --git a/projects/001-budget-app/budget.py b/projects/001-budget-app/budget.py deleted file mode 100644 index 933cc4e..0000000 --- a/projects/001-budget-app/budget.py +++ /dev/null @@ -1,7 +0,0 @@ -class Category: - - - - - -def create_spend_chart(categories): \ No newline at end of file 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/budget_app/__init__.py b/projects/001-budget-app/python/budget_app/__init__.py new file mode 100644 index 0000000..e69de29 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..a6e1fb6 --- /dev/null +++ b/projects/001-budget-app/python/budget_app/app_function.py @@ -0,0 +1,108 @@ +from budget_app.controller import Controller +from budget_app.menu import PrintMenu + +class AppFunction: + def __init__(self): + self.controller = Controller() + self.menu = PrintMenu + + def menu_selection(self): + while True: + self.menu.print_menu() + 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=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): + try: + 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("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!") + except (ValueError, KeyError) as e: + print(f"Error: {e}") + + def show_category_detail(self): + if category_name := self.controller.get_category(input("Enter 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/budget_app/budget.py b/projects/001-budget-app/python/budget_app/budget.py new file mode 100644 index 0000000..92812a8 --- /dev/null +++ b/projects/001-budget-app/python/budget_app/budget.py @@ -0,0 +1,49 @@ +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 + + + + + + + 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..0424157 --- /dev/null +++ b/projects/001-budget-app/python/budget_app/controller.py @@ -0,0 +1,60 @@ +from budget_app.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): + categories = list(self.categories.values()) + 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 < 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..f51e89d --- /dev/null +++ b/projects/001-budget-app/python/budget_app/menu.py @@ -0,0 +1,12 @@ +class PrintMenu: + def print_initial_message(): + print("Welcome to budget app") + + 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/budget_app/tests/__init__.py b/projects/001-budget-app/python/budget_app/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/001-budget-app/test_module.py b/projects/001-budget-app/python/budget_app/tests/test_module.py similarity index 96% rename from projects/001-budget-app/test_module.py rename to projects/001-budget-app/python/budget_app/tests/test_module.py index cf058eb..f17af24 100644 --- a/projects/001-budget-app/test_module.py +++ b/projects/001-budget-app/python/budget_app/tests/test_module.py @@ -1,7 +1,11 @@ -import unittest -import budget -from budget import create_spend_chart +import sys +import os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) +from source.class_config import budget +from source.utility.create_spend_chart import create_spend_chart +import unittest class UnitTests(unittest.TestCase): def setUp(self): diff --git a/projects/001-budget-app/python/main.py b/projects/001-budget-app/python/main.py index e69de29..8d3b03f 100644 --- a/projects/001-budget-app/python/main.py +++ b/projects/001-budget-app/python/main.py @@ -0,0 +1,13 @@ +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__": + StartApp.start() \ No newline at end of file diff --git a/projects/__init__.py b/projects/__init__.py new file mode 100644 index 0000000..e69de29