355 lines
14 KiB
Python
355 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Service Finder Integration Data Seeding Script
|
|
Populates PostgreSQL DB with real test data for frontend integration.
|
|
|
|
Inserts:
|
|
1. User: tester_pro@profibot.hu (Password: Tester123!, Role: admin)
|
|
2. Organization: "Profibot Test Fleet" in fleet.organizations
|
|
3. Vehicles: 4 real vehicles (BMW, Audi, Mercedes, Tesla) in vehicle.assets
|
|
4. Logs: Initial logs in audit.process_logs
|
|
"""
|
|
|
|
import asyncio
|
|
import sys
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Tuple
|
|
import uuid
|
|
|
|
from sqlalchemy import select, delete, text
|
|
from sqlalchemy.dialects.postgresql import insert
|
|
|
|
from app.db.session import AsyncSessionLocal
|
|
from app.models.identity.identity import User, Person, UserRole, Wallet
|
|
from app.models.marketplace.organization import Organization
|
|
from app.models.vehicle.asset import Asset
|
|
from app.models.audit import ProcessLog
|
|
from app.core.security import get_password_hash
|
|
|
|
# Environment safety check
|
|
ENVIRONMENT = "development"
|
|
|
|
|
|
async def cleanup_existing_integration_data(db):
|
|
"""Clean up previously seeded integration data (only in non-production environments)."""
|
|
if ENVIRONMENT == "production":
|
|
print("⚠️ Production environment detected - skipping cleanup.")
|
|
return
|
|
|
|
print("🧹 Cleaning up previously seeded integration data...")
|
|
|
|
# We need to delete in the correct order due to foreign key constraints:
|
|
# There's a circular reference between User and Person:
|
|
# - User has person_id (foreign key to Person)
|
|
# - Person has user_id (foreign key to User, but optional)
|
|
# So we need to break the circular reference by setting person.user_id = NULL first
|
|
|
|
# 1. Delete integration test vehicles (by VIN pattern)
|
|
result = await db.execute(
|
|
delete(Asset).where(Asset.vin.like("INTEG%"))
|
|
)
|
|
print(f" Deleted {result.rowcount} integration test vehicles")
|
|
|
|
# 2. Delete integration test organization
|
|
result = await db.execute(
|
|
delete(Organization).where(Organization.name == "Profibot Test Fleet")
|
|
)
|
|
print(f" Deleted {result.rowcount} integration test organization")
|
|
|
|
# 3. Find integration test users and break circular references
|
|
user_stmt = select(User).where(
|
|
(User.email == "tester_pro@profibot.hu") |
|
|
(User.email == "superadmin@profibot.hu")
|
|
)
|
|
user_result = await db.execute(user_stmt)
|
|
integration_users = user_result.scalars().all()
|
|
|
|
for user in integration_users:
|
|
# Find the person associated with this user
|
|
person_stmt = select(Person).where(Person.user_id == user.id)
|
|
person_result = await db.execute(person_stmt)
|
|
person = person_result.scalar_one_or_none()
|
|
if person:
|
|
# Break the circular reference: set person.user_id = NULL
|
|
person.user_id = None
|
|
person.active_user_account = None
|
|
await db.flush()
|
|
print(f" Broke circular reference for person {person.id}")
|
|
|
|
# 4. Delete wallets for integration test users
|
|
for user in integration_users:
|
|
wallet_stmt = select(Wallet).where(Wallet.user_id == user.id)
|
|
wallet_result = await db.execute(wallet_stmt)
|
|
wallet = wallet_result.scalar_one_or_none()
|
|
if wallet:
|
|
await db.execute(delete(Wallet).where(Wallet.id == wallet.id))
|
|
print(f" Deleted wallet for user {user.email}")
|
|
|
|
# 5. Now delete the users
|
|
result = await db.execute(
|
|
delete(User).where(
|
|
(User.email == "tester_pro@profibot.hu") |
|
|
(User.email == "superadmin@profibot.hu")
|
|
)
|
|
)
|
|
print(f" Deleted {result.rowcount} integration test users")
|
|
|
|
# 6. Delete the persons (now that user references are broken)
|
|
# Find persons that were associated with the deleted users
|
|
# We need to join with users to find persons based on user email
|
|
person_stmt = select(Person).join(User, Person.user_id == User.id).where(
|
|
(User.email == "tester_pro@profibot.hu") |
|
|
(User.email == "superadmin@profibot.hu")
|
|
)
|
|
person_result = await db.execute(person_stmt)
|
|
integration_persons = person_result.scalars().all()
|
|
|
|
for person in integration_persons:
|
|
# Double-check that no user references this person
|
|
user_check_stmt = select(User).where(User.person_id == person.id)
|
|
user_check_result = await db.execute(user_check_stmt)
|
|
remaining_users = user_check_result.scalars().all()
|
|
|
|
if remaining_users:
|
|
print(f"⚠️ Person {person.id} still referenced by users: {[u.email for u in remaining_users]}")
|
|
# Try to break the reference by setting user.person_id = NULL
|
|
for user in remaining_users:
|
|
user.person_id = None
|
|
await db.flush()
|
|
|
|
await db.execute(delete(Person).where(Person.id == person.id))
|
|
print(f" Deleted person {person.first_name} {person.last_name}")
|
|
|
|
# 7. Delete integration test logs
|
|
result = await db.execute(
|
|
delete(ProcessLog).where(ProcessLog.process_name == "integration_seeding")
|
|
)
|
|
print(f" Deleted {result.rowcount} integration test logs")
|
|
|
|
|
|
async def seed_integration_data():
|
|
"""Main seeding function for integration test data."""
|
|
print("🚀 Starting integration data seeding...")
|
|
|
|
async with AsyncSessionLocal() as db:
|
|
try:
|
|
# Clean up old integration data first
|
|
await cleanup_existing_integration_data(db)
|
|
|
|
# 1. Create Person for the tester
|
|
print("👤 Creating Person for tester...")
|
|
person = Person(
|
|
first_name="Test",
|
|
last_name="User",
|
|
phone="+36123456789",
|
|
is_active=True,
|
|
lifetime_xp=1000,
|
|
penalty_points=0,
|
|
social_reputation=4.5
|
|
)
|
|
db.add(person)
|
|
await db.flush() # Get the person ID
|
|
|
|
# 2. Create User with admin role (tester)
|
|
print("👤 Creating User tester_pro@profibot.hu...")
|
|
user = User(
|
|
email="tester_pro@profibot.hu",
|
|
hashed_password=get_password_hash("Tester123!"),
|
|
role=UserRole.admin,
|
|
person_id=person.id,
|
|
is_active=True,
|
|
subscription_plan="PREMIUM",
|
|
subscription_expires_at=datetime.now() + timedelta(days=365),
|
|
is_vip=True,
|
|
preferred_language="hu",
|
|
region_code="HU",
|
|
preferred_currency="HUF",
|
|
scope_level="organization",
|
|
custom_permissions={},
|
|
created_at=datetime.now()
|
|
)
|
|
db.add(user)
|
|
await db.flush() # Get the user ID
|
|
|
|
# Update person with active user reference
|
|
person.user_id = user.id
|
|
person.active_user_account = user
|
|
|
|
# 2b. Create superadmin user (separate person)
|
|
print("👑 Creating superadmin user superadmin@profibot.hu...")
|
|
superadmin_person = Person(
|
|
first_name="Super",
|
|
last_name="Admin",
|
|
phone="+36123456788",
|
|
is_active=True,
|
|
lifetime_xp=5000,
|
|
penalty_points=0,
|
|
social_reputation=5.0
|
|
)
|
|
db.add(superadmin_person)
|
|
await db.flush()
|
|
|
|
superadmin_user = User(
|
|
email="superadmin@profibot.hu",
|
|
hashed_password=get_password_hash("Superadmin123!"),
|
|
role=UserRole.superadmin,
|
|
person_id=superadmin_person.id,
|
|
is_active=True,
|
|
subscription_plan="ENTERPRISE",
|
|
subscription_expires_at=datetime.now() + timedelta(days=365),
|
|
is_vip=True,
|
|
preferred_language="hu",
|
|
region_code="HU",
|
|
preferred_currency="HUF",
|
|
scope_level="system",
|
|
custom_permissions={},
|
|
created_at=datetime.now()
|
|
)
|
|
db.add(superadmin_user)
|
|
await db.flush()
|
|
|
|
superadmin_person.user_id = superadmin_user.id
|
|
superadmin_person.active_user_account = superadmin_user
|
|
|
|
# 3. Create Organization
|
|
print("🏢 Creating Organization 'Profibot Test Fleet'...")
|
|
organization = Organization(
|
|
name="Profibot Test Fleet",
|
|
full_name="Profibot Test Fleet Kft.",
|
|
owner_id=user.id,
|
|
legal_owner_id=person.id,
|
|
default_currency="HUF",
|
|
country_code="HU",
|
|
language="hu",
|
|
folder_slug="profibot",
|
|
first_registered_at=datetime.now(),
|
|
current_lifecycle_started_at=datetime.now(),
|
|
subscription_plan="PREMIUM",
|
|
base_asset_limit=10,
|
|
purchased_extra_slots=0,
|
|
notification_settings={"notify_owner": True, "alert_days_before": [30, 15, 7, 1]},
|
|
external_integration_config={},
|
|
org_type="fleet_owner",
|
|
status="active",
|
|
is_active=True,
|
|
is_verified=True,
|
|
created_at=datetime.now(),
|
|
is_ownership_transferable=True
|
|
)
|
|
db.add(organization)
|
|
await db.flush()
|
|
|
|
# 4. Create 4 real vehicles
|
|
print("🚗 Creating 4 real vehicles...")
|
|
|
|
vehicles_data = [
|
|
{
|
|
"vin": "INTEGBMW123456", # 13 chars
|
|
"license_plate": "ABC-123",
|
|
"name": "BMW X5",
|
|
"year_of_manufacture": 2022,
|
|
"owner_person_id": person.id,
|
|
"owner_org_id": organization.id,
|
|
"current_organization_id": organization.id,
|
|
"status": "active",
|
|
"current_mileage": 45000,
|
|
"currency": "EUR",
|
|
"individual_equipment": {},
|
|
"created_at": datetime.now()
|
|
},
|
|
{
|
|
"vin": "INTEGAUDI789012", # 14 chars
|
|
"license_plate": "DEF-456",
|
|
"name": "Audi A6",
|
|
"year_of_manufacture": 2021,
|
|
"owner_person_id": person.id,
|
|
"owner_org_id": organization.id,
|
|
"current_organization_id": organization.id,
|
|
"status": "active",
|
|
"current_mileage": 32000,
|
|
"currency": "EUR",
|
|
"individual_equipment": {},
|
|
"created_at": datetime.now()
|
|
},
|
|
{
|
|
"vin": "INTEGMB345678", # 12 chars
|
|
"license_plate": "GHI-789",
|
|
"name": "Mercedes E-Class",
|
|
"year_of_manufacture": 2023,
|
|
"owner_person_id": person.id,
|
|
"owner_org_id": organization.id,
|
|
"current_organization_id": organization.id,
|
|
"status": "active",
|
|
"current_mileage": 15000,
|
|
"currency": "EUR",
|
|
"individual_equipment": {},
|
|
"created_at": datetime.now()
|
|
},
|
|
{
|
|
"vin": "INTEGTESLA90123", # 15 chars
|
|
"license_plate": "JKL-012",
|
|
"name": "Tesla Model 3",
|
|
"year_of_manufacture": 2023,
|
|
"owner_person_id": person.id,
|
|
"owner_org_id": organization.id,
|
|
"current_organization_id": organization.id,
|
|
"status": "active",
|
|
"current_mileage": 25000,
|
|
"currency": "EUR",
|
|
"individual_equipment": {},
|
|
"created_at": datetime.now()
|
|
}
|
|
]
|
|
|
|
for i, vehicle_data in enumerate(vehicles_data, 1):
|
|
vehicle = Asset(**vehicle_data)
|
|
db.add(vehicle)
|
|
print(f" Created vehicle {i}: {vehicle_data['name']}")
|
|
|
|
# 5. Create initial process logs
|
|
print("📝 Creating initial process logs...")
|
|
|
|
# Create a single process log for the entire seeding process
|
|
log = ProcessLog(
|
|
process_name="integration_seeding",
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
items_processed=7, # 1 user + 1 org + 4 vehicles + 1 log
|
|
items_failed=0,
|
|
details={
|
|
"user_email": "tester_pro@profibot.hu",
|
|
"organization": "Profibot Test Fleet",
|
|
"vehicle_count": 4,
|
|
"makes": ["BMW", "Audi", "Mercedes-Benz", "Tesla"]
|
|
}
|
|
)
|
|
db.add(log)
|
|
|
|
# Commit all changes
|
|
await db.commit()
|
|
print("✅ Integration data seeding completed successfully!")
|
|
|
|
# Print summary
|
|
print("\n📊 Seeding Summary:")
|
|
print(f" • User: tester_pro@profibot.hu (Password: Tester123!)")
|
|
print(f" • Organization: Profibot Test Fleet")
|
|
print(f" • Vehicles: 4 real vehicles (BMW X5, Audi A6, Mercedes E-Class, Tesla Model 3)")
|
|
print(f" • Logs: 3 process logs created")
|
|
|
|
except Exception as e:
|
|
await db.rollback()
|
|
print(f"❌ Error during integration data seeding: {e}")
|
|
raise
|
|
|
|
|
|
async def main():
|
|
"""Entry point for the seeding script."""
|
|
try:
|
|
await seed_integration_data()
|
|
except Exception as e:
|
|
print(f"💥 Fatal error: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main()) |