# FlightMonitor/data/area_profile_manager.py """ Manages loading, saving, and handling of geographic area profiles. """ import json import os from typing import Dict, Optional, List from flightmonitor.utils.logger import get_logger from flightmonitor.data import config as app_config module_logger = get_logger(__name__) PROFILE_FILENAME = "area_profiles.json" DEFAULT_PROFILE_NAME = "Default Zone" class AreaProfileManager: """ Handles reading from and writing to the area profiles JSON file. """ def __init__(self, profiles_directory: str = "config"): """ Initializes the AreaProfileManager. Args: profiles_directory: The directory where the profiles file will be stored. """ self.profiles_path = os.path.join( os.path.abspath(profiles_directory), PROFILE_FILENAME ) self._profiles: Dict[str, Dict[str, float]] = {} self._load_profiles() def _create_default_profile(self) -> Dict[str, float]: """Creates the default profile from the application config.""" return { "lat_min": app_config.DEFAULT_BBOX_LAT_MIN, "lon_min": app_config.DEFAULT_BBOX_LON_MIN, "lat_max": app_config.DEFAULT_BBOX_LAT_MAX, "lon_max": app_config.DEFAULT_BBOX_LON_MAX, } def _load_profiles(self): """Loads profiles from the JSON file and ensures the default profile exists.""" # Start with the mandatory default profile self._profiles = {DEFAULT_PROFILE_NAME: self._create_default_profile()} if os.path.exists(self.profiles_path): try: with open(self.profiles_path, "r") as f: user_profiles = json.load(f) if isinstance(user_profiles, dict): # Merge user profiles, default profile cannot be overwritten for name, data in user_profiles.items(): if name != DEFAULT_PROFILE_NAME: self._profiles[name] = data module_logger.info( f"Loaded {len(user_profiles)} user profiles from {self.profiles_path}" ) except (json.JSONDecodeError, IOError) as e: module_logger.error( f"Error loading profiles from {self.profiles_path}: {e}" ) else: module_logger.info( "Area profiles file not found. Starting with default profile." ) # We can optionally save the default profile to create the file on first run self._save_profiles() def _save_profiles(self): """Saves the current user-defined profiles to the JSON file.""" # Ensure the directory exists try: os.makedirs(os.path.dirname(self.profiles_path), exist_ok=True) except OSError as e: module_logger.error(f"Could not create directory for profiles file: {e}") return # Prepare user-defined profiles for saving (exclude the default) profiles_to_save = { name: data for name, data in self._profiles.items() if name != DEFAULT_PROFILE_NAME } try: with open(self.profiles_path, "w") as f: json.dump(profiles_to_save, f, indent=4) module_logger.info( f"Successfully saved {len(profiles_to_save)} profiles to {self.profiles_path}" ) except IOError as e: module_logger.error(f"Error saving profiles to {self.profiles_path}: {e}") def get_profile_names(self) -> List[str]: """Returns a sorted list of all available profile names.""" return sorted(self._profiles.keys()) def get_profile_data(self, name: str) -> Optional[Dict[str, float]]: """Returns the BBox data for a given profile name.""" return self._profiles.get(name) def save_profile(self, name: str, data: Dict[str, float]) -> bool: """ Saves a new or updated profile. Returns False if name is invalid or default. """ if not name or name == DEFAULT_PROFILE_NAME: module_logger.warning( f"Attempt to save invalid or default profile name: '{name}'" ) return False self._profiles[name] = data self._save_profiles() return True def delete_profile(self, name: str) -> bool: """Deletes a profile. Returns False if the profile is default or not found.""" if name == DEFAULT_PROFILE_NAME: module_logger.warning("Cannot delete the default profile.") return False if name in self._profiles: del self._profiles[name] self._save_profiles() return True module_logger.warning(f"Profile '{name}' not found for deletion.") return False