#!/usr/bin/env python3 """Seed performance test data — creates N contacts in the test database. Usage: python scripts/seed_perf_data.py --count 200000 python scripts/seed_perf_data.py --count 5000 --tenant-id Requires DATABASE_URL environment variable or .env file. """ from __future__ import annotations import argparse import asyncio import os import sys import uuid from datetime import UTC, datetime # Add project root to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from app.config import get_settings from app.models.contact import Contact from app.models.tenant import Tenant from app.models.user import User async def seed_contacts(count: int, tenant_id_str: str | None = None) -> None: """Seed `count` contacts into the database for performance testing.""" settings = get_settings() engine = create_async_engine( settings.database_url, pool_size=settings.db_pool_size, max_overflow=settings.db_max_overflow, ) session_factory = async_sessionmaker(bind=engine, expire_on_commit=False) async with session_factory() as session: # Get or create a tenant if tenant_id_str: tenant_id = uuid.UUID(tenant_id_str) result = await session.execute(select(Tenant).where(Tenant.id == tenant_id)) tenant = result.scalar_one_or_none() if not tenant: print(f"ERROR: Tenant {tenant_id_str} not found") return else: # Get first tenant or create one result = await session.execute(select(Tenant).limit(1)) tenant = result.scalar_one_or_none() if not tenant: tenant = Tenant(name="Perf Test Tenant", slug="perf-test") session.add(tenant) await session.flush() # Get or create a user for this tenant result = await session.execute( select(User).where(User.tenant_id == tenant.id).limit(1) ) user = result.scalar_one_or_none() if not user: from app.core.auth import hash_password user = User( tenant_id=tenant.id, email="perf@test.local", name="Perf Test User", password_hash=hash_password("TestPass123!"), role="admin", is_active=True, preferences={}, ) session.add(user) await session.flush() tenant_id = tenant.id user_id = user.id # Seed contacts in batches batch_size = 1000 total_seeded = 0 first_names = ["Hans", "Maria", "Peter", "Anna", "Klaus", "Ursula", "Werner", "Brigitte", "Jürgen", "Helga"] last_names = ["Müller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Hoffmann", "Schäfer"] while total_seeded < count: batch = [] batch_count = min(batch_size, count - total_seeded) for i in range(batch_count): idx = total_seeded + i fn = first_names[idx % len(first_names)] ln = last_names[idx % len(last_names)] batch.append(Contact( tenant_id=tenant_id, first_name=f"{fn}-{idx}", last_name=f"{ln}-{idx}", email=f"{fn.lower()}.{ln.lower()}-{idx}@example.com", phone=f"+49-555-{idx:06d}", created_by=user_id, updated_by=user_id, )) session.add_all(batch) await session.commit() total_seeded += batch_count if total_seeded % 10000 == 0 or total_seeded == count: print(f"Seeded {total_seeded}/{count} contacts...") await engine.dispose() print(f"Done: {total_seeded} contacts seeded.") def main(): """Parse arguments and run the seeding script.""" parser = argparse.ArgumentParser( description="Seed performance test data into the LeoCRM database." ) parser.add_argument( "--count", type=int, default=200000, help="Number of contacts to create (default: 200000)", ) parser.add_argument( "--tenant-id", type=str, default=None, help="Tenant UUID to seed data into (default: first available tenant)", ) args = parser.parse_args() print(f"Starting seed: {args.count} contacts") asyncio.run(seed_contacts(args.count, args.tenant_id)) if __name__ == "__main__": main()