2026.06.04 frontend építés közben
This commit is contained in:
305
backend/scripts/purge_test_data.py
Normal file
305
backend/scripts/purge_test_data.py
Normal file
@@ -0,0 +1,305 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Ground Zero Database Purge Script
|
||||
|
||||
Deletes ALL test users (e.g., tester_pro@profibot.hu, noreply@...),
|
||||
and cascade deletes all their Organizations, Branches, and Assets.
|
||||
Outputs the exact number of deleted rows for each table.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from sqlalchemy import text, delete, select
|
||||
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():
|
||||
"""Main purge function"""
|
||||
# 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
|
||||
)
|
||||
|
||||
deleted_counts = {}
|
||||
|
||||
async with async_session() as session:
|
||||
try:
|
||||
# Start transaction
|
||||
await session.begin()
|
||||
|
||||
print("🔍 Starting Ground Zero database purge...")
|
||||
print("=" * 60)
|
||||
|
||||
# 1. First, identify test users to delete
|
||||
test_email_patterns = [
|
||||
'%test%',
|
||||
'%noreply%',
|
||||
'%profibot%',
|
||||
'tester_pro@profibot.hu',
|
||||
'test@example.com',
|
||||
'%example.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 for cascade operations
|
||||
query = text(f"""
|
||||
SELECT id, email
|
||||
FROM identity.users
|
||||
WHERE {where_clause}
|
||||
AND is_deleted = false
|
||||
""")
|
||||
|
||||
result = await session.execute(query)
|
||||
test_users = result.fetchall()
|
||||
|
||||
if not test_users:
|
||||
print("✅ No test users found to delete.")
|
||||
await session.commit()
|
||||
return deleted_counts
|
||||
|
||||
user_ids = [str(user[0]) for user in test_users]
|
||||
user_emails = [user[1] for user in test_users]
|
||||
|
||||
print(f"📋 Found {len(test_users)} test users to delete:")
|
||||
for email in user_emails[:10]: # Show first 10
|
||||
print(f" - {email}")
|
||||
if len(test_users) > 10:
|
||||
print(f" ... and {len(test_users) - 10} more")
|
||||
|
||||
user_ids_str = ",".join(user_ids)
|
||||
|
||||
# 3. Delete in reverse cascade order to respect foreign keys
|
||||
|
||||
# First, get person IDs for these users
|
||||
person_query = text(f"""
|
||||
SELECT DISTINCT person_id
|
||||
FROM identity.users
|
||||
WHERE id IN ({user_ids_str})
|
||||
AND person_id IS NOT NULL
|
||||
""")
|
||||
person_result = await session.execute(person_query)
|
||||
person_ids = [str(row[0]) for row in person_result.fetchall()]
|
||||
person_ids_str = ",".join(person_ids) if person_ids else "NULL"
|
||||
|
||||
# Delete from tables that reference users/persons
|
||||
|
||||
# a) Delete from gamification tables
|
||||
gamification_tables = [
|
||||
("gamification.user_stats", "user_id"),
|
||||
("gamification.user_badges", "user_id"),
|
||||
("gamification.points_ledger", "user_id"),
|
||||
("gamification.user_contributions", "user_id"),
|
||||
]
|
||||
|
||||
for table, column in gamification_tables:
|
||||
delete_query = text(f"""
|
||||
DELETE FROM {table}
|
||||
WHERE {column} IN ({user_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(delete_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts[table] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} rows from {table}")
|
||||
|
||||
# b) Delete from social tables - with error handling for missing tables
|
||||
social_tables = [
|
||||
("identity.social_accounts", "user_id"),
|
||||
("marketplace.service_requests", "user_id"),
|
||||
]
|
||||
|
||||
# Try to delete from service_reviews if it exists
|
||||
social_tables_to_try = social_tables + [("identity.service_reviews", "user_id")]
|
||||
|
||||
for table, column in social_tables_to_try:
|
||||
try:
|
||||
delete_query = text(f"""
|
||||
DELETE FROM {table}
|
||||
WHERE {column} IN ({user_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(delete_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts[table] = 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:
|
||||
raise
|
||||
|
||||
# c) Delete from organization-related tables
|
||||
# First get organization IDs owned by these users
|
||||
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()]
|
||||
org_ids_str = ",".join(org_ids) if org_ids else "NULL"
|
||||
|
||||
if org_ids:
|
||||
# Delete branches for these organizations
|
||||
branch_query = text(f"""
|
||||
DELETE FROM marketplace.branches
|
||||
WHERE organization_id IN ({org_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(branch_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["marketplace.branches"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} branches")
|
||||
|
||||
# Delete organization members
|
||||
org_member_query = text(f"""
|
||||
DELETE FROM marketplace.organization_members
|
||||
WHERE organization_id IN ({org_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(org_member_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["marketplace.organization_members"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} organization members")
|
||||
|
||||
# Delete organizations
|
||||
org_delete_query = text(f"""
|
||||
DELETE FROM marketplace.organizations
|
||||
WHERE id IN ({org_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(org_delete_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["marketplace.organizations"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} organizations")
|
||||
|
||||
# d) Delete from asset-related tables
|
||||
# Get asset IDs owned by these users
|
||||
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()]
|
||||
asset_ids_str = ",".join(asset_ids) if asset_ids else "NULL"
|
||||
|
||||
if asset_ids:
|
||||
# Delete asset costs
|
||||
asset_cost_query = text(f"""
|
||||
DELETE FROM data.asset_costs
|
||||
WHERE asset_id IN ({asset_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(asset_cost_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["data.asset_costs"] = deleted
|
||||
if deleted > 0:
|
||||
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})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(asset_event_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["data.asset_events"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} asset events")
|
||||
|
||||
# Delete assets
|
||||
asset_delete_query = text(f"""
|
||||
DELETE FROM data.assets
|
||||
WHERE id IN ({asset_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(asset_delete_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["data.assets"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} assets")
|
||||
|
||||
# e) Delete wallets
|
||||
wallet_query = text(f"""
|
||||
DELETE FROM identity.wallets
|
||||
WHERE user_id IN ({user_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(wallet_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["identity.wallets"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} wallets")
|
||||
|
||||
# f) Finally delete users
|
||||
user_delete_query = text(f"""
|
||||
DELETE FROM identity.users
|
||||
WHERE id IN ({user_ids_str})
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(user_delete_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["identity.users"] = deleted
|
||||
print(f" 🗑️ Deleted {deleted} users")
|
||||
|
||||
# g) Delete persons (if they have no other users)
|
||||
if person_ids:
|
||||
person_delete_query = text(f"""
|
||||
DELETE FROM identity.persons
|
||||
WHERE id IN ({person_ids_str})
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM identity.users
|
||||
WHERE person_id = persons.id
|
||||
)
|
||||
RETURNING *
|
||||
""")
|
||||
result = await session.execute(person_delete_query)
|
||||
deleted = result.rowcount
|
||||
deleted_counts["identity.persons"] = deleted
|
||||
if deleted > 0:
|
||||
print(f" 🗑️ Deleted {deleted} persons")
|
||||
|
||||
# Commit transaction
|
||||
await session.commit()
|
||||
|
||||
print("=" * 60)
|
||||
print("✅ Ground Zero purge completed successfully!")
|
||||
print("\n📊 Deletion Summary:")
|
||||
for table, count in deleted_counts.items():
|
||||
print(f" {table}: {count} rows")
|
||||
|
||||
total_deleted = sum(deleted_counts.values())
|
||||
print(f"\n📈 Total rows deleted: {total_deleted}")
|
||||
|
||||
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())
|
||||
Reference in New Issue
Block a user