From 900e2b9ef7888ddf2dc2c87667de37acf4222a32 Mon Sep 17 00:00:00 2001 From: Andrew Ward Date: Sat, 22 Mar 2025 11:01:33 +0000 Subject: [PATCH] refactor settings handling to its own class --- utils/__pycache__/text_to_mic.cpython-312.pyc | Bin 70365 -> 69095 bytes utils/ai_editor_manager.py | 5 +- utils/settings_manager.py | 116 ++++++++++++++++++ utils/text_to_mic.py | 76 ++---------- 4 files changed, 130 insertions(+), 67 deletions(-) create mode 100644 utils/settings_manager.py diff --git a/utils/__pycache__/text_to_mic.cpython-312.pyc b/utils/__pycache__/text_to_mic.cpython-312.pyc index ba0a47f8ef66225423e8c7ac25639fa941aa8d9d..1a6b8ef3b23918c3b45b15483b327ef3a3b399d5 100644 GIT binary patch delta 6234 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 delta 6978 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 O^C&kdzvr+m$^H*%RaPtj diff --git a/utils/ai_editor_manager.py b/utils/ai_editor_manager.py index 2189481..926b1ee 100644 --- a/utils/ai_editor_manager.py +++ b/utils/ai_editor_manager.py @@ -1,5 +1,6 @@ import tkinter as tk from tkinter import ttk, messagebox +from utils.settings_manager import SettingsManager class AIEditorManager: """ @@ -90,9 +91,9 @@ class AIEditorManager: def save_settings(self, settings): """Save AI copy editing settings and update the UI""" - self.app.save_settings_to_JSON(settings) + SettingsManager.update_settings(settings) + messagebox.showinfo("Settings Updated", "Your settings have been saved successfully.") - self.app.load_settings() # Refresh settings if needed elsewhere # Update the status indicator on the main screen with more specific information self.update_status_display() diff --git a/utils/settings_manager.py b/utils/settings_manager.py new file mode 100644 index 0000000..4aaca94 --- /dev/null +++ b/utils/settings_manager.py @@ -0,0 +1,116 @@ +import json +import platform +from pathlib import Path + +class SettingsManager: + """ + Centralizes access to application settings to prevent conflicts between components. + """ + + @staticmethod + def get_settings_file_path(filename="settings.json"): + """Get the platform-specific path for the settings file.""" + if platform.system() == 'Darwin': # macOS + from utils.api_key_manager import APIKeyManager + mac_path = APIKeyManager.get_app_support_path_mac() + return f"{mac_path}/{filename}" + else: + return filename # Default to current directory for non-macOS systems + + @classmethod + def get_default_settings(cls): + """Return the default settings structure.""" + return { + "chat_gpt_completion": False, + "model": "gpt-4o-mini", + "prompt": "", + "auto_apply_ai_to_recording": False, + "current_tone": "None", + "hide_banner": False, + "input_device": "Default", + "primary_device": "Select Device", + "secondary_device": "None", + "hotkeys": { + "record_start_stop": ["ctrl", "shift", "0"], + "stop_recording": ["ctrl", "shift", "9"], + "play_last_audio": ["ctrl", "shift", "8"], + "cancel_operation": ["ctrl", "shift", "1"] + }, + "max_tokens": 750 + } + + @classmethod + def load_settings(cls): + """Load settings from file, with defaults for missing values.""" + settings_file = cls.get_settings_file_path() + default_settings = cls.get_default_settings() + + try: + # Try to load existing settings + with open(settings_file, "r") as f: + settings = json.load(f) + + # Check if settings need to be updated with new defaults + settings_updated = False + + # Recursively update nested dictionaries with missing keys + def update_missing_settings(existing, defaults): + nonlocal settings_updated + for key, value in defaults.items(): + if key not in existing: + existing[key] = value + settings_updated = True + elif isinstance(value, dict) and isinstance(existing[key], dict): + # Recursively update nested dictionaries + update_missing_settings(existing[key], value) + return existing + + # Update settings with any missing values + settings = update_missing_settings(settings, default_settings) + + # Save if any settings were updated + if settings_updated: + cls.save_settings(settings) + print("Settings file updated with new default values") + + except FileNotFoundError: + # Create new settings file with defaults if it doesn't exist + settings = default_settings + cls.save_settings(settings) + + return settings + + @classmethod + def save_settings(cls, settings): + """Save complete settings to file.""" + settings_file = cls.get_settings_file_path() + + with open(settings_file, "w") as f: + json.dump(settings, f) + + @classmethod + def update_settings(cls, partial_settings): + """ + Update only specific settings without touching others. + + Args: + partial_settings: Dictionary containing only the settings to update + """ + # First load existing settings + current_settings = cls.load_settings() + + # Update settings (recursively for nested dictionaries) + def recursive_update(target, source): + for key, value in source.items(): + if isinstance(value, dict) and key in target and isinstance(target[key], dict): + # If both are dictionaries, update recursively + recursive_update(target[key], value) + else: + # Otherwise just update the value + target[key] = value + + recursive_update(current_settings, partial_settings) + + # Save the updated settings + cls.save_settings(current_settings) + return current_settings \ No newline at end of file diff --git a/utils/text_to_mic.py b/utils/text_to_mic.py index 6bf0bc7..78bb6bc 100644 --- a/utils/text_to_mic.py +++ b/utils/text_to_mic.py @@ -25,6 +25,7 @@ from utils.resource_utils import ResourceUtils from utils.tone_presets_manager import TonePresetsManager from utils.presets_manager import PresetsManager from utils.ai_editor_manager import AIEditorManager +from utils.settings_manager import SettingsManager # Modify the load environment variables to load from config/.env def load_env_file(): @@ -1168,75 +1169,20 @@ Please also make sure you read the Terms of use and licence statement before usi print(f"Transcription error: An error occurred during transcription: {str(e)}") def load_settings(self): - settings_file = self.get_settings_file_path("settings.json") - - # Define default settings structure - default_settings = { - "chat_gpt_completion": False, - "model": self.default_model, - "prompt": "", - "auto_apply_ai_to_recording": False, - "current_tone": "None", - "hide_banner": False, - "input_device": "Default", # Add default for input device - "primary_device": "Select Device", # Also add for primary device - "secondary_device": "None", # Also add for secondary device - "hotkeys": { - "record_start_stop": ["ctrl", "shift", "0"], - "stop_recording": ["ctrl", "shift", "9"], - "play_last_audio": ["ctrl", "shift", "8"], - "cancel_operation": ["ctrl", "shift", "1"] - }, - "max_tokens": 750 - } - - try: - # Try to load existing settings - with open(settings_file, "r") as f: - settings = json.load(f) - - # Check if settings need to be updated with new defaults - settings_updated = False - - # Recursively update nested dictionaries with missing keys - def update_missing_settings(existing, defaults): - nonlocal settings_updated - for key, value in defaults.items(): - if key not in existing: - existing[key] = value - settings_updated = True - elif isinstance(value, dict) and isinstance(existing[key], dict): - # Recursively update nested dictionaries - update_missing_settings(existing[key], value) - return existing - - # Update settings with any missing values - settings = update_missing_settings(settings, default_settings) - - # Save if any settings were updated - if settings_updated: - self.save_settings_to_JSON(settings) - print("Settings file updated with new default values") - - except FileNotFoundError: - # Create new settings file with defaults if it doesn't exist - settings = default_settings - self.save_settings_to_JSON(settings) - - return settings + """Load settings using the SettingsManager.""" + return SettingsManager.load_settings() def save_settings_to_JSON(self, settings): - settings_file = self.get_settings_file_path("settings.json") - - with open(settings_file, "w") as f: - json.dump(settings, f) + """Save complete settings using the SettingsManager.""" + SettingsManager.save_settings(settings) + + def update_settings(self, partial_settings): + """Update specific settings without overwriting others.""" + return SettingsManager.update_settings(partial_settings) def get_settings_file_path(self, filename): - if platform.system() == 'Darwin': # Check if the OS is macOS - mac_path = APIKeyManager.get_app_support_path_mac() - return f"{mac_path}/{filename}" - else: - return filename # Default to current directory for non-macOS systems + """Get the settings file path using SettingsManager.""" + return SettingsManager.get_settings_file_path(filename) # Methods for tone preset management def show_tone_presets_manager(self):