from config import ADMIN_PASSWORD class CLI: def __init__(self, tracker) -> None: self.tracker = tracker self.current_user = None from raspi.motor import Raspi self.motor = Raspi() def run(self): while True: self._clear_screen() self._show_main_menu() def _clear_screen(self): import subprocess import os subprocess.run("cls" if os.name == "nt" else "clear") def _show_main_menu(self): print("=" * 50) print("DEBT TRACKER") print("=" * 50) if self.current_user: user = self.tracker.get_user_by_id(self.current_user) debt = self.tracker.get_user_debt(self.current_user) print(f"\nLogged in as: {user.name}") print(f"Current Debt: ${debt:.2f}\n") print("1. Select user") print("2. View all debts") print("3. Admin panel") print("4. Exit") print() choice = input("Choose Option: ").strip() if choice == "1": self._select_user() elif choice == "2": self._view_all_debts() elif choice == "3": self._admin_panel() elif choice == "4": print("\nGoodbye!") exit() else: self._invalid_choice() def _select_user(self): self._clear_screen() print("=" * 50) print("SELECT USER") print("=" * 50) print() users = self.tracker.get_all_users() user_list = list(users.items()) for i, (user_id, user) in enumerate(user_list, 1): debt = self.tracker.get_user_debt(user_id) print(f"{i}. {user.name} (Debt: ${debt:.2f})") print(f"{len(user_list) + 1}. Back to main menu") print() try: choice = int(input("Choose user: ").strip()) if choice == len(user_list) + 1: return if 1 <= choice <= len(user_list): self.current_user = user_list[choice - 1][0] self._user_menu() else: self._invalid_choice() except ValueError: self._invalid_choice() def _user_menu(self): while True: self._clear_screen() user = self.tracker.get_user_by_id(self.current_user) debt = self.tracker.get_user_debt(self.current_user) print("=" * 50) print(f"USER MENU - {user.name}") print("=" * 50) print(f"Current debt: ${debt:.2f}\n") print("1. Add purchase") print("2. View my debt") print("3. Logout") print() choice = input("Choose option: ").strip() if choice == "1": self._add_purchase() elif choice == "2": self._view_user_debt() elif choice == "3": self.current_user = None break else: self._invalid_choice() def _add_purchase(self): self._clear_screen() print("=" * 50) print("ADD PURCHASE") print("=" * 50) print() menu_items = self.tracker.get_items() item_list = list(menu_items.items()) for i, (_item_id, item) in enumerate(item_list, 1): print(f"{i}. {item.name} - ${item.price:.2f}") print(f"{len(item_list) + 1}. Cancel") print() try: choice = int(input("Choose item: ").strip()) if choice == len(item_list) + 1: return if 1 <= choice <= len(item_list): selected_item_id = item_list[choice - 1][0] self._confirm_purchase(selected_item_id) else: self._invalid_choice() except ValueError: self._invalid_choice() def _confirm_purchase(self, item_id): self._clear_screen() user = self.tracker.get_user_by_id(self.current_user) item = self.tracker.get_items()[item_id] print("=" * 50) print("CONFIRM PURCHASE") print("=" * 50) print() print(f"User: {user.name}") print(f"Item: {item.name}") print(f"Price: ${item.price:.2f}") print() confirm = input("Add to debt? (y/n): ").strip().lower() if confirm == "y": result = self.tracker.add_purchase(self.current_user, item_id) if result["status"] == "success": self._clear_screen() print("=" * 50) print("PURCHASE ADDED") print("=" * 50) print() print(f"✓ {result['message']}") print(f"New debt: ${result['new_debt']:.2f}") print() self.motor.unlock_async() input("Press Enter to continue...") self.motor.lock_async() else: print(f"Error: {result['message']}") input("Press Enter to continue...") else: print("Purchase cancelled.") input("Press Enter to continue...") def _view_user_debt(self): self._clear_screen() user = self.tracker.get_user_by_id(self.current_user) debt = self.tracker.get_user_debt(self.current_user) print("=" * 50) print(f"DEBT - {user.name}") print("=" * 50) print() print(f"Amount owed: ${debt:.2f}") print() input("Press Enter to continue...") def _view_all_debts(self): self._clear_screen() print("=" * 50) print("ALL DEBTS") print("=" * 50) print() all_debts = self.tracker.get_all_debts() if not all_debts: print("No users with debt.") else: total = 0 for _user_id, debt_info in all_debts.items(): amount = debt_info["debt"] print(f"{debt_info['name']:.<30} ${amount:>7.2f}") total += amount print("-" * 50) print(f"{'TOTAL':.<30} ${total:>7.2f}") print() input("Press Enter to continue...") def _admin_panel(self): self._clear_screen() print("=" * 50) print("ADMIN PANEL") print("=" * 50) print() password = input("Enter admin password: ").strip() if password != ADMIN_PASSWORD: print("Invalid password!") input("Press Enter to continue...") return while True: self._clear_screen() print("=" * 50) print("ADMIN PANEL") print("=" * 50) print() print("1. View all debts") print("2. Adjust user debt") print("3. Clear user debt") print("4. View transaction history") print("5. Manage users") print("6. Manage items") print("7. Open Kühlschrank (Unlock)") print("8. Close Kühlschrank (Lock)") print("9. Logout") print() choice = input("Choose option: ").strip() if choice == "1": self._view_all_debts() elif choice == "2": self._adjust_debt() elif choice == "3": self._clear_user_debt() elif choice == "4": self._view_transaction_history() elif choice == "5": self._manage_users() elif choice == "6": self._manage_items() elif choice == "7": print("Kühlschrank wird geöffnet (Entriegeln)...") self.motor.unlock_async() input("Press Enter to continue...") elif choice == "8": print("Kühlschrank wird geschlossen (Verriegeln)...") self.motor.lock_async() input("Press Enter to continue...") elif choice == "9": break else: self._invalid_choice() def _adjust_debt(self): self._clear_screen() print("=" * 50) print("ADJUST DEBT") print("=" * 50) print() users = self.tracker.get_all_users() user_list = list(users.items()) for i, (user_id, user) in enumerate(user_list, 1): debt = self.tracker.get_user_debt(user_id) print(f"{i}. {user.name} - ${debt:.2f}") print() try: choice = int(input("Choose user: ").strip()) if 1 <= choice <= len(user_list): user_id = user_list[choice - 1][0] amount_str = input("Amount paid: $").strip() amount = float(amount_str) result = self.tracker.adjust_debt(user_id, amount, ADMIN_PASSWORD) self._clear_screen() print("=" * 50) print("DEBT ADJUSTED") print("=" * 50) print() print(f"{result['message']}") print(f"New debt: ${result['new_debt']:.2f}") print() input("Press Enter to continue...") else: self._invalid_choice() except ValueError: print("Invalid input!") input("Press Enter to continue...") def _clear_user_debt(self): self._clear_screen() print("=" * 50) print("CLEAR USER DEBT") print("=" * 50) print() users = self.tracker.get_all_users() user_list = list(users.items()) for i, (user_id, user) in enumerate(user_list, 1): debt = self.tracker.get_user_debt(user_id) print(f"{i}. {user.name} - ${debt:.2f}") print() try: choice = int(input("Choose user: ").strip()) if 1 <= choice <= len(user_list): user_id = user_list[choice - 1][0] debt_amount = self.tracker.get_user_debt(user_id) confirm = input(f"Clear ${debt_amount:.2f}? (y/n): ").strip().lower() if confirm == "y": _result = self.tracker.adjust_debt( user_id, debt_amount, ADMIN_PASSWORD ) self._clear_screen() print("=" * 50) print("DEBT CLEARED") print("=" * 50) print() print( f"Debt cleared for {self.tracker.get_user_by_id(user_id).name}" ) print() input("Press Enter to continue...") else: self._invalid_choice() except ValueError: print("Invalid input!") input("Press Enter to continue...") def _view_transaction_history(self): self._clear_screen() print("=" * 50) print("TRANSACTION HISTORY") print("=" * 50) print() transactions = self.tracker.transactions if not transactions: print("No transactions yet.") else: for t in sorted(transactions, key=lambda x: x.timestamp, reverse=True): user = self.tracker.get_user_by_id(t.user_id) item = self.tracker.get_items().get(t.item_id) item_name = item.name if item else ("Abbuchung" if t.item_id in ("adjustment", "Abbuchung") else "Deleted Item") print( f"{t.timestamp[:10]} | {user.name:.<20} {item_name:.<15} ${t.amount:.2f}" ) print() input("Press Enter to continue...") def _invalid_choice(self): print("Invalid choice. Please try again.") input("Press Enter to continue...") def _manage_users(self): while True: self._clear_screen() print("=" * 50) print("MANAGE USERS") print("=" * 50) print() print("1. Add user") print("2. Edit user") print("3. Remove user") print("4. Back to admin panel") print() choice = input("Choose option: ").strip() if choice == "1": self._cli_add_user() elif choice == "2": self._cli_edit_user() elif choice == "3": self._cli_remove_user() elif choice == "4": break else: self._invalid_choice() def _cli_add_user(self): self._clear_screen() print("=" * 50) print("ADD USER") print("=" * 50) print() name = input("Enter new user's name: ").strip() if not name: print("Name cannot be empty!") input("Press Enter to continue...") return import uuid user_id = f"user_{uuid.uuid4().hex[:8]}" res = self.tracker.add_user(user_id, name) print(res["message"]) input("Press Enter to continue...") def _cli_edit_user(self): self._clear_screen() print("=" * 50) print("EDIT USER") print("=" * 50) print() users = self.tracker.get_all_users() user_list = list(users.items()) for i, (user_id, user) in enumerate(user_list, 1): print(f"{i}. {user.name} (PIN: {user.pin})") print(f"{len(user_list) + 1}. Cancel") print() try: choice = int(input("Choose user to edit: ").strip()) if choice == len(user_list) + 1: return if 1 <= choice <= len(user_list): user_id = user_list[choice - 1][0] user = users[user_id] new_name = input(f"Enter new name (leave empty to keep '{user.name}'): ").strip() new_pin = input(f"Enter new PIN (leave empty to keep '{user.pin}'): ").strip() name = new_name if new_name else user.name pin = new_pin if new_pin else user.pin if len(pin) < 4: print("PIN must be at least 4 digits!") input("Press Enter to continue...") return res = self.tracker.edit_user(user_id, name, pin) print(res["message"]) input("Press Enter to continue...") else: self._invalid_choice() except ValueError: self._invalid_choice() def _cli_remove_user(self): self._clear_screen() print("=" * 50) print("REMOVE USER") print("=" * 50) print() users = self.tracker.get_all_users() user_list = list(users.items()) for i, (user_id, user) in enumerate(user_list, 1): print(f"{i}. {user.name}") print(f"{len(user_list) + 1}. Cancel") print() try: choice = int(input("Choose user to remove: ").strip()) if choice == len(user_list) + 1: return if 1 <= choice <= len(user_list): user_id = user_list[choice - 1][0] confirm = input(f"Are you sure you want to delete {users[user_id].name}? (y/n): ").strip().lower() if confirm == "y": res = self.tracker.remove_user(user_id) print(res["message"]) input("Press Enter to continue...") else: self._invalid_choice() except ValueError: self._invalid_choice() def _manage_items(self): while True: self._clear_screen() print("=" * 50) print("MANAGE ITEMS") print("=" * 50) print() print("1. Add item") print("2. Edit item") print("3. Remove item") print("4. Back to admin panel") print() choice = input("Choose option: ").strip() if choice == "1": self._cli_add_item() elif choice == "2": self._cli_edit_item() elif choice == "3": self._cli_remove_item() elif choice == "4": break else: self._invalid_choice() def _cli_add_item(self): self._clear_screen() print("=" * 50) print("ADD ITEM") print("=" * 50) print() name = input("Enter item name: ").strip() if not name: print("Name cannot be empty!") input("Press Enter to continue...") return price_str = input("Enter price ($): ").strip() try: price = float(price_str) if price < 0: raise ValueError except ValueError: print("Invalid price!") input("Press Enter to continue...") return import uuid item_id = f"item_{uuid.uuid4().hex[:8]}" res = self.tracker.add_item(item_id, name, price) print(res["message"]) input("Press Enter to continue...") def _cli_edit_item(self): self._clear_screen() print("=" * 50) print("EDIT ITEM") print("=" * 50) print() items = self.tracker.get_items() item_list = list(items.items()) for i, (item_id, item) in enumerate(item_list, 1): print(f"{i}. {item.name} (${item.price:.2f})") print(f"{len(item_list) + 1}. Cancel") print() try: choice = int(input("Choose item to edit: ").strip()) if choice == len(item_list) + 1: return if 1 <= choice <= len(item_list): item_id = item_list[choice - 1][0] item = items[item_id] new_name = input(f"Enter new name (leave empty to keep '{item.name}'): ").strip() new_price_str = input(f"Enter new price (leave empty to keep '${item.price:.2f}'): ").strip() name = new_name if new_name else item.name price = item.price if new_price_str: try: price = float(new_price_str) if price < 0: raise ValueError except ValueError: print("Invalid price!") input("Press Enter to continue...") return res = self.tracker.edit_item(item_id, name, price) print(res["message"]) input("Press Enter to continue...") else: self._invalid_choice() except ValueError: self._invalid_choice() def _cli_remove_item(self): self._clear_screen() print("=" * 50) print("REMOVE ITEM") print("=" * 50) print() items = self.tracker.get_items() item_list = list(items.items()) for i, (item_id, item) in enumerate(item_list, 1): print(f"{i}. {item.name} (${item.price:.2f})") print(f"{len(item_list) + 1}. Cancel") print() try: choice = int(input("Choose item to remove: ").strip()) if choice == len(item_list) + 1: return if 1 <= choice <= len(item_list): item_id = item_list[choice - 1][0] confirm = input(f"Are you sure you want to delete {items[item_id].name}? (y/n): ").strip().lower() if confirm == "y": res = self.tracker.remove_item(item_id) print(res["message"]) input("Press Enter to continue...") else: self._invalid_choice() except ValueError: self._invalid_choice()