"""Migration: Add custom_fields column to customers table. Sprint 6.6: Enable dynamic WordPress field synchronization. This migration adds a TEXT column for storing JSON-encoded custom fields from WordPress booking forms, enabling automatic synchronization without schema changes. Usage: python -m customer_portal.migrations.001_add_custom_fields """ import os import sys from sqlalchemy import create_engine, inspect, text def get_database_url() -> str: """Get database URL from environment.""" return os.getenv( "DATABASE_URL", os.getenv( "SQLALCHEMY_DATABASE_URI", "sqlite:///customer_portal.db", ), ) def migrate(): """Add custom_fields column to customers table.""" database_url = get_database_url() engine = create_engine(database_url) inspector = inspect(engine) columns = [col["name"] for col in inspector.get_columns("customers")] if "custom_fields" in columns: print("Column 'custom_fields' already exists. Skipping.") return True print("Adding 'custom_fields' column to customers table...") # Detect database type for appropriate SQL dialect = engine.dialect.name with engine.begin() as conn: if dialect in {"sqlite", "postgresql"}: conn.execute( text("ALTER TABLE customers ADD COLUMN custom_fields TEXT DEFAULT '{}'") ) elif dialect == "mysql": conn.execute(text("ALTER TABLE customers ADD COLUMN custom_fields TEXT")) conn.execute( text( "UPDATE customers SET custom_fields = '{}' WHERE custom_fields IS NULL" ) ) else: # Generic SQL conn.execute( text("ALTER TABLE customers ADD COLUMN custom_fields TEXT DEFAULT '{}'") ) print("Migration completed successfully.") return True def rollback(): """Remove custom_fields column (if needed).""" database_url = get_database_url() engine = create_engine(database_url) inspector = inspect(engine) columns = [col["name"] for col in inspector.get_columns("customers")] if "custom_fields" not in columns: print("Column 'custom_fields' does not exist. Skipping rollback.") return True print("WARNING: Rolling back will DELETE all custom field data!") dialect = engine.dialect.name with engine.begin() as conn: if dialect == "sqlite": # SQLite doesn't support DROP COLUMN directly print("SQLite does not support DROP COLUMN. Manual migration required.") return False else: conn.execute(text("ALTER TABLE customers DROP COLUMN custom_fields")) print("Rollback completed.") return True if __name__ == "__main__": if len(sys.argv) > 1 and sys.argv[1] == "--rollback": success = rollback() else: success = migrate() sys.exit(0 if success else 1)