Files
quality_app-v2/init_db.py
Quality App Developer 8de85ca87f feat: Implement warehouse module roles with auto-schema repair and remove module access section
- Add SchemaVerifier class for automatic database schema verification and repair
- Implement warehouse_manager (Level 75) and warehouse_worker (Level 35) roles
- Add zone-based access control for warehouse workers
- Implement worker-manager binding system with zone filtering
- Add comprehensive database auto-repair on Docker initialization
- Remove Module Access section from user form (role-based access only)
- Add autocomplete attributes to password fields for better UX
- Include detailed documentation for warehouse implementation
- Update initialize_db.py with schema verification as Step 0
2026-01-28 00:46:59 +02:00

273 lines
11 KiB
Python

"""
Database initialization script
Creates required tables for the application
Run this script to initialize the database
"""
import pymysql
import os
import logging
import hashlib
from app.config import Config
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
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"""
try:
conn = pymysql.connect(
user=Config.DB_USER,
password=Config.DB_PASSWORD,
host=Config.DB_HOST,
port=Config.DB_PORT
)
cursor = conn.cursor()
# Create database
cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{Config.DB_NAME}`")
logger.info(f"Database {Config.DB_NAME} created or already exists")
cursor.close()
conn.close()
except Exception as e:
logger.error(f"Error creating database: {e}")
raise
def create_tables():
"""Create application 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
)
cursor = conn.cursor()
# Users table
cursor.execute("""
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
""")
logger.info("Table 'users' created or already exists")
# User credentials table
cursor.execute("""
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
""")
logger.info("Table 'user_credentials' created or already exists")
# Quality inspections table
cursor.execute("""
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
""")
logger.info("Table 'quality_inspections' created or already exists")
# Settings table
cursor.execute("""
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
""")
logger.info("Table 'application_settings' created or already exists")
# Roles table
cursor.execute("""
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
""")
logger.info("Table 'roles' created or already exists")
# User modules (which modules a user has access to)
cursor.execute("""
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
""")
logger.info("Table 'user_modules' created or already exists")
# User permissions (granular permissions)
cursor.execute("""
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
""")
logger.info("Table 'user_permissions' created or already exists")
# Worker-Manager bindings (for warehouse module hierarchy)
cursor.execute("""
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
""")
logger.info("Table 'worker_manager_bindings' created or already exists")
conn.commit()
cursor.close()
conn.close()
logger.info("All tables created successfully")
except Exception as e:
logger.error(f"Error creating tables: {e}")
raise
def insert_default_user():
"""Insert default admin user and roles"""
try:
conn = pymysql.connect(
user=Config.DB_USER,
password=Config.DB_PASSWORD,
host=Config.DB_HOST,
port=Config.DB_PORT,
database=Config.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),
]
for role_name, role_desc, role_level in roles:
cursor.execute(
"SELECT id FROM roles WHERE name = %s",
(role_name,)
)
if not cursor.fetchone():
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")
# 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
# 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'))
# Get admin user ID
cursor.execute("SELECT id FROM users WHERE username = 'admin'")
admin_id = cursor.fetchone()[0]
# 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))
# 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))
conn.commit()
cursor.close()
conn.close()
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
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)