#!/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 # WARNING: The fallback DATABASE_URL uses a placeholder password. # Production credentials MUST be set via the DATABASE_URL environment variable. # NEVER commit real credentials to version control (CWE-798). DATABASE_URL = os.environ.get('DATABASE_URL', 'postgresql://nordabiz_app:CHANGE_ME@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)