diff --git a/run_migration.py b/run_migration.py new file mode 100644 index 0000000..9d2f95a --- /dev/null +++ b/run_migration.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +""" +Run database migration on production PostgreSQL. +Adds google_opening_hours and google_photos_count columns. +""" + +import os +import sys +import site + +# Add user site-packages to path (for pip --user installs) +user_site = site.getusersitepackages() +if user_site not in sys.path: + sys.path.insert(0, user_site) + +# Use localhost for production (PostgreSQL only accepts local connections) +# See CLAUDE.md: Scripts in scripts/ must use localhost (127.0.0.1) to connect +DATABASE_URL = os.environ.get('DATABASE_URL', 'postgresql://nordabiz_app:NordaBiz2025Secure@127.0.0.1:5432/nordabiz') + +try: + import psycopg2 + from psycopg2 import sql +except ImportError: + print("ERROR: psycopg2 not installed. Run: pip install psycopg2-binary") + sys.exit(1) + +MIGRATION_SQL = """ +-- Add google_opening_hours column if not exists +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'company_website_analysis' + AND column_name = 'google_opening_hours' + ) THEN + ALTER TABLE company_website_analysis ADD COLUMN google_opening_hours JSONB; + RAISE NOTICE 'Added google_opening_hours column'; + ELSE + RAISE NOTICE 'google_opening_hours column already exists'; + END IF; +END $$; + +-- Add google_photos_count column if not exists +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'company_website_analysis' + AND column_name = 'google_photos_count' + ) THEN + ALTER TABLE company_website_analysis ADD COLUMN google_photos_count INTEGER; + RAISE NOTICE 'Added google_photos_count column'; + ELSE + RAISE NOTICE 'google_photos_count column already exists'; + END IF; +END $$; + +-- Grant permissions +GRANT ALL ON TABLE company_website_analysis TO nordabiz_app; +""" + +def run_migration(): + print(f"Connecting to database...") + print(f"URL: {DATABASE_URL.replace('NordaBiz2025Secure', '****')}") + + try: + conn = psycopg2.connect(DATABASE_URL) + conn.autocommit = True + cursor = conn.cursor() + + print("Running migration...") + cursor.execute(MIGRATION_SQL) + + # Verify columns exist + cursor.execute(""" + SELECT column_name, data_type + FROM information_schema.columns + WHERE table_name = 'company_website_analysis' + AND column_name IN ('google_opening_hours', 'google_photos_count') + ORDER BY column_name; + """) + + results = cursor.fetchall() + print("\nVerification - Columns found:") + for row in results: + print(f" - {row[0]}: {row[1]}") + + if len(results) == 2: + print("\n✅ Migration completed successfully!") + return True + else: + print(f"\n❌ Expected 2 columns, found {len(results)}") + return False + + except psycopg2.OperationalError as e: + print(f"\n❌ Connection error: {e}") + print("\nThis script must be run from a machine that can reach the PostgreSQL server.") + print("Try running on the production server itself using localhost connection.") + return False + except Exception as e: + print(f"\n❌ Error: {e}") + return False + finally: + if 'cursor' in locals(): + cursor.close() + if 'conn' in locals(): + conn.close() + +if __name__ == '__main__': + success = run_migration() + sys.exit(0 if success else 1)