""" Data service for handling JSON file operations. """ import json import os import aiofiles from typing import Any, List, Dict, Optional from pathlib import Path from app.core.config import settings from app.core.logging import setup_logging logger = setup_logging() class DataService: """Service for handling data persistence in JSON files.""" def __init__(self): """Initialize data service.""" self.data_dir = Path(settings.DATA_DIR) self.data_dir.mkdir(exist_ok=True) def _get_file_path(self, filename: str) -> Path: """Get full path for a data file.""" return self.data_dir / filename async def load_json(self, filename: str, default: Any = None) -> Any: """ Load JSON data from file. Args: filename: Name of the JSON file default: Default value if file doesn't exist or is invalid Returns: Loaded data or default value """ file_path = self._get_file_path(filename) if not file_path.exists(): logger.warning(f"File {filename} not found, returning default") return default if default is not None else [] try: async with aiofiles.open(file_path, 'r', encoding='utf-8') as f: content = await f.read() return json.loads(content) except json.JSONDecodeError as e: logger.error(f"Error decoding JSON from {filename}: {e}") return default if default is not None else [] except Exception as e: logger.error(f"Error loading {filename}: {e}") return default if default is not None else [] async def save_json(self, filename: str, data: Any) -> bool: """ Save data to JSON file. Args: filename: Name of the JSON file data: Data to save Returns: True if successful, False otherwise """ file_path = self._get_file_path(filename) try: # Create backup of existing file if it exists if file_path.exists(): backup_path = file_path.with_suffix('.json.bak') async with aiofiles.open(file_path, 'r', encoding='utf-8') as src: async with aiofiles.open(backup_path, 'w', encoding='utf-8') as dst: await dst.write(await src.read()) # Save new data async with aiofiles.open(file_path, 'w', encoding='utf-8') as f: await f.write(json.dumps(data, indent=2, ensure_ascii=False)) logger.info(f"Saved data to {filename}") return True except Exception as e: logger.error(f"Error saving {filename}: {e}") return False async def load_prompts_historic(self) -> List[Dict[str, str]]: """Load historic prompts from JSON file.""" return await self.load_json( settings.PROMPTS_HISTORIC_FILE, default=[] ) async def save_prompts_historic(self, prompts: List[Dict[str, str]]) -> bool: """Save historic prompts to JSON file.""" return await self.save_json(settings.PROMPTS_HISTORIC_FILE, prompts) async def load_prompts_pool(self) -> List[str]: """Load prompt pool from JSON file.""" return await self.load_json( settings.PROMPTS_POOL_FILE, default=[] ) async def save_prompts_pool(self, prompts: List[str]) -> bool: """Save prompt pool to JSON file.""" return await self.save_json(settings.PROMPTS_POOL_FILE, prompts) async def load_feedback_words(self) -> List[Dict[str, Any]]: """Load feedback words from JSON file.""" return await self.load_json( settings.FEEDBACK_WORDS_FILE, default=[] ) async def save_feedback_words(self, feedback_words: List[Dict[str, Any]]) -> bool: """Save feedback words to JSON file.""" return await self.save_json(settings.FEEDBACK_WORDS_FILE, feedback_words) async def load_feedback_historic(self) -> List[Dict[str, str]]: """Load historic feedback words from JSON file.""" return await self.load_json( settings.FEEDBACK_HISTORIC_FILE, default=[] ) async def save_feedback_historic(self, feedback_words: List[Dict[str, str]]) -> bool: """Save historic feedback words to JSON file.""" return await self.save_json(settings.FEEDBACK_HISTORIC_FILE, feedback_words) async def load_prompt_template(self) -> str: """Load prompt template from file.""" template_path = Path(settings.PROMPT_TEMPLATE_PATH) if not template_path.exists(): logger.error(f"Prompt template not found at {template_path}") return "" try: async with aiofiles.open(template_path, 'r', encoding='utf-8') as f: return await f.read() except Exception as e: logger.error(f"Error loading prompt template: {e}") return "" async def load_feedback_template(self) -> str: """Load feedback template from file.""" template_path = Path(settings.FEEDBACK_TEMPLATE_PATH) if not template_path.exists(): logger.error(f"Feedback template not found at {template_path}") return "" try: async with aiofiles.open(template_path, 'r', encoding='utf-8') as f: return await f.read() except Exception as e: logger.error(f"Error loading feedback template: {e}") return "" async def load_settings_config(self) -> Dict[str, Any]: """Load settings from config file.""" config_path = Path(settings.SETTINGS_CONFIG_PATH) if not config_path.exists(): logger.warning(f"Settings config not found at {config_path}") return {} try: import configparser config = configparser.ConfigParser() config.read(config_path) settings_dict = {} if 'prompts' in config: prompts_section = config['prompts'] settings_dict['min_length'] = int(prompts_section.get('min_length', settings.MIN_PROMPT_LENGTH)) settings_dict['max_length'] = int(prompts_section.get('max_length', settings.MAX_PROMPT_LENGTH)) settings_dict['num_prompts'] = int(prompts_section.get('num_prompts', settings.NUM_PROMPTS_PER_SESSION)) if 'prefetch' in config: prefetch_section = config['prefetch'] settings_dict['cached_pool_volume'] = int(prefetch_section.get('cached_pool_volume', settings.CACHED_POOL_VOLUME)) return settings_dict except Exception as e: logger.error(f"Error loading settings config: {e}") return {}