191 lines
7.4 KiB
Python
191 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
P0 HYBRID VENDOR REFACTOR: Data Migration Script
|
|
|
|
Migrates existing crowdsourced service provider organizations to the new
|
|
marketplace.service_providers table.
|
|
|
|
Safe List (valódi garázsok, amelyek NEM kerülnek migrálásra):
|
|
(15, 21, 43, 45, 47, 48, 49, 50, 66, 67, 69, 70, 71)
|
|
|
|
Folyamat:
|
|
1. Lekérdezi az összes Organization-t, ahol org_type='service_provider'
|
|
és az id NINCS a safe list-ben.
|
|
2. Minden ilyen Organization-hoz létrehoz egy ServiceProvider rekordot.
|
|
3. Frissíti a ServiceProfile-okat: service_provider_id beállítása.
|
|
4. Frissíti az AssetCost-okat: service_provider_id beállítása.
|
|
5. Jelentés a migrációról.
|
|
|
|
Használat:
|
|
docker compose exec sf_api python3 /app/scripts/migrate_providers.py
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import select, text
|
|
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
|
from app.core.config import settings
|
|
|
|
# ──────────────────────────────────────────────
|
|
# Konfiguráció
|
|
# ──────────────────────────────────────────────
|
|
|
|
# Safe list: ezek az Organization ID-k valódi garázsok, NEM migráljuk őket
|
|
SAFE_ORG_IDS = (15, 21, 43, 45, 47, 48, 49, 50, 66, 67, 69, 70, 71)
|
|
|
|
# Adatbázis URL a settings-ből
|
|
DATABASE_URL = settings.DATABASE_URL
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
|
handlers=[logging.StreamHandler(sys.stdout)],
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def migrate_providers():
|
|
"""
|
|
Main migration logic.
|
|
"""
|
|
engine = create_async_engine(DATABASE_URL, echo=False)
|
|
|
|
async with AsyncSession(engine) as db:
|
|
# ── 1. Keresd meg a migrálható Organization-öket ──
|
|
logger.info("🔍 Searching for service_provider organizations outside safe list...")
|
|
|
|
# Build safe_ids placeholder list for asyncpg
|
|
safe_list = ", ".join([str(x) for x in SAFE_ORG_IDS])
|
|
sql = f"""
|
|
SELECT id, name, display_name, address_city, address_zip,
|
|
address_street_name, address_street_type, address_house_number,
|
|
plus_code, owner_id, created_at
|
|
FROM fleet.organizations
|
|
WHERE org_type = 'service_provider'
|
|
AND id NOT IN ({safe_list})
|
|
"""
|
|
result = await db.execute(text(sql))
|
|
orgs = result.fetchall()
|
|
|
|
logger.info(f"📊 Found {len(orgs)} organizations to migrate.")
|
|
|
|
if not orgs:
|
|
logger.info("✅ No organizations to migrate. Nothing to do.")
|
|
return
|
|
|
|
# ── 2. Minden Organization-hoz hozz létre egy ServiceProvider-t ──
|
|
migrated_count = 0
|
|
skipped_count = 0
|
|
error_count = 0
|
|
|
|
for org in orgs:
|
|
org_id = org[0]
|
|
org_name = org[1]
|
|
|
|
try:
|
|
# Ellenőrizd, hogy már létezik-e ServiceProvider ehhez az org-hoz
|
|
check_stmt = select(text("id")).select_from(
|
|
text("marketplace.service_providers")
|
|
).where(text("name = :name")).params(name=org_name)
|
|
check_result = await db.execute(check_stmt)
|
|
existing = check_result.fetchone()
|
|
|
|
if existing:
|
|
logger.warning(f"⚠️ ServiceProvider already exists for org_id={org_id} (name={org_name}), skipping...")
|
|
skipped_count += 1
|
|
continue
|
|
|
|
# Összeállítjuk a címet
|
|
street_parts = [p for p in [org[5], org[6], org[7]] if p]
|
|
address = ", ".join(street_parts) if street_parts else org_name
|
|
|
|
# ServiceProvider létrehozása
|
|
insert_sp = text("""
|
|
INSERT INTO marketplace.service_providers
|
|
(name, address, city, address_zip, address_street_name,
|
|
address_street_type, address_house_number, plus_code,
|
|
status, source, validation_score, added_by_user_id, created_at)
|
|
VALUES
|
|
(:name, :address, :city, :zip, :street_name,
|
|
:street_type, :house_number, :plus_code,
|
|
'pending', 'manual', 50, :added_by, :created_at)
|
|
RETURNING id
|
|
""")
|
|
|
|
sp_result = await db.execute(
|
|
insert_sp,
|
|
{
|
|
"name": org_name,
|
|
"address": address,
|
|
"city": org[3],
|
|
"zip": org[4],
|
|
"street_name": org[5],
|
|
"street_type": org[6],
|
|
"house_number": org[7],
|
|
"plus_code": org[8],
|
|
"added_by": org[9],
|
|
"created_at": org[10] or datetime.now(timezone.utc),
|
|
}
|
|
)
|
|
sp_id = sp_result.scalar()
|
|
|
|
# ── 3. Frissítsd a ServiceProfile-okat ──
|
|
update_profile = text("""
|
|
UPDATE marketplace.service_profiles
|
|
SET service_provider_id = :sp_id
|
|
WHERE organization_id = :org_id
|
|
""")
|
|
await db.execute(update_profile, {"sp_id": sp_id, "org_id": org_id})
|
|
|
|
# ── 4. Frissítsd az AssetCost-okat ──
|
|
update_cost = text("""
|
|
UPDATE fleet_finance.asset_costs
|
|
SET service_provider_id = :sp_id
|
|
WHERE vendor_organization_id = :org_id
|
|
""")
|
|
await db.execute(update_cost, {"sp_id": sp_id, "org_id": org_id})
|
|
|
|
await db.commit()
|
|
migrated_count += 1
|
|
logger.info(f"✅ Migrated org_id={org_id} (name={org_name}) -> service_provider_id={sp_id}")
|
|
|
|
except Exception as e:
|
|
await db.rollback()
|
|
error_count += 1
|
|
logger.error(f"❌ Error migrating org_id={org_id} (name={org_name}): {e}")
|
|
|
|
# ── 5. Jelentés ──
|
|
logger.info("=" * 60)
|
|
logger.info("📋 MIGRATION REPORT")
|
|
logger.info("=" * 60)
|
|
logger.info(f" Total organizations found: {len(orgs)}")
|
|
logger.info(f" Successfully migrated: {migrated_count}")
|
|
logger.info(f" Skipped (already exists): {skipped_count}")
|
|
logger.info(f" Errors: {error_count}")
|
|
logger.info("=" * 60)
|
|
|
|
if error_count > 0:
|
|
logger.warning("⚠️ Some migrations failed. Check logs above for details.")
|
|
else:
|
|
logger.info("✅ Migration completed successfully!")
|
|
|
|
|
|
async def main():
|
|
logger.info("🚀 P0 Hybrid Vendor Refactor - Data Migration Script")
|
|
logger.info(f" Safe org IDs: {SAFE_ORG_IDS}")
|
|
logger.info(f" Database: {DATABASE_URL}")
|
|
logger.info("")
|
|
|
|
try:
|
|
await migrate_providers()
|
|
except Exception as e:
|
|
logger.error(f"💥 Fatal error: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|