Files
service-finder/backend/scripts/purge_test_data_final.py
2026-06-04 07:26:22 +00:00

325 lines
13 KiB
Python

#!/usr/bin/env python3
"""
Final Ground Zero Database Purge Script
Deletes ALL test users in correct cascade order.
"""
import asyncio
import sys
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
# Import settings
sys.path.insert(0, '/app')
from app.core.config import settings
async def purge_test_data_final():
"""Final purge function with proper cascade order"""
# Create async engine
engine = create_async_engine(
str(settings.SQLALCHEMY_DATABASE_URI),
echo=False,
pool_size=5,
max_overflow=10,
)
async_session = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
async with async_session() as session:
try:
print("🔍 Starting Ground Zero database purge (final)...")
print("=" * 60)
# Start transaction
await session.begin()
# 1. First, identify test users to delete
test_email_patterns = [
'%test%',
'%noreply%',
'%profibot%',
'tester_pro@profibot.hu',
'test@example.com',
'%example.com',
'%@gmail.com'
]
# Build WHERE clause for test users
where_conditions = []
for pattern in test_email_patterns:
where_conditions.append(f"email ILIKE '{pattern}'")
where_clause = " OR ".join(where_conditions)
# 2. Get user IDs to delete
get_users_query = text(f"""
SELECT id, email
FROM identity.users
WHERE {where_clause}
AND is_deleted = false
ORDER BY id
""")
result = await session.execute(get_users_query)
test_users = result.fetchall()
if not test_users:
print("✅ No test users found to delete.")
await session.commit()
return
user_ids = [str(user[0]) for user in test_users]
user_emails = [user[1] for user in test_users]
user_ids_str = ",".join(user_ids)
print(f"📋 Found {len(test_users)} test users to delete:")
for email in user_emails[:10]:
print(f" - {email}")
if len(test_users) > 10:
print(f" ... and {len(test_users) - 10} more")
# 3. Delete in correct cascade order
# a) First delete from tables that reference users but have no further dependencies
print("\n🗑️ Deleting related records in cascade order...")
# Tables to delete (in order of dependencies)
tables_to_clean = [
# Gamification tables
("gamification.user_stats", "user_id"),
("gamification.user_badges", "user_id"),
("gamification.points_ledger", "user_id"),
("gamification.user_contributions", "user_id"),
# Social tables (skip if they don't exist)
("identity.social_accounts", "user_id"),
("marketplace.service_requests", "user_id"),
# Organization members first (before organizations)
("marketplace.organization_members", "user_id"),
# Payment intents
("marketplace.payment_intents", "payer_id"),
("marketplace.payment_intents", "beneficiary_id"),
# Withdrawal requests
("marketplace.withdrawal_requests", "user_id"),
# Service reviews (if table exists)
("identity.service_reviews", "user_id"),
# Vehicle ratings
("vehicle.vehicle_user_ratings", "user_id"),
# Vehicle ownership
("vehicle.vehicle_ownership", "user_id"),
]
deleted_counts = {}
for table, column in tables_to_clean:
try:
# Check if table exists by trying to count
check_query = text(f"""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = '{table.split('.')[0]}'
AND table_name = '{table.split('.')[1]}'
)
""")
exists_result = await session.execute(check_query)
exists = exists_result.scalar()
if not exists:
print(f" ⚠️ Table {table} doesn't exist, skipping...")
continue
delete_query = text(f"""
DELETE FROM {table}
WHERE {column} IN ({user_ids_str})
""")
result = await session.execute(delete_query)
deleted = result.rowcount
deleted_counts[f"{table}.{column}"] = deleted
if deleted > 0:
print(f" ✅ Deleted {deleted} rows from {table}")
except Exception as e:
if "does not exist" in str(e) or "UndefinedTableError" in str(e):
print(f" ⚠️ Table {table} doesn't exist, skipping...")
continue
else:
print(f" ⚠️ Error deleting from {table}: {e}")
continue
# b) Delete wallets (references users)
try:
wallet_query = text(f"""
DELETE FROM identity.wallets
WHERE user_id IN ({user_ids_str})
""")
result = await session.execute(wallet_query)
deleted = result.rowcount
deleted_counts["identity.wallets"] = deleted
print(f" ✅ Deleted {deleted} wallets")
except Exception as e:
print(f" ⚠️ Error deleting wallets: {e}")
# c) Delete organizations owned by these users
try:
# Get organization IDs
org_query = text(f"""
SELECT id FROM marketplace.organizations
WHERE owner_id IN ({user_ids_str})
""")
org_result = await session.execute(org_query)
org_ids = [str(row[0]) for row in org_result.fetchall()]
if org_ids:
org_ids_str = ",".join(org_ids)
# Delete branches for these organizations
branch_query = text(f"""
DELETE FROM marketplace.branches
WHERE organization_id IN ({org_ids_str})
""")
result = await session.execute(branch_query)
deleted = result.rowcount
deleted_counts["marketplace.branches"] = deleted
print(f" ✅ Deleted {deleted} branches")
# Delete organization members for these organizations
org_member_query = text(f"""
DELETE FROM marketplace.organization_members
WHERE organization_id IN ({org_ids_str})
""")
result = await session.execute(org_member_query)
deleted = result.rowcount
deleted_counts["marketplace.organization_members_org"] = deleted
print(f" ✅ Deleted {deleted} organization members (by org)")
# Delete organizations
org_delete_query = text(f"""
DELETE FROM marketplace.organizations
WHERE id IN ({org_ids_str})
""")
result = await session.execute(org_delete_query)
deleted = result.rowcount
deleted_counts["marketplace.organizations"] = deleted
print(f" ✅ Deleted {deleted} organizations")
except Exception as e:
print(f" ⚠️ Error deleting organizations: {e}")
# d) Delete assets owned by these users
try:
# Get asset IDs
asset_query = text(f"""
SELECT id FROM data.assets
WHERE owner_id IN ({user_ids_str})
""")
asset_result = await session.execute(asset_query)
asset_ids = [str(row[0]) for row in asset_result.fetchall()]
if asset_ids:
asset_ids_str = ",".join(asset_ids)
# Delete asset costs
asset_cost_query = text(f"""
DELETE FROM data.asset_costs
WHERE asset_id IN ({asset_ids_str})
""")
result = await session.execute(asset_cost_query)
deleted = result.rowcount
deleted_counts["data.asset_costs"] = deleted
print(f" ✅ Deleted {deleted} asset costs")
# Delete asset events
asset_event_query = text(f"""
DELETE FROM data.asset_events
WHERE asset_id IN ({asset_ids_str})
""")
result = await session.execute(asset_event_query)
deleted = result.rowcount
deleted_counts["data.asset_events"] = deleted
print(f" ✅ Deleted {deleted} asset events")
# Delete assets
asset_delete_query = text(f"""
DELETE FROM data.assets
WHERE id IN ({asset_ids_str})
""")
result = await session.execute(asset_delete_query)
deleted = result.rowcount
deleted_counts["data.assets"] = deleted
print(f" ✅ Deleted {deleted} assets")
except Exception as e:
print(f" ⚠️ Error deleting assets: {e}")
# e) Finally delete users
print("\n🗑️ Deleting test users...")
user_delete_query = text(f"""
DELETE FROM identity.users
WHERE id IN ({user_ids_str})
AND is_deleted = false
""")
result = await session.execute(user_delete_query)
deleted_users = result.rowcount
deleted_counts["identity.users"] = deleted_users
print(f" ✅ Deleted {deleted_users} users")
# f) Delete orphaned persons
person_delete_query = text("""
DELETE FROM identity.persons
WHERE NOT EXISTS (
SELECT 1 FROM identity.users
WHERE users.person_id = persons.id
)
""")
result = await session.execute(person_delete_query)
deleted_persons = result.rowcount
deleted_counts["identity.persons"] = deleted_persons
if deleted_persons > 0:
print(f" ✅ Deleted {deleted_persons} orphaned persons")
# Commit transaction
await session.commit()
print("=" * 60)
print("✅ Ground Zero purge completed successfully!")
print("\n📊 Deletion Summary:")
total_deleted = 0
for key, count in deleted_counts.items():
print(f" {key}: {count} rows")
total_deleted += count
print(f"\n📈 Total rows deleted: {total_deleted}")
# Verify cleanup
print("\n🔍 Verifying cleanup...")
verify_query = text(f"""
SELECT COUNT(*)
FROM identity.users
WHERE {where_clause}
AND is_deleted = false
""")
result = await session.execute(verify_query)
remaining = result.scalar()
if remaining == 0:
print(f"✅ Verification: All test users have been removed.")
else:
print(f"⚠️ Verification: {remaining} test users still remain.")
return deleted_counts
except Exception as e:
await session.rollback()
print(f"❌ Error during purge: {e}")
raise
if __name__ == "__main__":
asyncio.run(purge_test_data_final())