""" ScanLook Core Database Migration System IMPORTANT: This file only contains CORE system migrations. Module-specific migrations are in each module's migrations.py file. """ import sqlite3 import os DB_PATH = os.path.join(os.path.dirname(__file__), 'database', 'scanlook.db') def get_db(): """Get database connection""" conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def init_migrations_table(): """Create the migrations tracking table if it doesn't exist""" conn = get_db() conn.execute(''' CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, name TEXT NOT NULL, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ''') conn.commit() conn.close() def get_applied_migrations(): """Get list of already-applied migration versions""" conn = get_db() try: rows = conn.execute('SELECT version FROM schema_migrations ORDER BY version').fetchall() return [row['version'] for row in rows] except: return [] finally: conn.close() def record_migration(version, name): """Record that a migration was applied""" conn = get_db() conn.execute('INSERT INTO schema_migrations (version, name) VALUES (?, ?)', [version, name]) conn.commit() conn.close() def column_exists(table, column): """Check if a column exists in a table""" conn = get_db() cursor = conn.execute(f'PRAGMA table_info({table})') columns = [row[1] for row in cursor.fetchall()] conn.close() return column in columns def table_exists(table): """Check if a table exists""" conn = get_db() cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", [table]) exists = cursor.fetchone() is not None conn.close() return exists # ============================================ # CORE SYSTEM MIGRATIONS ONLY # ============================================ # Module-specific migrations are handled by each module's migrations.py # ============================================ def migration_001_add_modules_tables(): """Add Modules and UserModules tables (if not created by init_db)""" conn = get_db() if not table_exists('Modules'): conn.execute(''' CREATE TABLE Modules ( module_id INTEGER PRIMARY KEY AUTOINCREMENT, module_name TEXT NOT NULL, module_key TEXT UNIQUE NOT NULL, description TEXT, icon TEXT, is_active INTEGER DEFAULT 1, display_order INTEGER DEFAULT 0 ) ''') print(" Created Modules table") if not table_exists('UserModules'): conn.execute(''' CREATE TABLE UserModules ( user_module_id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, module_id INTEGER NOT NULL, granted_by INTEGER, granted_timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES Users(user_id), FOREIGN KEY (module_id) REFERENCES Modules(module_id), FOREIGN KEY (granted_by) REFERENCES Users(user_id), UNIQUE(user_id, module_id) ) ''') print(" Created UserModules table") conn.commit() conn.close() def migration_002_add_usermodules_granted_columns(): """Add granted_by and granted_timestamp to UserModules if missing""" conn = get_db() if table_exists('UserModules'): if not column_exists('UserModules', 'granted_by'): conn.execute('ALTER TABLE UserModules ADD COLUMN granted_by INTEGER') print(" Added granted_by column to UserModules") if not column_exists('UserModules', 'granted_timestamp'): conn.execute('ALTER TABLE UserModules ADD COLUMN granted_timestamp DATETIME') print(" Added granted_timestamp column to UserModules") conn.commit() conn.close() def migration_003_add_module_registry(): """Add module_registry table for new module manager system""" conn = get_db() if not table_exists('module_registry'): conn.execute(''' CREATE TABLE module_registry ( id INTEGER PRIMARY KEY AUTOINCREMENT, module_key TEXT UNIQUE NOT NULL, name TEXT NOT NULL, version TEXT NOT NULL, author TEXT, description TEXT, is_installed INTEGER DEFAULT 0, is_active INTEGER DEFAULT 0, installed_at TEXT, config_json TEXT ) ''') print(" Created module_registry table") conn.commit() conn.close() # List of CORE migrations only MIGRATIONS = [ (1, 'add_modules_tables', migration_001_add_modules_tables), (2, 'add_usermodules_granted_columns', migration_002_add_usermodules_granted_columns), (3, 'add_module_registry', migration_003_add_module_registry), ] def run_migrations(): """Run all pending core migrations""" print("šŸ”„ Checking core database migrations...") # Make sure migrations table exists init_migrations_table() # Get already-applied migrations applied = get_applied_migrations() # Run pending migrations pending = [(v, n, f) for v, n, f in MIGRATIONS if v not in applied] if not pending: print("āœ… Core database is up to date") return print(f"šŸ“¦ Running {len(pending)} core migration(s)...") for version, name, func in pending: print(f"\n Migration {version}: {name}") try: func() record_migration(version, name) print(f" āœ… Migration {version} complete") except Exception as e: print(f" āŒ Migration {version} failed: {e}") raise print("\nāœ… All core migrations complete") if __name__ == '__main__': run_migrations()