#!/usr/bin/env python3 """ Comprehensive Database Initialization Script Creates all required tables and initializes default data Includes schema verification to check existing databases for correctness This script should be run once when the application starts """ import pymysql import os import sys import logging import hashlib from pathlib import Path from app.db_schema_verifier import SchemaVerifier # Setup logging logging.basicConfig( level=logging.INFO, format='[%(asctime)s] %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # Database configuration from environment DB_HOST = os.getenv('DB_HOST', 'mariadb') DB_PORT = int(os.getenv('DB_PORT', '3306')) DB_USER = os.getenv('DB_USER', 'quality_user') DB_PASSWORD = os.getenv('DB_PASSWORD', 'quality_pass') DB_NAME = os.getenv('DB_NAME', 'quality_db') def hash_password(password): """Hash password using SHA256""" return hashlib.sha256(password.encode()).hexdigest() def execute_sql(conn, sql, params=None, description=""): """Execute SQL statement and log result""" try: cursor = conn.cursor() if params: cursor.execute(sql, params) else: cursor.execute(sql) if description: logger.info(f"✓ {description}") cursor.close() return True except pymysql.Error as e: if "already exists" in str(e).lower() or "duplicate" in str(e).lower(): if description: logger.info(f"✓ {description} (already exists)") return True logger.error(f"✗ SQL Error: {e}") return False except Exception as e: logger.error(f"✗ Unexpected Error: {e}") return False def check_and_repair_database(): """ Check existing database for correct structure Repair any missing tables, columns, or reference data """ logger.info("Step 0: Checking existing database structure...") try: # First check if database exists conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD ) cursor = conn.cursor() cursor.execute(f"SHOW DATABASES LIKE %s", (DB_NAME,)) if not cursor.fetchone(): # Database doesn't exist, skip verification logger.info(" ℹ Database doesn't exist yet, skipping structure check") conn.close() return True cursor.close() conn.close() # Database exists, now connect to it and verify/repair structure conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, database=DB_NAME ) # Run schema verification and repair verifier = SchemaVerifier(conn) success, summary = verifier.verify_and_repair() # Log the summary for line in summary.split('\n'): if line.strip(): logger.info(f" {line}") conn.close() return success except pymysql.Error as e: if "Unknown database" in str(e): logger.info(" ℹ Database doesn't exist yet, skipping structure check") return True logger.error(f"✗ Database check failed: {e}") return False except Exception as e: logger.error(f"✗ Database check error: {e}") return False def create_database(): """Create the database if it doesn't exist""" logger.info("Step 1: Creating database...") try: conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD ) cursor = conn.cursor() cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{DB_NAME}`") cursor.close() conn.close() logger.info(f"✓ Database '{DB_NAME}' created or already exists") return True except Exception as e: logger.error(f"✗ Failed to create database: {e}") return False def create_tables(): """Create all application tables""" logger.info("\nStep 2: Creating tables...") try: conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, database=DB_NAME ) # Users table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255), full_name VARCHAR(255), role VARCHAR(50) DEFAULT 'user', is_active TINYINT(1) DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'users'") # User credentials table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS user_credentials ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'user_credentials'") # Quality inspections table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS quality_inspections ( id INT AUTO_INCREMENT PRIMARY KEY, inspection_type VARCHAR(100), status VARCHAR(50), inspector_id INT, inspection_date DATETIME DEFAULT CURRENT_TIMESTAMP, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (inspector_id) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'quality_inspections'") # Settings table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS application_settings ( id INT AUTO_INCREMENT PRIMARY KEY, setting_key VARCHAR(255) UNIQUE NOT NULL, setting_value LONGTEXT, setting_type VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'application_settings'") # QZ Tray Pairing Keys table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS qz_pairing_keys ( id INT AUTO_INCREMENT PRIMARY KEY, printer_name VARCHAR(255) NOT NULL, pairing_key VARCHAR(255) UNIQUE NOT NULL, valid_until DATE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'qz_pairing_keys'") # API Keys table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS api_keys ( id INT AUTO_INCREMENT PRIMARY KEY, key_name VARCHAR(255) NOT NULL, key_type VARCHAR(100) NOT NULL, api_key VARCHAR(255) UNIQUE NOT NULL, is_active TINYINT(1) DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'api_keys'") # Backup Schedules table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS backup_schedules ( id INT AUTO_INCREMENT PRIMARY KEY, schedule_name VARCHAR(255) NOT NULL, frequency VARCHAR(50) NOT NULL COMMENT 'daily or weekly', day_of_week VARCHAR(20) COMMENT 'Monday, Tuesday, etc for weekly schedules', time_of_day TIME NOT NULL COMMENT 'HH:MM format', backup_type VARCHAR(50) DEFAULT 'full' COMMENT 'full or data_only', is_active TINYINT(1) DEFAULT 1, last_run DATETIME, next_run DATETIME, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'backup_schedules'") # Roles table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS roles ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) UNIQUE NOT NULL, description TEXT, level INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'roles'") # User modules table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS user_modules ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, module_name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_user_module (user_id, module_name), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'user_modules'") # User permissions table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS user_permissions ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, module_name VARCHAR(100) NOT NULL, section_name VARCHAR(100) NOT NULL, action_name VARCHAR(100) NOT NULL, granted TINYINT(1) DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY unique_permission (user_id, module_name, section_name, action_name), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'user_permissions'") # Worker-Manager bindings (for warehouse module hierarchy) execute_sql(conn, """ CREATE TABLE IF NOT EXISTS worker_manager_bindings ( id INT AUTO_INCREMENT PRIMARY KEY, manager_id INT NOT NULL, worker_id INT NOT NULL, warehouse_zone VARCHAR(100), is_active TINYINT(1) DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY unique_binding (manager_id, worker_id), FOREIGN KEY (manager_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (worker_id) REFERENCES users(id) ON DELETE CASCADE, CHECK (manager_id != worker_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'worker_manager_bindings'") # Warehouse Locations table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS warehouse_locations ( id BIGINT AUTO_INCREMENT PRIMARY KEY, location_code VARCHAR(12) NOT NULL UNIQUE, size INT, description VARCHAR(250), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'warehouse_locations'") # Boxes Crates table (for quick box checkpoint) execute_sql(conn, """ CREATE TABLE IF NOT EXISTS boxes_crates ( id BIGINT AUTO_INCREMENT PRIMARY KEY, box_number VARCHAR(20) NOT NULL UNIQUE, status ENUM('open', 'closed') DEFAULT 'open', location_id BIGINT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, created_by INT, FOREIGN KEY (location_id) REFERENCES warehouse_locations(id) ON DELETE SET NULL, FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_box_number (box_number), INDEX idx_status (status), INDEX idx_location_id (location_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'boxes_crates'") # Box Contents table (CP-to-Box mapping) execute_sql(conn, """ CREATE TABLE IF NOT EXISTS box_contents ( id BIGINT AUTO_INCREMENT PRIMARY KEY, box_id BIGINT NOT NULL, cp_code VARCHAR(50) NOT NULL, quantity INT DEFAULT 1, added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (box_id) REFERENCES boxes_crates(id) ON DELETE CASCADE, INDEX idx_box_id (box_id), INDEX idx_cp_code (cp_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'box_contents'") # FG Scan Orders table execute_sql(conn, """ CREATE TABLE IF NOT EXISTS scanfg_orders ( id INT AUTO_INCREMENT PRIMARY KEY, operator_code VARCHAR(50), CP_full_code VARCHAR(50), OC1_code VARCHAR(50), OC2_code VARCHAR(50), quality_code VARCHAR(10), date DATE, time TIME, approved_quantity INT DEFAULT 0, rejected_quantity INT DEFAULT 0, box_id BIGINT, location_id BIGINT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (box_id) REFERENCES boxes_crates(id) ON DELETE SET NULL, FOREIGN KEY (location_id) REFERENCES warehouse_locations(id) ON DELETE SET NULL, INDEX idx_cp_code (CP_full_code), INDEX idx_operator (operator_code), INDEX idx_date (date), INDEX idx_box_id (box_id), INDEX idx_location_id (location_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'scanfg_orders'") # CP Location History table (audit trail) execute_sql(conn, """ CREATE TABLE IF NOT EXISTS cp_location_history ( id BIGINT AUTO_INCREMENT PRIMARY KEY, cp_code VARCHAR(50) NOT NULL, box_id BIGINT NOT NULL, from_location_id BIGINT, to_location_id BIGINT NOT NULL, moved_by INT, moved_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, reason VARCHAR(100), FOREIGN KEY (box_id) REFERENCES boxes_crates(id) ON DELETE CASCADE, FOREIGN KEY (from_location_id) REFERENCES warehouse_locations(id) ON DELETE SET NULL, FOREIGN KEY (to_location_id) REFERENCES warehouse_locations(id) ON DELETE CASCADE, FOREIGN KEY (moved_by) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_cp_code (cp_code), INDEX idx_box_id (box_id), INDEX idx_moved_at (moved_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'cp_location_history'") # Order For Labels table (for printing module) execute_sql(conn, """ CREATE TABLE IF NOT EXISTS order_for_labels ( id BIGINT AUTO_INCREMENT PRIMARY KEY, comanda_productie VARCHAR(50) NOT NULL, cod_articol VARCHAR(50), descr_com_prod TEXT, cantitate DECIMAL(10, 2), com_achiz_client VARCHAR(50), nr_linie_com_client VARCHAR(50), customer_name VARCHAR(255), customer_article_number VARCHAR(100), open_for_order TINYINT(1) DEFAULT 1, line_number INT, printed_labels TINYINT(1) DEFAULT 0, data_livrara DATE, dimensiune VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_comanda_productie (comanda_productie), INDEX idx_printed_labels (printed_labels), INDEX idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci """, description="Table 'order_for_labels'") conn.commit() conn.close() logger.info("✓ All tables created successfully") return True except Exception as e: logger.error(f"✗ Failed to create tables: {e}") return False def create_triggers(): """Create database triggers for automatic quantity calculations""" logger.info("\nStep 2.5: Creating database triggers...") try: conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, database=DB_NAME ) cursor = conn.cursor() # Drop existing triggers to avoid conflicts logger.info(" Dropping existing triggers...") cursor.execute("DROP TRIGGER IF EXISTS set_quantities_fg") cursor.execute("DROP TRIGGER IF EXISTS set_quantities_scan1") # Create trigger for scanfg_orders - Automatic quantity calculation logger.info(" Creating trigger for scanfg_orders...") cursor.execute(""" CREATE TRIGGER set_quantities_fg BEFORE INSERT ON scanfg_orders FOR EACH ROW BEGIN -- Count how many APPROVED entries exist for this CP_base_code SET @approved = (SELECT COUNT(*) FROM scanfg_orders WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10) AND quality_code = 0); -- Count how many REJECTED entries exist for this CP_base_code SET @rejected = (SELECT COUNT(*) FROM scanfg_orders WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10) AND quality_code != 0); -- Set quantities based on this new row's quality_code IF NEW.quality_code = 0 THEN -- Approved scan: increment approved count SET NEW.approved_quantity = @approved + 1; SET NEW.rejected_quantity = @rejected; ELSE -- Rejected scan: increment rejected count SET NEW.approved_quantity = @approved; SET NEW.rejected_quantity = @rejected + 1; END IF; END """) logger.info(" ✓ Trigger 'set_quantities_fg' created successfully") # Create trigger for scan1_orders - Automatic quantity calculation (T1 Phase) logger.info(" Creating trigger for scan1_orders...") cursor.execute(""" CREATE TRIGGER set_quantities_scan1 BEFORE INSERT ON scan1_orders FOR EACH ROW BEGIN -- Count how many APPROVED entries exist for this CP_base_code SET @approved = (SELECT COUNT(*) FROM scan1_orders WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10) AND quality_code = 0); -- Count how many REJECTED entries exist for this CP_base_code SET @rejected = (SELECT COUNT(*) FROM scan1_orders WHERE SUBSTRING(CP_full_code, 1, 10) = SUBSTRING(NEW.CP_full_code, 1, 10) AND quality_code != 0); -- Set quantities based on this new row's quality_code IF NEW.quality_code = 0 THEN -- Approved scan: increment approved count SET NEW.approved_quantity = @approved + 1; SET NEW.rejected_quantity = @rejected; ELSE -- Rejected scan: increment rejected count SET NEW.approved_quantity = @approved; SET NEW.rejected_quantity = @rejected + 1; END IF; END """) logger.info(" ✓ Trigger 'set_quantities_scan1' created successfully") conn.commit() cursor.close() conn.close() logger.info("✓ All triggers created successfully") return True except pymysql.Error as e: logger.warning(f"⚠ Trigger creation warning: {e}") # Don't fail on trigger errors - they might already exist return True except Exception as e: logger.error(f"✗ Failed to create triggers: {e}") return False def insert_default_data(): """Insert default roles and admin user""" logger.info("\nStep 3: Inserting default data...") try: conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, database=DB_NAME ) cursor = conn.cursor() # Insert default roles if they don't exist roles = [ ('superadmin', 'Super Administrator - Full system access', 100), ('admin', 'Administrator - Administrative access', 90), ('manager', 'Manager - Quality - Full access to assigned modules', 70), ('warehouse_manager', 'Manager - Warehouse - Full warehouse module access', 75), ('worker', 'Worker - Quality - Limited access', 50), ('warehouse_worker', 'Worker - Warehouse - Input-only warehouse access', 35), ] logger.info(" Creating roles...") for role_name, role_desc, role_level in roles: try: cursor.execute( "INSERT INTO roles (name, description, level) VALUES (%s, %s, %s)", (role_name, role_desc, role_level) ) logger.info(f" ✓ Role '{role_name}' created") except pymysql.Error as e: if "duplicate" in str(e).lower(): logger.info(f" ✓ Role '{role_name}' already exists") else: logger.warning(f" ⚠ Role '{role_name}': {e}") # Check if admin user exists cursor.execute("SELECT id FROM users WHERE username = 'admin'") admin_result = cursor.fetchone() if not admin_result: logger.info(" Creating default admin user...") cursor.execute( "INSERT INTO users (username, email, full_name, role, is_active) VALUES (%s, %s, %s, %s, 1)", ('admin', 'admin@quality-app.local', 'Administrator', 'admin') ) # Get admin user ID cursor.execute("SELECT id FROM users WHERE username = 'admin'") admin_id = cursor.fetchone()[0] # Insert admin password password_hash = hash_password('admin123') cursor.execute( "INSERT INTO user_credentials (user_id, password_hash) VALUES (%s, %s)", (admin_id, password_hash) ) logger.info(" ✓ Admin user created (username: admin, password: admin123)") # Grant admin user access to all modules logger.info(" Granting module access to admin user...") modules = ['quality', 'warehouse', 'labels', 'settings'] for module in modules: try: cursor.execute( "INSERT IGNORE INTO user_modules (user_id, module_name) VALUES (%s, %s)", (admin_id, module) ) logger.info(f" ✓ Module '{module}' granted to admin") except pymysql.Error as e: logger.warning(f" ⚠ Module '{module}': {e}") else: logger.info(" ✓ Admin user already exists") # Insert default warehouse locations logger.info(" Creating default warehouse locations...") warehouse_locations = [ ('FG_INCOMING', 'Finished Goods Incoming', 'Initial receiving area for finished goods from production'), ('TRUCK_LOADING', 'Truck Loading Area', 'Loading and staging area for truck shipments'), ] for location_code, location_name, description in warehouse_locations: try: cursor.execute( "INSERT IGNORE INTO warehouse_locations (location_code, size, description) VALUES (%s, %s, %s)", (location_code, 100, description) ) logger.info(f" ✓ Warehouse location '{location_code}' created ({location_name})") except pymysql.Error as e: if "duplicate" in str(e).lower(): logger.info(f" ✓ Warehouse location '{location_code}' already exists") else: logger.warning(f" ⚠ Warehouse location '{location_code}': {e}") # Insert default application settings logger.info(" Creating default application settings...") default_settings = [ ('app_name', 'Quality App v2', 'string'), ('app_version', '2.0.0', 'string'), ('session_timeout', '480', 'integer'), ('backup_retention_days', '30', 'integer'), ('backup_auto_cleanup', '0', 'boolean'), ] for setting_key, setting_value, setting_type in default_settings: try: cursor.execute( "INSERT IGNORE INTO application_settings (setting_key, setting_value, setting_type) VALUES (%s, %s, %s)", (setting_key, setting_value, setting_type) ) logger.info(f" ✓ Setting '{setting_key}' initialized") except pymysql.Error as e: logger.warning(f" ⚠ Setting '{setting_key}': {e}") conn.commit() conn.close() logger.info("✓ Default data inserted successfully") return True except Exception as e: logger.error(f"✗ Failed to insert default data: {e}") return False def verify_database(): """Verify all tables were created""" logger.info("\nStep 4: Verifying database...") try: conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, database=DB_NAME ) cursor = conn.cursor() cursor.execute("SHOW TABLES") tables = [row[0] for row in cursor.fetchall()] required_tables = [ 'users', 'user_credentials', 'quality_inspections', 'application_settings', 'roles', 'user_modules', 'user_permissions' ] logger.info(f" Database tables: {', '.join(tables)}") missing = [t for t in required_tables if t not in tables] if missing: logger.error(f" ✗ Missing tables: {', '.join(missing)}") conn.close() return False # Count records cursor.execute("SELECT COUNT(*) FROM roles") role_count = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM users") user_count = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM user_credentials") cred_count = cursor.fetchone()[0] logger.info(f" ✓ All {len(required_tables)} required tables exist") logger.info(f" ✓ Roles: {role_count}") logger.info(f" ✓ Users: {user_count}") logger.info(f" ✓ User credentials: {cred_count}") conn.close() return True except Exception as e: logger.error(f"✗ Verification failed: {e}") return False def main(): """Main initialization flow""" logger.info("=" * 60) logger.info("Database Initialization Script") logger.info("=" * 60) logger.info(f"Target: {DB_USER}@{DB_HOST}:{DB_PORT}/{DB_NAME}\n") steps = [ ("Check/repair existing database", check_and_repair_database), ("Create database", create_database), ("Create tables", create_tables), ("Create triggers", create_triggers), ("Insert default data", insert_default_data), ("Verify database", verify_database), ] failed = [] for step_name, step_func in steps: try: if not step_func(): failed.append(step_name) except Exception as e: logger.error(f"✗ {step_name} failed: {e}") failed.append(step_name) logger.info("\n" + "=" * 60) if failed: logger.error(f"✗ FAILED: {', '.join(failed)}") logger.info("=" * 60) return 1 else: logger.info("✓ Database initialization completed successfully!") logger.info("=" * 60) return 0 if __name__ == '__main__': sys.exit(main())