""" User Role Migration Script (Phase 3) This script migrates existing users from the legacy role enum to the new dynamic role system. For each user, it maps their current role enum value to the corresponding role_id in the roles table. Usage: python migrate_users_to_dynamic_roles.py Environment Variables: DATABASE_URL - PostgreSQL connection string """ import os import sys from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from database import Base from models import User, Role, UserRole from dotenv import load_dotenv # Load environment variables load_dotenv() # Database connection DATABASE_URL = os.getenv("DATABASE_URL") if not DATABASE_URL: print("Error: DATABASE_URL environment variable not set") sys.exit(1) engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def migrate_users(): """Migrate users from enum role to role_id""" db = SessionLocal() try: print("šŸš€ Starting user role migration (Phase 3)...") print("="*60) # Step 1: Load all roles into a map print("\nšŸ“‹ Loading roles from database...") roles = db.query(Role).all() role_map = {role.code: role for role in roles} print(f"āœ“ Loaded {len(roles)} roles:") for role in roles: print(f" • {role.name} ({role.code}) - ID: {role.id}") # Step 2: Get all users print("\nšŸ‘„ Loading users...") users = db.query(User).all() print(f"āœ“ Found {len(users)} users to migrate") # Step 3: Check if any users already have role_id set users_with_role_id = [u for u in users if u.role_id is not None] if users_with_role_id: print(f"\nāš ļø Warning: {len(users_with_role_id)} users already have role_id set") response = input("Do you want to re-migrate these users? (yes/no): ") if response.lower() != 'yes': print("Skipping users that already have role_id set...") users = [u for u in users if u.role_id is None] print(f"Will migrate {len(users)} users without role_id") if not users: print("\nāœ… No users to migrate!") return # Step 4: Migrate users print(f"\nšŸ”„ Migrating {len(users)} users...") migration_stats = { UserRole.guest: 0, UserRole.member: 0, UserRole.admin: 0, UserRole.superadmin: 0 } for user in users: # Get the enum role code (e.g., "guest", "member", "admin", "superadmin") role_code = user.role.value # Find the matching role in the roles table if role_code not in role_map: print(f" āš ļø Warning: No matching role found for '{role_code}' (user: {user.email})") continue # Set the role_id user.role_id = role_map[role_code].id migration_stats[user.role] = migration_stats.get(user.role, 0) + 1 # Commit all changes db.commit() print(f"āœ“ Migrated {len(users)} users") # Step 5: Display migration summary print("\n" + "="*60) print("šŸ“Š Migration Summary:") print("="*60) print("\nUsers migrated by role:") for role_enum, count in migration_stats.items(): if count > 0: print(f" • {role_enum.value}: {count} users") # Step 6: Verify migration print("\nšŸ” Verifying migration...") users_without_role_id = db.query(User).filter(User.role_id == None).count() users_with_role_id = db.query(User).filter(User.role_id != None).count() print(f" • Users with role_id: {users_with_role_id}") print(f" • Users without role_id: {users_without_role_id}") if users_without_role_id > 0: print(f"\nāš ļø Warning: {users_without_role_id} users still don't have role_id set!") else: print("\nāœ… All users successfully migrated!") print("\n" + "="*60) print("āœ… User migration completed successfully!") print("="*60) print("\nšŸ“ Next Steps:") print(" 1. Migrate role_permissions table") print(" 2. Update auth.py to use dynamic roles") print(" 3. Update server.py role checks") print(" 4. Verify system still works with new roles") except Exception as e: db.rollback() print(f"\nāŒ Error migrating users: {str(e)}") import traceback traceback.print_exc() raise finally: db.close() if __name__ == "__main__": migrate_users()