From b880964d5ec51866e23fd5c900a6ac21da9d4c49 Mon Sep 17 00:00:00 2001 From: Matthew Iverson Date: Tue, 26 Nov 2024 18:00:25 -0500 Subject: [PATCH] Upload files to "Modules" --- Modules/submenu.py | 181 +++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 89 deletions(-) diff --git a/Modules/submenu.py b/Modules/submenu.py index 6d21cc7..2549fdc 100644 --- a/Modules/submenu.py +++ b/Modules/submenu.py @@ -7,143 +7,146 @@ GLOBAL_TOTAL_PROGRESS = {"completed": 0, "total": 0} # Tracks progress across a GLOBAL_HIGHLIGHTS = {} # Tracks highlighted actions -def build_submenu(menu_title, actions, module=None): +def build_submenu(menu_title, actions=None, module=None): """ - Builds a submenu and tracks progress across all nested submenus. - Automatically adds an 'All' option to execute all actions. + Builds a submenu dynamically by extracting functions from the given module, + or using a provided `actions` dictionary for pre-defined submenus. + Includes an `[ALL]` option to execute all functions or submenus on the page. + Allows actions to be unmarked and highlighted. """ - # Initialize global action status for this submenu if not already set + if not actions and not module: + raise ValueError("No module or actions provided to extract submenu items.") + + # If actions are not provided, dynamically extract from the module + if not actions: + actions = { + str(idx + 1): { + "description": func.__name__.replace("_", " ").title(), + "function": func, + } + for idx, (name, func) in enumerate(module.items()) + if ( + callable(func) + and func.__module__ == module["__name__"] # Ensure the function is from this module + and "submenu" not in func.__name__ # Exclude functions with 'submenu' in their name + ) + } + + # Reassign sequential numbering starting at 1 + numbered_actions = {str(idx + 1): action for idx, action in enumerate(actions.values())} + + # Add the ALL option with key "ALL" + numbered_actions["ALL"] = { + "description": f"Run All {menu_title}", + "function": lambda: run_all_actions(numbered_actions, module, GLOBAL_ACTION_STATUS[menu_title]), + } + + # Initialize global action status and highlights for this submenu if menu_title not in GLOBAL_ACTION_STATUS: - GLOBAL_ACTION_STATUS[menu_title] = {key: False for key in actions} # False means not completed - GLOBAL_HIGHLIGHTS[menu_title] = {key: False for key in actions} # False means not highlighted + GLOBAL_ACTION_STATUS[menu_title] = {key: False for key in numbered_actions} + GLOBAL_TOTAL_PROGRESS["total"] += len(numbered_actions) - 1 # Exclude the ALL option from the progress count - # Update global total progress for tracking across all menus - GLOBAL_TOTAL_PROGRESS["total"] += len(actions) + if menu_title not in GLOBAL_HIGHLIGHTS: + GLOBAL_HIGHLIGHTS[menu_title] = {key: False for key in numbered_actions} - # Access the action status and highlight status for this specific submenu action_status = GLOBAL_ACTION_STATUS[menu_title] highlight_status = GLOBAL_HIGHLIGHTS[menu_title] - # Dynamically add "All" option - actions["all"] = { - "description": "Run All", - "function": lambda: run_all_actions(actions, module, action_status), - } - - # Ensure the "all" option is in the dictionaries - if "all" not in action_status: - action_status["all"] = False - if "all" not in highlight_status: - highlight_status["all"] = False - while True: + # Check if all individual actions are complete; if so, mark "ALL" as complete + action_status["ALL"] = all(status for key, status in action_status.items() if key != "ALL") + clear_screen() - header() # Call the header without arguments - print("=====================================") + header() # Display global app header + + print("=" * 70) print(f" {menu_title} Menu ") - print("=====================================\n") + print("=" * 70, "\n") - # Display global progress only (not specific to this submenu) - global_completed = GLOBAL_TOTAL_PROGRESS["completed"] - global_total = GLOBAL_TOTAL_PROGRESS["total"] - print(f"Global Progress: {global_completed}/{global_total} tasks completed\n") - - # Display menu options with status, progress, and highlighting - for key, action in actions.items(): - if key == "all": # Highlight the "All" option in blue - print(f"\033[94m[{key.upper()}] {action['description']}\033[0m") + for key, action in numbered_actions.items(): + # Determine the status symbol with color + status = f"{Fore.GREEN}✔️ " if action_status[key] else f"❌" + # Highlight the key and description based on highlight status + if key == "ALL": + print(f"{Fore.BLUE}[{key}]{Style.RESET_ALL} {status} {action['description']}") + elif highlight_status[key]: + print(f"{Style.RESET_ALL}[{key}] {Fore.YELLOW}💡 {action['description']}{Style.RESET_ALL}") else: - # Check the status and highlight of the submenu or action - if "submenu" in action: - submenu_completed = is_submenu_complete(action["submenu"]) - status = "✔️" if submenu_completed else "❌" - else: - status = "✔️ " if action_status[key] else "❌" + print(f"{Style.RESET_ALL}[{key}] {status} {action['description']}") - if highlight_status[key]: # Highlight in yellow if flagged - print(f"\033[93m[{key}] {status} {action['description']}\033[0m") - else: - print(f"[{key}] {status} {action['description']}") print("[0] Return to Main Menu") print("[q] EXIT\n") # Get user choice - choice = input("Enter your choice: ").strip().lower() + choice = input("Enter your choice: ").strip().upper() - # Handle highlighting - if choice.endswith("!") and choice[:-1] in actions: + # Handle highlighting (toggle with "!") + if choice.endswith("!") and choice[:-1] in numbered_actions: key_to_toggle = choice[:-1] highlight_status[key_to_toggle] = not highlight_status[key_to_toggle] if highlight_status[key_to_toggle]: - print(f"Action [{key_to_toggle}] has been highlighted.") + print(f"{Fore.YELLOW}Action [{key_to_toggle}] has been highlighted.") else: - print(f"Highlight removed from action [{key_to_toggle}].") + print(f"{Fore.YELLOW}Highlight removed from action [{key_to_toggle}].") input("Press Enter to continue...") continue - # Handle unchecking of actions - if choice.startswith("-") and choice[1:] in actions: + # Handle unchecking of actions (with "-") + if choice.startswith("-") and choice[1:] in numbered_actions: key_to_uncheck = choice[1:] if action_status[key_to_uncheck]: action_status[key_to_uncheck] = False GLOBAL_TOTAL_PROGRESS["completed"] -= 1 - print(f"Unchecked action [{key_to_uncheck}]") + print(f"{Fore.RED}Unchecked action [{key_to_uncheck}].") else: - print(f"Action [{key_to_uncheck}] was not completed.") + print(f"{Fore.RED}Action [{key_to_uncheck}] was already unchecked.") input("Press Enter to continue...") continue - # Handle menu choices - if choice in actions: - if choice == "all": - run_all_actions(actions, module, action_status) - action_status["all"] = True # Mark the "All" option as completed - elif "submenu" in actions[choice]: # Navigate to nested submenu - actions[choice]["submenu"]() - # After returning, check if all nested submenu items are done - if is_submenu_complete(actions[choice]["submenu"]): - if not action_status[choice]: - action_status[choice] = True # Mark this submenu as complete - GLOBAL_TOTAL_PROGRESS["completed"] += 1 - elif "function" in actions[choice]: # Execute the associated function - actions[choice]["function"]() - if not action_status[choice]: - action_status[choice] = True # Mark the action as completed + if choice in numbered_actions: + if "function" in numbered_actions[choice]: + # Call the action function + numbered_actions[choice]["function"]() + if choice != "ALL" and not action_status[choice]: # Exclude ALL from progress tracking here + action_status[choice] = True GLOBAL_TOTAL_PROGRESS["completed"] += 1 - - # Propagate status updates to the parent menu - propagate_status_to_parent(menu_title) + elif "submenu" in numbered_actions[choice]: + # Call the nested submenu + numbered_actions[choice]["submenu"]() + if is_submenu_complete(numbered_actions[choice]["submenu"]): + if not action_status[choice]: + action_status[choice] = True + GLOBAL_TOTAL_PROGRESS["completed"] += 1 elif choice == "0": - print("Returning to main menu...") break - elif choice in ["q", "exit", "quit"]: - print("Logging out...") + elif choice in ["Q", "EXIT", "QUIT"]: sys.exit() else: - print("Invalid choice. Please try again.") - input("Press Enter to continue...") + print(f"{Fore.RED}Invalid choice. Press Enter to continue...") + input() + - # Propagate parent status again after menu completion - propagate_status_to_parent(menu_title) def run_all_actions(actions, module, action_status): """ - Executes all actions in the submenu sequentially. + Executes all actions in the submenu sequentially, excluding the "ALL" option itself. """ for key, action in actions.items(): - if key != "all": # Skip the "All" option itself - if "function" in action: + if action["description"].startswith("Run All"): # Skip the "ALL" option + continue + + if "function" in action: + if not action_status[key]: + action["function"]() + action_status[key] = True + GLOBAL_TOTAL_PROGRESS["completed"] += 1 + elif "submenu" in action: # Recursively handle submenus + actions[key]["submenu"]() + if is_submenu_complete(actions[key]["submenu"]): if not action_status[key]: - action["function"]() action_status[key] = True GLOBAL_TOTAL_PROGRESS["completed"] += 1 - elif "submenu" in action: # Recursively handle submenus - actions[key]["submenu"]() - if is_submenu_complete(actions[key]["submenu"]): - if not action_status[key]: - action_status[key] = True - GLOBAL_TOTAL_PROGRESS["completed"] += 1 def is_submenu_complete(submenu_function): @@ -171,4 +174,4 @@ def get_submenu_title(submenu_function): """ Converts a submenu function name to a readable submenu title. """ - return submenu_function.__name__.replace("_submenu", "").replace("_", " ").title() + return submenu_function.__name__.replace("_submenu", "").replace("_", " ").title() \ No newline at end of file