Source code for dbs_annotator.utils.program_config_manager

"""Program configuration manager for custom program names.

This module handles loading, saving, and managing custom program names
used in the DBS clinical programming interface. Config is persisted under
the platform's per-user application data directory so it survives app
reinstalls and upgrades.
"""

from __future__ import annotations

import json
from pathlib import Path

from .user_data import user_config_file, user_data_dir


[docs] class ProgramConfigManager: """Manages program name configuration with persistence.""" DEFAULT_PROGRAMS = ["None", "A", "B", "C", "D"] CONFIG_FILENAME = "program_names.json" def __init__(self, config_dir: str | None = None): """Initialize the program config manager. Args: config_dir: Directory for config files. If None, uses the platform-appropriate per-user data directory (upgrade-safe). Explicit paths are primarily for tests. """ if config_dir is None: self.config_dir = user_data_dir() self.config_file = user_config_file(self.CONFIG_FILENAME) else: self.config_dir = Path(config_dir) self.config_dir.mkdir(parents=True, exist_ok=True) self.config_file = self.config_dir / self.CONFIG_FILENAME self._custom_programs: list[str] = [] self._load_custom_programs() def _load_custom_programs(self) -> None: """Load custom program names from config file.""" if self.config_file.exists(): try: with open(self.config_file, encoding="utf-8") as f: data = json.load(f) self._custom_programs = data.get("custom_programs", []) except (OSError, json.JSONDecodeError): self._custom_programs = [] else: self._custom_programs = [] def save_custom_programs(self, programs: list[str]) -> None: """Save custom program names to config file. Args: programs: List of custom program names to save. """ self._custom_programs = programs data = {"custom_programs": programs} try: with open(self.config_file, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) except OSError: pass def get_all_programs(self) -> list[str]: """Get all available programs (default + custom). Returns: List of program names. """ return self.DEFAULT_PROGRAMS + self._custom_programs def get_custom_programs(self) -> list[str]: """Get only custom program names. Returns: List of custom program names. """ return self._custom_programs.copy() def add_program(self, program_name: str) -> bool: """Add a new custom program name. Args: program_name: Name of the program to add. Returns: True if added, False if already exists or invalid. """ if ( not program_name or program_name in self.DEFAULT_PROGRAMS or program_name in self._custom_programs ): return False self._custom_programs.append(program_name) self.save_custom_programs(self._custom_programs) return True def remove_program(self, program_name: str) -> bool: """Remove a custom program name. Args: program_name: Name of the program to remove. Returns: True if removed, False if not found or is a default program. """ if program_name in self.DEFAULT_PROGRAMS: return False if program_name in self._custom_programs: self._custom_programs.remove(program_name) self.save_custom_programs(self._custom_programs) return True return False def update_program(self, old_name: str, new_name: str) -> bool: """Update an existing custom program name. Args: old_name: Current name of the program. new_name: New name for the program. Returns: True if updated, False if old_name not found or new_name invalid. """ if old_name in self.DEFAULT_PROGRAMS: return False if ( not new_name or new_name in self.DEFAULT_PROGRAMS or new_name in self._custom_programs ): return False if old_name in self._custom_programs: idx = self._custom_programs.index(old_name) self._custom_programs[idx] = new_name self.save_custom_programs(self._custom_programs) return True return False
# Singleton instance _instance: ProgramConfigManager | None = None
[docs] def get_program_config_manager() -> ProgramConfigManager: """Get the singleton ProgramConfigManager instance. Returns: The singleton instance. """ global _instance if _instance is None: _instance = ProgramConfigManager() return _instance