#!/usr/bin/env python3 """ Database Migration Verification Script Checks for orphaned SiteBlueprint tables and verifies new migrations """ import os import sys import django # Setup Django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings') django.setup() from django.db import connection from django.core.management import call_command def check_orphaned_tables(): """Check for orphaned blueprint tables""" print("\n" + "="*60) print("CHECKING FOR ORPHANED SITEBLUEPRINT TABLES") print("="*60 + "\n") with connection.cursor() as cursor: cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name LIKE '%blueprint%' ORDER BY table_name; """) tables = cursor.fetchall() if tables: print("⚠️ Found blueprint-related tables:") for table in tables: print(f" - {table[0]}") print("\n💡 These tables can be safely dropped if no longer needed.") else: print("✅ No orphaned blueprint tables found.") return len(tables) if tables else 0 def verify_cluster_constraint(): """Verify cluster unique constraint is per-site/sector""" print("\n" + "="*60) print("VERIFYING CLUSTER UNIQUE CONSTRAINT") print("="*60 + "\n") with connection.cursor() as cursor: cursor.execute(""" SELECT tc.constraint_name, tc.constraint_type, string_agg(kcu.column_name, ', ' ORDER BY kcu.ordinal_position) as columns FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema WHERE tc.table_name = 'igny8_clusters' AND tc.constraint_type = 'UNIQUE' GROUP BY tc.constraint_name, tc.constraint_type; """) constraints = cursor.fetchall() if constraints: print("Found unique constraints on igny8_clusters:") for constraint in constraints: name, ctype, columns = constraint print(f" {name}: {columns}") # Check if it includes site and sector if 'site' in columns.lower() and 'sector' in columns.lower(): print(f" ✅ Constraint is scoped per-site/sector") else: print(f" ⚠️ Constraint may need updating") else: print("⚠️ No unique constraints found on igny8_clusters") def verify_automation_delays(): """Verify automation delay fields exist""" print("\n" + "="*60) print("VERIFYING AUTOMATION DELAY CONFIGURATION") print("="*60 + "\n") with connection.cursor() as cursor: cursor.execute(""" SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_name = 'igny8_automationconfig' AND column_name IN ('within_stage_delay', 'between_stage_delay') ORDER BY column_name; """) columns = cursor.fetchall() if len(columns) == 2: print("✅ Delay configuration fields found:") for col in columns: name, dtype, default = col print(f" {name}: {dtype} (default: {default})") else: print(f"⚠️ Expected 2 delay fields, found {len(columns)}") def check_migration_status(): """Check migration status""" print("\n" + "="*60) print("CHECKING MIGRATION STATUS") print("="*60 + "\n") with connection.cursor() as cursor: cursor.execute(""" SELECT app, name, applied FROM django_migrations WHERE name LIKE '%cluster%' OR name LIKE '%delay%' ORDER BY applied DESC LIMIT 10; """) migrations = cursor.fetchall() if migrations: print("Recent relevant migrations:") for mig in migrations: app, name, applied = mig status = "✅" if applied else "⏳" print(f" {status} {app}.{name}") print(f" Applied: {applied}") else: print("No relevant migrations found in history") def check_data_integrity(): """Check for data integrity issues""" print("\n" + "="*60) print("DATA INTEGRITY CHECKS") print("="*60 + "\n") from igny8_core.business.planning.models import Clusters, Keywords # Check for clusters with 'active' status (should all be 'new' or 'mapped') active_clusters = Clusters.objects.filter(status='active').count() if active_clusters > 0: print(f"⚠️ Found {active_clusters} clusters with status='active'") print(" These should be updated to 'new' or 'mapped'") else: print("✅ No clusters with invalid 'active' status") # Check for duplicate cluster names in same site/sector with connection.cursor() as cursor: cursor.execute(""" SELECT name, site_id, sector_id, COUNT(*) as count FROM igny8_clusters GROUP BY name, site_id, sector_id HAVING COUNT(*) > 1; """) duplicates = cursor.fetchall() if duplicates: print(f"\n⚠️ Found {len(duplicates)} duplicate cluster names in same site/sector:") for dup in duplicates[:5]: # Show first 5 print(f" - '{dup[0]}' (site={dup[1]}, sector={dup[2]}): {dup[3]} duplicates") else: print("✅ No duplicate cluster names within same site/sector") def main(): print("\n" + "#"*60) print("# IGNY8 DATABASE MIGRATION VERIFICATION") print("# Date:", __import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')) print("#"*60) try: orphaned = check_orphaned_tables() verify_cluster_constraint() verify_automation_delays() check_migration_status() check_data_integrity() print("\n" + "="*60) print("VERIFICATION COMPLETE") print("="*60) if orphaned > 0: print(f"\n⚠️ {orphaned} orphaned table(s) found - review recommended") else: print("\n✅ All verifications passed!") print("\n") except Exception as e: print(f"\n❌ ERROR: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == '__main__': main()