Files
customer-portal/customer_portal/migrations/001_add_custom_fields.py

104 lines
2.9 KiB
Python
Executable File

"""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)