325 lines
13 KiB
Python
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()) |