FG Scan form validation improvements with warehouse module updates
- Fixed 3 JavaScript syntax errors in fg_scan.html (lines 951, 840-950, 1175-1215) - Restored form field validation with proper null safety checks - Re-enabled auto-advance between form fields - Re-enabled CP code auto-complete with hyphen detection - Updated validation error messages with clear format specifications and examples - Added autocomplete='off' to all input fields - Removed auto-prefix correction feature - Updated warehouse routes and modules for box assignment workflow - Added/improved database initialization scripts - Updated requirements.txt dependencies Format specifications implemented: - Operator Code: OP + 2 digits (example: OP01, OP99) - CP Code: CP + 8 digits + hyphen + 4 digits (example: CP00000000-0001) - OC1/OC2 Codes: OC + 2 digits (example: OC01, OC99) - Defect Code: 3 digits only
This commit is contained in:
585
init_db.py
585
init_db.py
@@ -1,59 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Database initialization script
|
||||
Creates required tables for the application
|
||||
Run this script to initialize the database
|
||||
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 app.config import Config
|
||||
from pathlib import Path
|
||||
from app.db_schema_verifier import SchemaVerifier
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='[%(asctime)s] %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Database configuration from environment or Config
|
||||
try:
|
||||
from app.config import Config
|
||||
DB_HOST = Config.DB_HOST
|
||||
DB_PORT = Config.DB_PORT
|
||||
DB_USER = Config.DB_USER
|
||||
DB_PASSWORD = Config.DB_PASSWORD
|
||||
DB_NAME = Config.DB_NAME
|
||||
except ImportError:
|
||||
# Fallback to environment variables if Config not available
|
||||
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 create_database():
|
||||
"""Create the database if it doesn't exist"""
|
||||
def execute_sql(conn, sql, params=None, description=""):
|
||||
"""Execute SQL statement and log result"""
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
user=Config.DB_USER,
|
||||
password=Config.DB_PASSWORD,
|
||||
host=Config.DB_HOST,
|
||||
port=Config.DB_PORT
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
if params:
|
||||
cursor.execute(sql, params)
|
||||
else:
|
||||
cursor.execute(sql)
|
||||
|
||||
# Create database
|
||||
cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{Config.DB_NAME}`")
|
||||
logger.info(f"Database {Config.DB_NAME} created or already exists")
|
||||
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"Error creating database: {e}")
|
||||
raise
|
||||
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 application tables"""
|
||||
"""Create all application tables"""
|
||||
logger.info("\nStep 2: Creating tables...")
|
||||
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
user=Config.DB_USER,
|
||||
password=Config.DB_PASSWORD,
|
||||
host=Config.DB_HOST,
|
||||
port=Config.DB_PORT,
|
||||
database=Config.DB_NAME
|
||||
host=DB_HOST,
|
||||
port=DB_PORT,
|
||||
user=DB_USER,
|
||||
password=DB_PASSWORD,
|
||||
database=DB_NAME
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Users table
|
||||
cursor.execute("""
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(255) UNIQUE NOT NULL,
|
||||
@@ -64,11 +177,10 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'users' created or already exists")
|
||||
""", description="Table 'users'")
|
||||
|
||||
# User credentials table
|
||||
cursor.execute("""
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS user_credentials (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
@@ -77,11 +189,10 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'user_credentials' created or already exists")
|
||||
""", description="Table 'user_credentials'")
|
||||
|
||||
# Quality inspections table
|
||||
cursor.execute("""
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS quality_inspections (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
inspection_type VARCHAR(100),
|
||||
@@ -93,11 +204,10 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'quality_inspections' created or already exists")
|
||||
""", description="Table 'quality_inspections'")
|
||||
|
||||
# Settings table
|
||||
cursor.execute("""
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS application_settings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
setting_key VARCHAR(255) UNIQUE NOT NULL,
|
||||
@@ -106,11 +216,52 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'application_settings' created or already exists")
|
||||
""", 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
|
||||
cursor.execute("""
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) UNIQUE NOT NULL,
|
||||
@@ -119,11 +270,10 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'roles' created or already exists")
|
||||
""", description="Table 'roles'")
|
||||
|
||||
# User modules (which modules a user has access to)
|
||||
cursor.execute("""
|
||||
# User modules table
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS user_modules (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
@@ -132,11 +282,10 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'user_modules' created or already exists")
|
||||
""", description="Table 'user_modules'")
|
||||
|
||||
# User permissions (granular permissions)
|
||||
cursor.execute("""
|
||||
# User permissions table
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS user_permissions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
@@ -149,11 +298,10 @@ def create_tables():
|
||||
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
|
||||
""")
|
||||
logger.info("Table 'user_permissions' created or already exists")
|
||||
""", description="Table 'user_permissions'")
|
||||
|
||||
# Worker-Manager bindings (for warehouse module hierarchy)
|
||||
cursor.execute("""
|
||||
execute_sql(conn, """
|
||||
CREATE TABLE IF NOT EXISTS worker_manager_bindings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
manager_id INT NOT NULL,
|
||||
@@ -167,27 +315,120 @@ def create_tables():
|
||||
FOREIGN KEY (worker_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
CHECK (manager_id != worker_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
""")
|
||||
logger.info("Table 'worker_manager_bindings' created or already exists")
|
||||
""", 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'")
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
logger.info("All tables created successfully")
|
||||
logger.info("✓ All tables created successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating tables: {e}")
|
||||
raise
|
||||
logger.error(f"✗ Failed to create tables: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def insert_default_user():
|
||||
"""Insert default admin user and roles"""
|
||||
def insert_default_data():
|
||||
"""Insert default roles and admin user"""
|
||||
logger.info("\nStep 3: Inserting default data...")
|
||||
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
user=Config.DB_USER,
|
||||
password=Config.DB_PASSWORD,
|
||||
host=Config.DB_HOST,
|
||||
port=Config.DB_PORT,
|
||||
database=Config.DB_NAME
|
||||
host=DB_HOST,
|
||||
port=DB_PORT,
|
||||
user=DB_USER,
|
||||
password=DB_PASSWORD,
|
||||
database=DB_NAME
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
@@ -201,72 +442,198 @@ def insert_default_user():
|
||||
('warehouse_worker', 'Worker - Warehouse - Input-only warehouse access', 35),
|
||||
]
|
||||
|
||||
logger.info(" Creating roles...")
|
||||
for role_name, role_desc, role_level in roles:
|
||||
cursor.execute(
|
||||
"SELECT id FROM roles WHERE name = %s",
|
||||
(role_name,)
|
||||
)
|
||||
if not cursor.fetchone():
|
||||
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")
|
||||
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 admin_result:
|
||||
logger.info("Admin user already exists")
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return
|
||||
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', '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 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'))
|
||||
# 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'),
|
||||
]
|
||||
|
||||
# Get admin user ID
|
||||
cursor.execute("SELECT id FROM users WHERE username = 'admin'")
|
||||
admin_id = cursor.fetchone()[0]
|
||||
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 admin password (default: admin123)
|
||||
password_hash = hash_password('admin123')
|
||||
cursor.execute("""
|
||||
INSERT INTO user_credentials (user_id, password_hash)
|
||||
VALUES (%s, %s)
|
||||
""", (admin_id, password_hash))
|
||||
# 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'),
|
||||
]
|
||||
|
||||
# Grant admin user access to all modules
|
||||
modules = ['quality', 'settings']
|
||||
for module in modules:
|
||||
cursor.execute("""
|
||||
INSERT IGNORE INTO user_modules (user_id, module_name)
|
||||
VALUES (%s, %s)
|
||||
""", (admin_id, module))
|
||||
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()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
logger.info("✓ Default data inserted successfully")
|
||||
return True
|
||||
|
||||
logger.info("Default admin user created (username: admin, password: admin123)")
|
||||
logger.warning("IMPORTANT: Change the default admin password after first login!")
|
||||
except Exception as e:
|
||||
logger.error(f"Error inserting default user: {e}")
|
||||
raise
|
||||
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),
|
||||
("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__':
|
||||
logger.info("Starting database initialization...")
|
||||
|
||||
try:
|
||||
create_database()
|
||||
create_tables()
|
||||
insert_default_user()
|
||||
logger.info("Database initialization completed successfully!")
|
||||
except Exception as e:
|
||||
logger.error(f"Database initialization failed: {e}")
|
||||
exit(1)
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user