# Backup_Manager.py # # Imports: import os import shutil import sqlite3 from datetime import datetime import logging # # Local Imports: from App_Function_Libraries.DB.Character_Chat_DB import chat_DB_PATH from App_Function_Libraries.DB.RAG_QA_Chat_DB import get_rag_qa_db_path from App_Function_Libraries.Utils.Utils import get_project_relative_path # # End of Imports ####################################################################################################################### # # Functions: def init_backup_directory(backup_base_dir: str, db_name: str) -> str: """Initialize backup directory for a specific database.""" backup_dir = os.path.join(backup_base_dir, db_name) os.makedirs(backup_dir, exist_ok=True) return backup_dir def create_backup(db_path: str, backup_dir: str, db_name: str) -> str: """Create a full backup of the database.""" try: db_path = os.path.abspath(db_path) backup_dir = os.path.abspath(backup_dir) logging.info(f"Creating backup:") logging.info(f" DB Path: {db_path}") logging.info(f" Backup Dir: {backup_dir}") logging.info(f" DB Name: {db_name}") # Create subdirectory based on db_name specific_backup_dir = os.path.join(backup_dir, db_name) os.makedirs(specific_backup_dir, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") backup_file = os.path.join(specific_backup_dir, f"{db_name}_backup_{timestamp}.db") logging.info(f" Full backup path: {backup_file}") # Create a backup using SQLite's backup API with sqlite3.connect(db_path) as source, \ sqlite3.connect(backup_file) as target: source.backup(target) logging.info(f"Backup created successfully: {backup_file}") return f"Backup created: {backup_file}" except Exception as e: error_msg = f"Failed to create backup: {str(e)}" logging.error(error_msg) return error_msg def create_incremental_backup(db_path: str, backup_dir: str, db_name: str) -> str: """Create an incremental backup using VACUUM INTO.""" try: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") backup_file = os.path.join(backup_dir, f"{db_name}_incremental_{timestamp}.sqlib") with sqlite3.connect(db_path) as conn: conn.execute(f"VACUUM INTO '{backup_file}'") logging.info(f"Incremental backup created: {backup_file}") return f"Incremental backup created: {backup_file}" except Exception as e: error_msg = f"Failed to create incremental backup: {str(e)}" logging.error(error_msg) return error_msg def list_backups(backup_dir: str) -> str: """List all available backups.""" try: backups = [f for f in os.listdir(backup_dir) if f.endswith(('.db', '.sqlib'))] backups.sort(reverse=True) # Most recent first return "\n".join(backups) if backups else "No backups found" except Exception as e: error_msg = f"Failed to list backups: {str(e)}" logging.error(error_msg) return error_msg def restore_single_db_backup(db_path: str, backup_dir: str, db_name: str, backup_name: str) -> str: """Restore database from a backup file.""" try: logging.info(f"Restoring backup: {backup_name}") backup_path = os.path.join(backup_dir, backup_name) if not os.path.exists(backup_path): logging.error(f"Backup file not found: {backup_name}") return f"Backup file not found: {backup_name}" # Create a timestamp for the current db timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") current_backup = os.path.join(backup_dir, f"{db_name}_pre_restore_{timestamp}.db") # Backup current database before restore logging.info(f"Creating backup of current database: {current_backup}") shutil.copy2(db_path, current_backup) # Restore the backup logging.info(f"Restoring database from {backup_name}") shutil.copy2(backup_path, db_path) logging.info(f"Database restored from {backup_name}") return f"Database restored from {backup_name}" except Exception as e: error_msg = f"Failed to restore backup: {str(e)}" logging.error(error_msg) return error_msg def setup_backup_config(): """Setup configuration for database backups.""" backup_base_dir = get_project_relative_path('tldw_DB_Backups') logging.info(f"Base backup directory: {os.path.abspath(backup_base_dir)}") # RAG Chat DB configuration rag_db_path = get_rag_qa_db_path() rag_backup_dir = os.path.join(backup_base_dir, 'rag_chat') os.makedirs(rag_backup_dir, exist_ok=True) logging.info(f"RAG backup directory: {os.path.abspath(rag_backup_dir)}") rag_db_config = { 'db_path': rag_db_path, 'backup_dir': rag_backup_dir, # Make sure we use the full path 'db_name': 'rag_qa' } # Character Chat DB configuration char_backup_dir = os.path.join(backup_base_dir, 'character_chat') os.makedirs(char_backup_dir, exist_ok=True) logging.info(f"Character backup directory: {os.path.abspath(char_backup_dir)}") char_db_config = { 'db_path': chat_DB_PATH, 'backup_dir': char_backup_dir, # Make sure we use the full path 'db_name': 'chatDB' } # Media DB configuration (based on your logs) media_backup_dir = os.path.join(backup_base_dir, 'media') os.makedirs(media_backup_dir, exist_ok=True) logging.info(f"Media backup directory: {os.path.abspath(media_backup_dir)}") media_db_config = { 'db_path': os.path.join(os.path.dirname(chat_DB_PATH), 'media_summary.db'), 'backup_dir': media_backup_dir, 'db_name': 'media' } return rag_db_config, char_db_config, media_db_config