Cleanup: MB 2.0 Gap Analysis előtti állapot (adatok kizárva)

This commit is contained in:
2026-02-23 09:44:02 +01:00
parent 5757754aae
commit 893f39fa15
74 changed files with 34239 additions and 2834 deletions

9
.env
View File

@@ -37,8 +37,15 @@ N8N_DB_PASSWORD=MiskociA74
# --- ROBOTOK API KULCSAI ---
RDW_APP_TOKEN=kSMUn0tvnmoM6TMSegLpFvKI8
GEMINI_API_KEY=AIzaSyAaCVNPwf8PCphu_pt6spjAa2OVu8Exug8
# GEMINI_API_KEY=AIzaSyAaCVNPwf8PCphu_pt6spjAa2OVu8Exug8
DVLA_API_KEY=qUBQDigumYaFnc9Nt15Bx4Nc4k8W0L966s9iZAmK
# --- RENDSZER ---
PYTHONPATH=/app
ENV=development
# VS Code (code-server) beállítások
VS_CODE_PASSWORD=MiskociA74
PUID=1000
PGID=1000
TZ=Europe/Budapest

23
.gitignore vendored
View File

@@ -1,4 +1,21 @@
n8n/db_data/
N8N/db_data/
*.pyc
# Python cache
__pycache__/
*.pyc
# Docker & Data (Master Book 2.0 izoláció)
ollama_data/
n8n/data/*.log
n8n/data/*.json
temp/
# Logs
logs/*.log
# IDE & AI Config
.continue/
vscode_config/
.env
# Backup files
*.bak
*.old

View File

@@ -1,5 +1,5 @@
from fastapi import APIRouter
from app.api.v1.endpoints import auth, catalog, assets, organizations, documents, services, admin
from app.api.v1.endpoints import auth, catalog, assets, organizations, documents, services, admin, expenses, evidence
api_router = APIRouter()
@@ -24,3 +24,9 @@ api_router.include_router(documents.router, prefix="/documents", tags=["Document
# --- 🛡️ SENTINEL ADMIN KONTROLL PANEL ---
# Ez a rész tette láthatóvá az Admin API-t a felületen
api_router.include_router(admin.router, prefix="/admin", tags=["Admin Control Center (Sentinel)"])
# Evidence & OCR Robot 3
api_router.include_router(evidence.router, prefix="/evidence", tags=["Evidence & OCR (Robot 3)"])
# Fleet Expenses TCO
api_router.include_router(expenses.router, prefix="/expenses", tags=["Fleet Expenses (TCO)"])

View File

@@ -1,115 +1,164 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from typing import List, Any, Dict
from sqlalchemy import select, func, text, delete
from typing import List, Any, Dict, Optional
from datetime import datetime, timedelta
from app.api import deps
from app.models.identity import User, UserRole
from app.models import SystemParameter
from app.models.system import SystemParameter
from app.models.security import PendingAction, ActionStatus
from app.models.history import AuditLog, LogSeverity
from app.schemas.admin_security import PendingActionResponse, SecurityStatusResponse
from app.services.security_service import security_service
# Feltételezve, hogy a JSON-alapú TranslationService-ed már készen van
from app.services.translation_service import TranslationService
from pydantic import BaseModel
class ConfigUpdate(BaseModel):
key: str
value: Any
scope_level: str = "global"
scope_id: Optional[str] = None
category: str = "general"
router = APIRouter()
# --- 🛡️ ADMIN JOGOSULTSÁG ELLENŐRZŐ ---
async def check_admin_access(current_user: User = Depends(deps.get_current_active_user)):
"""Szigorú hozzáférés-ellenőrzés: Csak Admin vagy Superadmin."""
if current_user.role not in [UserRole.admin, UserRole.superadmin]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin jogosultság szükséges!"
detail="Sentinel jogosultság szükséges a művelethez!"
)
return current_user
# --- 1. SENTINEL: NÉGY SZEM ELV (Approval System) ---
# --- 🛰️ 1. SENTINEL: RENDSZERÁLLAPOT ÉS MONITORING ---
@router.get("/pending-actions", response_model=List[PendingActionResponse])
@router.get("/health-monitor", tags=["Sentinel Monitoring"])
async def get_system_health(
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""
Rendszer pulzusának ellenőrzése (pgAdmin nélkül).
Látod a felhasználók eloszlását, az eszközök számát és a kritikus hibákat.
"""
stats = {}
# Adatbázis statisztikák (Dynamic counts)
user_stats = await db.execute(text("SELECT subscription_plan, count(*) FROM data.users GROUP BY subscription_plan"))
stats["user_distribution"] = {row[0]: row[1] for row in user_stats}
asset_count = await db.execute(text("SELECT count(*) FROM data.assets"))
stats["total_assets"] = asset_count.scalar()
org_count = await db.execute(text("SELECT count(*) FROM data.organizations"))
stats["total_organizations"] = org_count.scalar()
# Biztonsági státusz (Kritikus logok az elmúlt 24 órában)
day_ago = datetime.now() - timedelta(days=1)
crit_logs = await db.execute(select(func.count(AuditLog.id)).where(
AuditLog.severity.in_([LogSeverity.critical, LogSeverity.emergency]),
AuditLog.timestamp >= day_ago
))
stats["critical_alerts_24h"] = crit_logs.scalar() or 0
return stats
# --- ⚖️ 2. SENTINEL: NÉGY SZEM ELV (Approval System) ---
@router.get("/pending-actions", response_model=List[PendingActionResponse], tags=["Sentinel Security"])
async def list_pending_actions(
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""Jóváhagyásra váró kritikus kérések listázása."""
"""Jóváhagyásra váró kritikus kérések listázása (pl. törlések, rang-emelések)."""
stmt = select(PendingAction).where(PendingAction.status == ActionStatus.pending)
result = await db.execute(stmt)
return result.scalars().all()
@router.post("/approve/{action_id}")
@router.post("/approve/{action_id}", tags=["Sentinel Security"])
async def approve_action(
action_id: int,
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""Művelet véglegesítése (második admin által)."""
"""Művelet véglegesítése. Csak egy második admin hagyhatja jóvá az első kérését."""
try:
await security_service.approve_action(db, admin.id, action_id)
return {"status": "success", "message": "Művelet végrehajtva."}
return {"status": "success", "message": "Művelet sikeresen végrehajtva."}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
# --- 2. SENTINEL: BIZTONSÁGI ÖSSZEGZÉS ---
# --- ⚙️ 3. DINAMIKUS KONFIGURÁCIÓ (Hierarchical Config) ---
@router.get("/security-status", response_model=SecurityStatusResponse)
async def get_security_status(
@router.get("/parameters", tags=["Dynamic Configuration"])
async def list_all_parameters(
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""Rendszerállapot: Zárolt júzerek és kritikus események."""
day_ago = datetime.now() - timedelta(days=1)
crit_count = (await db.execute(select(func.count(AuditLog.id)).where(
AuditLog.severity.in_([LogSeverity.critical, LogSeverity.emergency]),
AuditLog.timestamp >= day_ago
))).scalar() or 0
locked_count = (await db.execute(select(func.count(User.id)).where(
User.is_active == False, User.is_deleted == False
))).scalar() or 0
return {
"total_pending": (await db.execute(select(func.count(PendingAction.id)).where(PendingAction.status == ActionStatus.pending))).scalar() or 0,
"critical_logs_last_24h": crit_count,
"emergency_locks_active": locked_count
}
# --- 3. RENDSZERBEÁLLÍTÁSOK (Dynamic Config) ---
@router.get("/settings")
async def get_settings(db: AsyncSession = Depends(deps.get_db), admin: User = Depends(check_admin_access)):
"""Minden globális paraméter (Gamification, Limitek stb.) lekérése."""
"""Minden globális és lokális paraméter (Limitek, XP szorzók stb.) lekérése."""
result = await db.execute(select(SystemParameter))
return result.scalars().all()
@router.put("/settings/{key}")
async def update_setting(key: str, value: Any, db: AsyncSession = Depends(deps.get_db), admin: User = Depends(check_admin_access)):
"""Paraméter módosítása és Audit Log generálása."""
stmt = select(SystemParameter).where(SystemParameter.key == key)
param = (await db.execute(stmt)).scalar_one_or_none()
if not param:
raise HTTPException(status_code=404, detail="Nincs ilyen beállítás.")
@router.post("/parameters", tags=["Dynamic Configuration"])
async def set_parameter(
config: ConfigUpdate, # <--- Most már egy objektumot várunk a Body-ban
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""
Paraméter beállítása. A Swaggerben most már látsz egy JSON ablakot a 'value' számára!
"""
query = text("""
INSERT INTO data.system_parameters (key, value, scope_level, scope_id, category, last_modified_by)
VALUES (:key, :val, :sl, :sid, :cat, :user)
ON CONFLICT (key, scope_level, scope_id)
DO UPDATE SET
value = EXCLUDED.value,
category = EXCLUDED.category,
last_modified_by = EXCLUDED.last_modified_by,
updated_at = now()
""")
old_val = param.value
param.value = value
await security_service.log_event(
db, admin.id, action="SETTING_CHANGE", severity=LogSeverity.warning,
old_data={key: old_val}, new_data={key: value}
)
await db.execute(query, {
"key": config.key,
"val": config.value, # Itt bármilyen komplex JSON-t átadhatsz
"sl": config.scope_level,
"sid": config.scope_id,
"cat": config.category,
"user": admin.email
})
await db.commit()
return {"status": "success", "key": key, "new_value": value}
return {"status": "success", "message": f"'{config.key}' frissítve."}
# --- 🌍 JSON FORDÍTÁSOK KEZELÉSE ---
@router.delete("/parameters/{key}", tags=["Dynamic Configuration"])
async def delete_parameter(
key: str,
scope_level: str = "global",
scope_id: Optional[str] = None,
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""Egy adott konfiguráció törlése (visszaállás az eggyel magasabb szintű alapértelmezésre)."""
stmt = delete(SystemParameter).where(
SystemParameter.key == key,
SystemParameter.scope_level == scope_level,
SystemParameter.scope_id == scope_id
)
await db.execute(stmt)
await db.commit()
return {"status": "success", "message": "Konfiguráció törölve."}
@router.post("/translations/sync")
# --- 🌍 4. UTILITY: FORDÍTÁSOK ---
@router.post("/translations/sync", tags=["System Utilities"])
async def sync_translations_to_json(
db: AsyncSession = Depends(deps.get_db),
admin: User = Depends(check_admin_access)
):
"""Szinkronizálja az adatbázisban tárolt fordításokat a JSON fájlokba."""
# A TranslationService-ben kell megírni a fájlbaíró logikát
await TranslationService.export_to_json(db)
return {"message": "JSON nyelvi fájlok frissítve."}
return {"message": "JSON nyelvi fájlok frissítve a fájlrendszerben."}

View File

@@ -0,0 +1,66 @@
# backend/app/api/v1/endpoints/evidence.py
from fastapi import APIRouter, UploadFile, File, HTTPException, status, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from app.api.deps import get_db, get_current_user
from app.schemas.evidence import OcrResponse
from app.services.image_processor import DocumentImageProcessor
from app.services.ai_ocr_service import AiOcrService
router = APIRouter()
@router.post("/scan-registration", response_model=OcrResponse)
async def scan_registration_document(
file: UploadFile = File(...),
db: AsyncSession = Depends(get_db),
current_user = Depends(get_current_user)
):
"""
Forgalmi engedély feldolgozása dinamikus, rendszer-szintű korlátok ellenőrzésével.
"""
try:
# 1. 🔍 DINAMIKUS LIMIT LEKÉRDEZÉS (Hierarchikus system_parameters táblából)
limit_query = text("""
SELECT (value->>:plan)::int
FROM data.system_parameters
WHERE key = 'VEHICLE_LIMIT'
AND scope_level = 'global'
AND is_active = true
""")
limit_res = await db.execute(limit_query, {"plan": current_user.subscription_plan})
max_allowed = limit_res.scalar() or 1 # Ha nincs paraméter, 1-re korlátozunk a biztonság kedvéért
# 2. 📊 FELHASZNÁLÓI JÁRMŰSZÁM ELLENŐRZÉSE
count_query = text("SELECT count(*) FROM data.assets WHERE operator_person_id = :p_id")
current_count = (await db.execute(count_query, {"p_id": current_user.person_id})).scalar()
if current_count >= max_allowed:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Csomaglimit túllépés. A jelenlegi '{current_user.subscription_plan}' csomagod max {max_allowed} járművet engedélyez."
)
# 3. 📸 KÉPFELDOLGOZÁS ÉS AI OCR
raw_bytes = await file.read()
clean_bytes = DocumentImageProcessor.process_for_ocr(raw_bytes)
if not clean_bytes:
raise ValueError("A kép optimalizálása az OCR számára nem sikerült.")
extracted_data = await AiOcrService.extract_registration_data(clean_bytes)
return OcrResponse(
success=True,
message=f"Sikeres adatkivonás ({current_user.subscription_plan} csomag).",
data=extracted_data
)
except HTTPException as he:
# FastAPI hibák továbbdobása (pl. 403 Forbidden)
raise he
except Exception as e:
# Általános hiba kezelése korrekt indentálással
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Robot 3 feldolgozási hiba: {str(e)}"
)

View File

@@ -2,21 +2,33 @@
from app.db.base_class import Base # noqa
# Közvetlen importok (HOZZÁADVA az audit és sales modellek)
from app.models.address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Branch # noqa
from app.models.identity import User, Person, VerificationToken, Wallet # noqa
from app.models.organization import Organization, OrganizationMember, OrganizationSalesAssignment # noqa
from app.models.address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Branch, Rating # noqa
from app.models.identity import Person, User, Wallet, VerificationToken, SocialAccount # noqa
from app.models.organization import Organization, OrganizationMember, OrganizationFinancials, OrganizationSalesAssignment # noqa
from app.models.service import ServiceProfile, ExpertiseTag, ServiceExpertise, ServiceStaging, DiscoveryParameter # noqa
from app.models.vehicle_definitions import VehicleType, VehicleModelDefinition, FeatureDefinition # noqa
from app.models.audit import SecurityAuditLog, OperationalLog, FinancialLedger # noqa <--- KRITIKUS!
from app.models.asset import ( # noqa
Asset, AssetCatalog, AssetCost, AssetEvent,
AssetFinancials, AssetTelemetry, AssetReview, ExchangeRate
)
from app.models.gamification import ( # noqa
PointRule, LevelConfig, UserStats, Badge, UserBadge, Rating, PointsLedger
)
from app.models.gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, PointsLedger # noqa
from app.models.system import SystemParameter # noqa (system.py használata)
from app.models.history import AuditLog, VehicleOwnership # noqa
from app.models.document import Document # noqa
from app.models.translation import Translation # noqa
from app.models.core_logic import ( # noqa
SubscriptionTier, OrganizationSubscription, CreditTransaction, ServiceSpecialty
)

View File

@@ -3,11 +3,12 @@ from app.core.config import settings
from typing import AsyncGenerator
engine = create_async_engine(
settings.DATABASE_URL, # A te eredeti kulcsod
echo=getattr(settings, "DEBUG", False),
settings.DATABASE_URL,
echo=False, # Termelésben ne legyen True a log-áradat miatt
future=True,
pool_size=20,
max_overflow=10
pool_size=30, # Megemelve a Researcher 15-20 szála miatt
max_overflow=20, # Extra rugalmasság csúcsidőben
pool_pre_ping=True # Megakadályozza a "Server closed connection" hibákat
)
AsyncSessionLocal = async_sessionmaker(
@@ -16,7 +17,7 @@ AsyncSessionLocal = async_sessionmaker(
expire_on_commit=False,
autoflush=False
)
# Ez a sor kell, mert a main.py és a többiek ezen a néven keresik
SessionLocal = AsyncSessionLocal
async def get_db() -> AsyncGenerator[AsyncSession, None]:

View File

@@ -6,10 +6,12 @@ from starlette.middleware.sessions import SessionMiddleware # ÚJ
from app.api.v1.api import api_router
from app.core.config import settings
# Statikus mappák létrehozása induláskor
os.makedirs("static/previews", exist_ok=True)
app = FastAPI(
title="Service Finder API",
description="Traffic Ecosystem, Asset Vault & AI Evidence Processing",
version="2.0.0",
openapi_url="/api/v1/openapi.json",
docs_url="/docs"
@@ -21,6 +23,7 @@ app.add_middleware(
secret_key=settings.SECRET_KEY
)
# --- CORS BEÁLLÍTÁSOK ---
app.add_middleware(
CORSMiddleware,
allow_origins=[
@@ -34,13 +37,30 @@ app.add_middleware(
allow_headers=["*"],
)
# Statikus fájlok kiszolgálása (képek, letöltések)
app.mount("/static", StaticFiles(directory="static"), name="static")
# A V1-es API router bekötése a /api/v1 prefix alá
app.include_router(api_router, prefix="/api/v1")
@app.get("/")
# --- ALAPVETŐ VÉGPONTOK ---
@app.get("/", tags=["System"])
async def root():
return {
"status": "online",
"message": "Service Finder Master System v2.0",
"features": ["Google Auth Enabled", "Asset Vault", "Org Onboarding"]
"features": [
"Google Auth Enabled",
"Asset Vault",
"Org Onboarding",
"AI Evidence OCR (Robot 3)",
"Fleet Expenses (TCO)"
]
}
@app.get("/health", tags=["System"])
async def health_check():
"""
Monitoring és Load Balancer egészségügyi ellenőrző végpont.
"""
return {"status": "ok", "message": "Service Finder API is running flawlessly."}

View File

@@ -3,10 +3,10 @@
from app.db.base_class import Base
# Identitás és Jogosultság
from .identity import User, Person, Wallet, UserRole, VerificationToken, SocialAccount
from .identity import Person, User, Wallet, VerificationToken, SocialAccount
# Szervezeti struktúra (HOZZÁADVA: OrganizationSalesAssignment)
from .organization import Organization, OrganizationMember, OrganizationSalesAssignment
from .organization import Organization, OrganizationMember, OrganizationFinancials, OrganizationSalesAssignment
# Járművek és Eszközök (Digital Twin)
from .asset import (
@@ -15,13 +15,13 @@ from .asset import (
)
# Szerviz és Szakértelem
from .service import ServiceProfile, ExpertiseTag, ServiceExpertise, ServiceStaging
from .service import ServiceProfile, ExpertiseTag, ServiceExpertise, ServiceStaging, DiscoveryParameter
# Földrajzi adatok és Címek
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Branch
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Branch, Rating
# Gamification és Economy
from .gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, Rating, PointsLedger
from .gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, PointsLedger
# Rendszerkonfiguráció (HASZNÁLJUK a frissített system.py-t!)
from .system import SystemParameter

View File

@@ -1,12 +1,11 @@
import uuid
# Hozzáadva: Boolean, text, func
from sqlalchemy import Column, String, Integer, ForeignKey, Text, DateTime, Float, Boolean, text, func
# PostgreSQL specifikus típusok
from sqlalchemy import Column, String, Integer, ForeignKey, Text, DateTime, Float, Boolean, text, func, Numeric, Index
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from sqlalchemy.orm import relationship
from sqlalchemy.orm import relationship, foreign
from app.db.base_class import Base
class GeoPostalCode(Base):
"""Irányítószám alapú földrajzi kereső tábla."""
__tablename__ = "geo_postal_codes"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
@@ -15,6 +14,7 @@ class GeoPostalCode(Base):
city = Column(String(100), nullable=False)
class GeoStreet(Base):
"""Utcajegyzék tábla."""
__tablename__ = "geo_streets"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
@@ -22,6 +22,7 @@ class GeoStreet(Base):
name = Column(String(200), nullable=False)
class GeoStreetType(Base):
"""Közterület jellege (utca, út, köz stb.)."""
__tablename__ = "geo_street_types"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
@@ -49,7 +50,6 @@ class Address(Base):
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Add to /app/models/address.py
class Branch(Base):
"""
Telephely entitás. A fizikai helyszín, ahol a szolgáltatás vagy flotta-kezelés zajlik.
@@ -62,7 +62,7 @@ class Branch(Base):
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
name = Column(String(100), nullable=False) # pl. "Központi iroda", "Dunakeszi Szerviz"
name = Column(String(100), nullable=False)
is_main = Column(Boolean, default=False)
# Részletes címadatok (Denormalizált a gyors kereséshez)
@@ -74,9 +74,8 @@ class Branch(Base):
stairwell = Column(String(20))
floor = Column(String(20))
door = Column(String(20))
hrsz = Column(String(50)) # Helyrajzi szám
hrsz = Column(String(50))
# Telephely specifikus adatok
opening_hours = Column(JSONB, server_default=text("'{}'::jsonb"))
branch_rating = Column(Float, default=0.0)
@@ -86,5 +85,34 @@ class Branch(Base):
organization = relationship("Organization", back_populates="branches")
address = relationship("Address")
# Kapcsolat a szerviz értékelésekkel
reviews = relationship("Rating", primaryjoin="and_(Branch.id==foreign(Rating.target_id), Rating.target_type=='branch')")
# JAVÍTOTT KAPCSOLAT: target_branch_id használata target_id helyett
reviews = relationship(
"Rating",
primaryjoin="and_(Branch.id==foreign(Rating.target_branch_id))"
)
class Rating(Base):
"""Univerzális értékelési rendszer - v1.3.1"""
__tablename__ = "ratings"
__table_args__ = (
Index('idx_rating_org', 'target_organization_id'),
Index('idx_rating_user', 'target_user_id'),
Index('idx_rating_branch', 'target_branch_id'),
{"schema": "data"}
)
# Az ID most már Integer, ahogy kérted a statisztikákhoz
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
# Explicit célpontok a típusbiztonság és gyorsaság érdekében
target_organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
target_user_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
target_branch_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.branches.id"), nullable=True)
score = Column(Numeric(3, 2), nullable=False) # 1.00 - 5.00
comment = Column(Text)
images = Column(JSONB, server_default=text("'[]'::jsonb"))
is_verified = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -1,5 +1,5 @@
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, BigInteger
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from sqlalchemy.sql import func
@@ -16,7 +16,6 @@ class AssetCatalog(Base):
)
id = Column(Integer, primary_key=True, index=True)
# Kapcsolat az MDM-hez
master_definition_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), nullable=True)
make = Column(String, index=True, nullable=False)
@@ -27,18 +26,15 @@ class AssetCatalog(Base):
year_to = Column(Integer)
vehicle_class = Column(String)
fuel_type = Column(String, index=True)
# ÚJ MEZŐ: Kapcsolat az MDM-hez
master_definition = relationship("VehicleModelDefinition", back_populates="variants")
# --- ÚJ OSZLOPOK (Ezeket add hozzá!) ---
power_kw = Column(Integer, index=True)
engine_capacity = Column(Integer, index=True)
max_weight_kg = Column(Integer)
axle_count = Column(Integer)
euro_class = Column(String(20))
body_type = Column(String(100))
# ---------------------------------------
engine_code = Column(String)
factory_data = Column(JSONB, server_default=text("'{}'::jsonb"))
@@ -56,18 +52,25 @@ class Asset(Base):
current_organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"))
# Moderációs mezők a Robot 3 (OCR) számára
is_verified = Column(Boolean, default=False)
verification_method = Column(String(20)) # 'manual', 'ocr', 'vin_api'
verification_notes = Column(Text, nullable=True) # Eltérések jegyzőkönyve
catalog_match_score = Column(Numeric(5, 2), nullable=True) # 0-100% egyezési arány
verification_method = Column(String(20))
verification_notes = Column(Text, nullable=True)
catalog_match_score = Column(Numeric(5, 2), nullable=True)
status = Column(String(20), default="active")
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# --- KAPCSOLATOK (A kettőzött current_org törölve, pontosítva) ---
catalog = relationship("AssetCatalog", back_populates="assets")
current_org = relationship("Organization")
# 1. Jelenlegi szervezet (Üzemeltető telephely)
current_org = relationship(
"Organization",
primaryjoin="Asset.current_organization_id == Organization.id",
foreign_keys="[Asset.current_organization_id]"
)
financials = relationship("AssetFinancials", back_populates="asset", uselist=False)
telemetry = relationship("AssetTelemetry", back_populates="asset", uselist=False)
assignments = relationship("AssetAssignment", back_populates="asset")
@@ -76,6 +79,43 @@ class Asset(Base):
reviews = relationship("AssetReview", back_populates="asset")
ownership_history = relationship("VehicleOwnership", back_populates="vehicle")
registration_uuid = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, index=True, nullable=False)
is_corporate = Column(Boolean, default=False, server_default=text("false"))
# Tulajdonos és Üzembentartó oszlopok
owner_person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
owner_org_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
operator_person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
operator_org_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
# 2. Tulajdonos szervezet (Kapcsolat pótolva)
owner_org = relationship(
"Organization",
primaryjoin="Asset.owner_org_id == Organization.id",
foreign_keys="[Asset.owner_org_id]"
)
# 3. Üzembentartó szervezet
operator_org = relationship(
"Organization",
primaryjoin="Asset.operator_org_id == Organization.id",
foreign_keys="[Asset.operator_org_id]"
)
# 4. Tulajdonos magánszemély
owner_person = relationship(
"Person",
primaryjoin="Asset.owner_person_id == Person.id",
foreign_keys="[Asset.owner_person_id]"
)
# 5. Üzembentartó magánszemély
operator_person = relationship(
"Person",
primaryjoin="Asset.operator_person_id == Person.id",
foreign_keys="[Asset.operator_person_id]"
)
class AssetFinancials(Base):
__tablename__ = "asset_financials"
__table_args__ = {"schema": "data"}
@@ -117,17 +157,14 @@ class AssetAssignment(Base):
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
# ÚJ: Telephelyi hozzárendelés
branch_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.branches.id"), nullable=True)
assigned_at = Column(DateTime(timezone=True), server_default=func.now())
released_at = Column(DateTime(timezone=True), nullable=True)
status = Column(String(30), default="active")
asset = relationship("Asset", back_populates="assignments")
organization = relationship("Organization")
branch = relationship("Branch") # Új kapcsolat
branch = relationship("Branch")
class AssetEvent(Base):
__tablename__ = "asset_events"
@@ -138,6 +175,7 @@ class AssetEvent(Base):
recorded_mileage = Column(Integer)
data = Column(JSONB, server_default=text("'{}'::jsonb"))
asset = relationship("Asset", back_populates="events")
registration_uuid = Column(PG_UUID(as_uuid=True), index=True, nullable=True)
class AssetCost(Base):
__tablename__ = "asset_costs"
@@ -159,6 +197,7 @@ class AssetCost(Base):
asset = relationship("Asset", back_populates="costs")
organization = relationship("Organization")
driver = relationship("User")
registration_uuid = Column(PG_UUID(as_uuid=True), index=True, nullable=True)
class ExchangeRate(Base):
__tablename__ = "exchange_rates"
@@ -169,23 +208,17 @@ class ExchangeRate(Base):
rate = Column(Numeric(18, 6), nullable=False)
class CatalogDiscovery(Base):
"""
Discovery tábla: Ide gyűjtjük a piaci 'neveket' (pl. Citroen C3).
A Robot innen indulva keresi meg az összes létező technikai variánst.
"""
__tablename__ = "catalog_discovery"
id = Column(Integer, primary_key=True, index=True)
make = Column(String(100), nullable=False, index=True)
model = Column(String(100), nullable=False, index=True)
vehicle_class = Column(String(50), index=True) # car, motorcycle, truck, stb.
source = Column(String(50)) # 'hasznaltauto', 'mobile.de'
vehicle_class = Column(String(50), index=True)
source = Column(String(50))
status = Column(String(20), server_default=text("'pending'"), index=True)
attempts = Column(Integer, default=0)
last_attempt = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# EGYESÍTETT __table_args__
__table_args__ = (
UniqueConstraint('make', 'model', 'vehicle_class', name='_make_model_class_uc'),
{"schema": "data"}

View File

@@ -81,12 +81,3 @@ class UserBadge(Base):
user: Mapped["User"] = relationship("User")
class Rating(Base):
__tablename__ = "ratings"
__table_args__ = SCHEMA_ARGS
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
target_type: Mapped[str] = mapped_column(String(20))
target_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True))
score: Mapped[int] = mapped_column(Integer)
comment: Mapped[Optional[str]] = mapped_column(String)

View File

@@ -33,6 +33,9 @@ class Organization(Base):
id = Column(Integer, primary_key=True, index=True)
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
is_anonymized = Column(Boolean, default=False, server_default=text("false"))
anonymized_at = Column(DateTime(timezone=True), nullable=True)
full_name = Column(String, nullable=False) # Hivatalos név
name = Column(String, nullable=False) # Rövid név
display_name = Column(String(50))

View File

@@ -1,6 +1,6 @@
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text, Text, Float
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text, Text, Float, Index, Numeric
from sqlalchemy.orm import relationship, backref
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from geoalchemy2 import Geometry # PostGIS támogatás
from sqlalchemy.sql import func
@@ -8,45 +8,78 @@ from app.db.base_class import Base
class ServiceProfile(Base):
"""
Szerviz szolgáltató kiterjesztett adatai.
Szerviz szolgáltató kiterjesztett adatai (v1.3.1).
Egy Organization-höz (org_type='service') kapcsolódik.
Támogatja a hierarchiát (Franchise/Telephely) és az automatizált dúsítást.
"""
__tablename__ = "service_profiles"
__table_args__ = {"schema": "data"}
__table_args__ = (
# Egyedi ujjlenyomat index a robot számára a duplikációk elkerülésére
Index('idx_service_fingerprint', 'fingerprint', unique=True),
{"schema": "data"}
)
id = Column(Integer, primary_key=True, index=True)
# --- KAPCSOLAT A CÉGES IKERHEZ (Twin) ---
organization_id = Column(Integer, ForeignKey("data.organizations.id"), unique=True)
# --- HIERARCHIA (Fa struktúra) ---
# Ez tárolja a szülő egység ID-ját (pl. hálózat központja)
parent_id = Column(Integer, ForeignKey("data.service_profiles.id"), nullable=True)
# --- ROBOT IDENTITÁS ---
# Normalize(Név + Város + Utca) hash, hogy ne legyen duplikáció
fingerprint = Column(String(255), nullable=False, index=True)
# PostGIS GPS pont (SRID 4326 = WGS84 koordináták)
location = Column(Geometry(geometry_type='POINT', srid=4326), index=True)
# Állapotkezelés: ghost, active, flagged, inactive
# Állapotkezelés: ghost (robot találta), active, flagged, inactive
status = Column(String(20), server_default=text("'ghost'"), index=True)
last_audit_at = Column(DateTime(timezone=True), server_default=func.now())
last_audit_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
# --- MAGÁNNYOMOZÓ (Deep Enrichment) ADATOK ---
# --- GOOGLE ÉS KÜLSŐ ADATOK ---
google_place_id = Column(String(100), unique=True)
rating = Column(Float)
user_ratings_total = Column(Integer)
# Bentley vs BMW logika: JSONB a gyors, márkaszintű szűréshez
# Példa: {"brands": ["Bentley", "Audi"], "specialty": ["engine", "tuning"]}
# --- MÉLYFÚRÁS (Deep Enrichment) ADATOK ---
# AI elemzés: {"tone": "barátságos", "pricing": "közép", "reliability": "magas"}
vibe_analysis = Column(JSONB, server_default=text("'{}'::jsonb"))
# Közösségi háló: {"facebook": "url", "tiktok": "url", "insta": "url"}
social_links = Column(JSONB, server_default=text("'{}'::jsonb"))
# Speciális szűrő címkék: {"brands": ["Yamaha", "Suzuki"], "specialty": ["engine", "tuning"]}
specialization_tags = Column(JSONB, server_default=text("'{}'::jsonb"))
# Trust Engine (Bot Discovery=30, User Entry=50, Admin/Partner=100)
trust_score = Column(Integer, default=30)
is_verified = Column(Boolean, default=False)
verification_log = Column(JSON, server_default=text("'{}'::jsonb"))
verification_log = Column(JSONB, server_default=text("'{}'::jsonb"))
opening_hours = Column(JSON, server_default=text("'{}'::jsonb"))
# --- ELÉRHETŐSÉG ---
opening_hours = Column(JSONB, server_default=text("'{}'::jsonb"))
contact_phone = Column(String)
contact_email = Column(String)
website = Column(String)
bio = Column(Text)
# Kapcsolatok
organization = relationship("Organization")
# --- KAPCSOLATOK ---
organization = relationship("Organization", back_populates="service_profile")
expertises = relationship("ServiceExpertise", back_populates="service")
# --- ÖNMAGÁRA HIVATKOZÓ KAPCSOLAT (Hierarchia) ---
sub_services = relationship(
"ServiceProfile",
backref=backref("parent_service", remote_side=[id]),
cascade="all, delete-orphan"
)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class ExpertiseTag(Base):
"""Szakmai szempontok taxonómiája."""
__tablename__ = "expertise_tags"
@@ -74,56 +107,57 @@ class ServiceExpertise(Base):
class ServiceStaging(Base):
"""
Átmeneti tábla a Hunter (n8n/scraping) adatoknak.
A címek itt már darabolva (IRSZ, Város, Utca, Házszám) szerepelnek
a jobb kereshetőség és validálás érdekében.
"""
__tablename__ = "service_staging"
__table_args__ = {"schema": "data"}
__table_args__ = (
Index('idx_staging_fingerprint', 'fingerprint', unique=True),
{"schema": "data"}
)
id = Column(Integer, primary_key=True, index=True)
# --- Alapadatok ---
name = Column(String, nullable=False, index=True)
# --- Strukturált cím adatok (A kérésedre bontva) ---
# --- Strukturált cím adatok ---
postal_code = Column(String(10), index=True)
city = Column(String(100), index=True)
street_name = Column(String(150))
street_type = Column(String(50)) # utca, út, tér...
street_type = Column(String(50))
house_number = Column(String(20))
stairwell = Column(String(20)) # lépcsőház
floor = Column(String(20)) # emelet
door = Column(String(20)) # ajtó
hrsz = Column(String(50)) # helyrajzi szám
stairwell = Column(String(20))
floor = Column(String(20))
door = Column(String(20))
hrsz = Column(String(50))
full_address = Column(String) # Eredeti string (audit célból)
# --- Elérhetőségek ---
full_address = Column(String)
contact_phone = Column(String, nullable=True)
email = Column(String, nullable=True)
website = Column(String, nullable=True)
# --- Forrás és Azonosítás ---
source = Column(String(50), nullable=True, index=True) # Forrás: 'OSM', 'Facebook', stb.
external_id = Column(String(100), nullable=True, index=True) # Külső ID (pl. OSM node id)
source = Column(String(50), nullable=True, index=True)
external_id = Column(String(100), nullable=True, index=True)
# Robot ujjlenyomat a Staging szintű deduplikációhoz
fingerprint = Column(String(255), nullable=False)
# --- Adatmentés ---
# Itt landol a teljes robot-zsákmány minden apró részlettel
raw_data = Column(JSONB, server_default=text("'{}'::jsonb"))
# --- Státusz és Bizalom ---
# status lehet: pending (feldolgozás alatt), enriched (nyomozó által bővített),
# duplicate (már megvan), verified (élesítésre kész)
status = Column(String(20), server_default=text("'pending'"), index=True)
trust_score = Column(Integer, default=0)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class DiscoveryParameter(Base):
"""Robot vezérlési paraméterek."""
__tablename__ = "discovery_parameters"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
city = Column(String(100), nullable=False)
keyword = Column(String(100), nullable=False) # pl. "autóvillamosság"
keyword = Column(String(100), nullable=False)
country_code = Column(String(2), default="HU")
is_active = Column(Boolean, default=True)
last_run_at = Column(DateTime(timezone=True))

View File

@@ -1,17 +1,35 @@
from sqlalchemy import Column, String, JSON, DateTime, Boolean
# backend/app/models/system.py
import enum
from sqlalchemy import Column, String, DateTime, Boolean, text, UniqueConstraint, Integer
from sqlalchemy.dialects.postgresql import JSONB # <-- JSONB-t használunk a stabilitásért
from sqlalchemy.sql import func
from app.db.base_class import Base
class SystemParameter(Base):
"""
Központi, dinamikus konfigurációs tábla.
Támogatja a többlépcsős felülbírálást (Global -> Country -> Region -> Individual).
"""
__tablename__ = "system_parameters"
__table_args__ = {"schema": "data", "extend_existing": True}
__table_args__ = (
UniqueConstraint('key', 'scope_level', 'scope_id', name='uix_param_scope'),
{"schema": "data", "extend_existing": True}
)
key = Column(String, primary_key=True, index=True, nullable=False)
# Csoportosítás az Admin felületnek (pl. 'xp', 'scout', 'routing')
# Technikai ID, hogy a 'key' ne legyen Primary Key, így engedve a hierarchiát
id = Column(Integer, primary_key=True, autoincrement=True)
key = Column(String, index=True, nullable=False) # pl. 'VEHICLE_LIMIT'
category = Column(String, index=True, server_default="general")
value = Column(JSON, nullable=False)
# A tényleges érték (JSONB-ben tárolva)
value = Column(JSONB, nullable=False) # pl. {"FREE": 1, "PREMIUM": 4}
# --- 🛡️ HIERARCHIKUS SZINTEK ---
scope_level = Column(String(30), server_default=text("'global'"), index=True)
scope_id = Column(String(50), nullable=True)
is_active = Column(Boolean, default=True)
description = Column(String)
# Kötelező audit mező: ki módosította utoljára?
last_modified_by = Column(String, nullable=True)
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())

View File

@@ -1,15 +0,0 @@
from sqlalchemy import Column, String, JSON
from app.db.base import Base
class SystemSetting(Base):
"""
Globális rendszerbeállítások tárolása.
Kulcs-Érték párok (JSON támogatással a komplex szabályokhoz).
Példa: key='FREE_VEHICLE_LIMIT', value='2'
"""
__tablename__ = "system_settings"
__table_args__ = {"schema": "data"}
key = Column(String, primary_key=True, index=True)
value = Column(JSON, nullable=False)
description = Column(String, nullable=True)

16
backend/app/models/translation.py Executable file → Normal file
View File

@@ -1,16 +1,10 @@
from sqlalchemy import Column, Integer, String, Text, Boolean, UniqueConstraint
# JAVÍTÁS: Közvetlenül a base_class-ból importálunk, hogy elkerüljük a körkörös importot
from sqlalchemy import Column, Integer, String, Text
from app.db.base_class import Base
class Translation(Base):
__tablename__ = "translations"
__table_args__ = (
UniqueConstraint("key", "lang_code", name="uq_translation_key_lang"),
{"schema": "data"}
)
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
key = Column(String(100), nullable=False, index=True)
lang_code = Column(String(5), nullable=False, index=True)
value = Column(Text, nullable=False)
is_published = Column(Boolean, default=False)
key = Column(String(255), index=True)
lang = Column(String(5), index=True) # pl: 'hu', 'en'
value = Column(Text)

View File

@@ -1,6 +1,7 @@
from sqlalchemy import Column, Integer, String, JSON, UniqueConstraint, text, Boolean, DateTime, ForeignKey, Numeric, Index
from sqlalchemy import Column, Integer, String, JSON, UniqueConstraint, text, Boolean, DateTime, ForeignKey, Numeric, Index, Text
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from sqlalchemy.dialects.postgresql import JSONB
from app.db.base_class import Base
class VehicleType(Base):
@@ -9,8 +10,8 @@ class VehicleType(Base):
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
code = Column(String(30), unique=True, index=True) # car, motorcycle, truck, bus, boat, etc.
name = Column(String(50)) # Megjelenítendő név
code = Column(String(30), unique=True, index=True)
name = Column(String(50))
icon = Column(String(50))
units = Column(JSON, server_default=text("'{\"power\": \"kW\", \"weight\": \"kg\", \"cargo\": \"m3\"}'::jsonb"))
@@ -24,24 +25,24 @@ class FeatureDefinition(Base):
id = Column(Integer, primary_key=True)
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
category = Column(String(50)) # Műszaki, Beltér, Kültér, Multimédia
category = Column(String(50))
name = Column(String(100), nullable=False)
data_type = Column(String(20), default="boolean")
vehicle_type = relationship("VehicleType", back_populates="features")
class ModelFeatureMap(Base):
"""Modell-szintű felszereltségi sablon (Alap vs Extra)"""
"""Modell-szintű felszereltségi sablon"""
__tablename__ = "model_feature_maps"
__table_args__ = {"schema": "data"}
model_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), primary_key=True)
feature_id = Column(Integer, ForeignKey("data.feature_definitions.id"), primary_key=True)
availability = Column(String(20), default="standard") # standard, optional, accessory
availability = Column(String(20), default="standard")
value = Column(String(100))
class VehicleModelDefinition(Base):
"""MDM Master rekordok - Kibővítve Deduplikációs és Évjárat mezőkkel (v1.2.5)"""
"""MDM Master rekordok - v1.3.0 Pipeline Edition (Researcher & Alchemist)"""
__tablename__ = "vehicle_model_definitions"
__table_args__ = (
UniqueConstraint('make', 'technical_code', 'vehicle_type', name='uix_make_tech_type'),
@@ -59,19 +60,24 @@ class VehicleModelDefinition(Base):
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
vehicle_class = Column(String(50))
# --- ÚJ MEZŐK AZ INTELLIGENS ÖSSZEFÉSÜLÉSHEZ ---
# Ha ez a rekord egy duplikátum, itt tároljuk, melyik az eredeti (Master) rekord
parent_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), nullable=True)
# Gyártási intervallum meghatározása
year_from = Column(Integer, nullable=True, index=True)
year_to = Column(Integer, nullable=True, index=True)
# Alternatív elnevezések kereshetőséghez (pl. ["Tracer 9", "MT-09 Tracer"])
synonyms = Column(JSON, server_default=text("'[]'::jsonb"))
# -----------------------------------------------
# --- LOGISZTIKAI ÉS TECHNIKAI FIX OSZLOPOK ---
# --- ROBOT VÉDELMI ÉS PIPELINE MEZŐK (v1.3.0) ---
is_manual = Column(Boolean, default=False, server_default=text("false"), index=True)
attempts = Column(Integer, default=0, server_default=text("0"), index=True)
last_error = Column(Text, nullable=True)
# Robot 2.1 "Researcher" porszívózott nyers adatai (A szemetesláda)
raw_search_context = Column(Text, nullable=True)
# Telemetria és forrás adatok (JSONB a hatékonyabb kereséshez)
research_metadata = Column(JSONB, server_default=text("'{}'::jsonb"), nullable=False)
# --------------------------------------------------
# --- TECHNIKAI FIX OSZLOPOK ---
engine_capacity = Column(Integer, index=True)
power_kw = Column(Integer, index=True)
max_weight_kg = Column(Integer, index=True)
@@ -82,12 +88,12 @@ class VehicleModelDefinition(Base):
cargo_length_mm = Column(Integer)
cargo_width_mm = Column(Integer)
cargo_height_mm = Column(Integer)
# ----------------------------------------------
specifications = Column(JSON, server_default=text("'{}'::jsonb"))
features_json = Column(JSON, server_default=text("'{}'::jsonb"))
status = Column(String(20), server_default="unverified") # unverified, ai_enriched, duplicate, manual_check
# Státusz mező hossza 30-ra növelve az automatikus migrációhoz
status = Column(String(30), server_default="unverified", index=True)
is_master = Column(Boolean, default=False)
source = Column(String(50))
@@ -96,9 +102,5 @@ class VehicleModelDefinition(Base):
# Kapcsolatok
v_type_rel = relationship("VehicleType", back_populates="definitions")
# Önmagára hivatkozó kapcsolat a duplikációk kezeléséhez
master_record = relationship("VehicleModelDefinition", remote_side=[id], backref="merged_variants")
# Meglévő kapcsolatok megtartása
variants = relationship("AssetCatalog", back_populates="master_definition")
variants = relationship("AssetCatalog", back_populates="master_definition", primaryjoin="VehicleModelDefinition.id == AssetCatalog.master_definition_id")

View File

@@ -0,0 +1,109 @@
from sqlalchemy import Column, Integer, String, JSON, UniqueConstraint, text, Boolean, DateTime, ForeignKey, Numeric, Index, Text
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from sqlalchemy.dialects.postgresql import JSONB # PostgreSQL specifikus JSONB a hatékony kereséshez
from app.db.base_class import Base
class VehicleType(Base):
"""Jármű főtípusok sémája (Séma-gazda)"""
__tablename__ = "vehicle_types"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
code = Column(String(30), unique=True, index=True) # car, motorcycle, truck, bus, boat, etc.
name = Column(String(50)) # Megjelenítendő név
icon = Column(String(50))
units = Column(JSON, server_default=text("'{\"power\": \"kW\", \"weight\": \"kg\", \"cargo\": \"m3\"}'::jsonb"))
features = relationship("FeatureDefinition", back_populates="vehicle_type")
definitions = relationship("VehicleModelDefinition", back_populates="v_type_rel")
class FeatureDefinition(Base):
"""Globális felszereltség szótár"""
__tablename__ = "feature_definitions"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
category = Column(String(50)) # Műszaki, Beltér, Kültér, Multimédia
name = Column(String(100), nullable=False)
data_type = Column(String(20), default="boolean")
vehicle_type = relationship("VehicleType", back_populates="features")
class ModelFeatureMap(Base):
"""Modell-szintű felszereltségi sablon (Alap vs Extra)"""
__tablename__ = "model_feature_maps"
__table_args__ = {"schema": "data"}
model_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), primary_key=True)
feature_id = Column(Integer, ForeignKey("data.feature_definitions.id"), primary_key=True)
availability = Column(String(20), default="standard") # standard, optional, accessory
value = Column(String(100))
class VehicleModelDefinition(Base):
"""MDM Master rekordok - v1.3.0 Pipeline Edition (Researcher & Alchemist)"""
__tablename__ = "vehicle_model_definitions"
__table_args__ = (
UniqueConstraint('make', 'technical_code', 'vehicle_type', name='uix_make_tech_type'),
Index('idx_vmd_lookup', 'make', 'technical_code'),
{"schema": "data"}
)
id = Column(Integer, primary_key=True)
make = Column(String(50), nullable=False, index=True)
technical_code = Column(String(50), nullable=False, index=True)
marketing_name = Column(String(100), index=True)
family_name = Column(String(100))
vehicle_type = Column(String(30), index=True)
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
vehicle_class = Column(String(50))
parent_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), nullable=True)
year_from = Column(Integer, nullable=True, index=True)
year_to = Column(Integer, nullable=True, index=True)
synonyms = Column(JSON, server_default=text("'[]'::jsonb"))
# --- ROBOT VÉDELMI ÉS PIPELINE MEZŐK (v1.3.0) ---
is_manual = Column(Boolean, default=False, server_default=text("false"), index=True)
attempts = Column(Integer, default=0, server_default=text("0"), index=True)
last_error = Column(Text, nullable=True)
# Robot 2.1 "Researcher" porszívózott nyers adatai (A szemetesláda)
raw_search_context = Column(Text, nullable=True)
# Telemetria és forrás adatok (melyik API/URL hozta az adatot)
research_metadata = Column(JSONB, server_default=text("'{}'::jsonb"), nullable=False)
# --------------------------------------------------
# --- TECHNIKAI FIX OSZLOPOK ---
engine_capacity = Column(Integer, index=True)
power_kw = Column(Integer, index=True)
max_weight_kg = Column(Integer, index=True)
axle_count = Column(Integer)
payload_capacity_kg = Column(Integer)
cargo_volume_m3 = Column(Numeric(10, 2))
cargo_length_mm = Column(Integer)
cargo_width_mm = Column(Integer)
cargo_height_mm = Column(Integer)
specifications = Column(JSON, server_default=text("'{}'::jsonb"))
features_json = Column(JSON, server_default=text("'{}'::jsonb"))
# Státusz mező hossza növelve a pipeline flagekhez
status = Column(String(30), server_default="unverified", index=True)
is_master = Column(Boolean, default=False)
source = Column(String(50)) # 'ROBOT-v1.3.0-Pipeline'
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolatok
v_type_rel = relationship("VehicleType", back_populates="definitions")
master_record = relationship("VehicleModelDefinition", remote_side=[id], backref="merged_variants")
# AssetCatalog kapcsolat
# Megjegyzés: Ellenőrizd, hogy az AssetCatalog modell be van-e importálva a Base-be!
variants = relationship("AssetCatalog", back_populates="master_definition", primaryjoin="VehicleModelDefinition.id == AssetCatalog.master_definition_id")

View File

@@ -0,0 +1,47 @@
# app/schemas/evidence.py
from pydantic import BaseModel, Field
from typing import Optional
class RegistrationDocumentExtracted(BaseModel):
"""A magyar forgalmi engedély teljes adattartalma."""
# A - Okmány adatok
license_plate: Optional[str] = Field(None, alias="A", description="Rendszám")
first_registration_date: Optional[str] = Field(None, alias="B", description="Első nyilvántartásba vétel")
doc_serial_number: Optional[str] = Field(None, description="Okmány sorszáma (jobb felső sarok)")
# C - Tulajdonos/Üzembentartó adatok
owner_last_name: Optional[str] = Field(None, alias="C.1.1", description="Családi név vagy cégnév")
owner_first_name: Optional[str] = Field(None, alias="C.1.2", description="Utónév")
owner_address: Optional[str] = Field(None, alias="C.1.3", description="Lakcím/Székhely")
owner_status: Optional[str] = Field(None, alias="C.4", description="Jogosultság státusza (a=tulaj, b=nem tulaj)")
# D - Jármű technikai adatai
make: Optional[str] = Field(None, alias="D.1", description="Gyártmány")
vehicle_type: Optional[str] = Field(None, alias="D.2", description="Típus")
commercial_description: Optional[str] = Field(None, alias="D.3", description="Kereskedelmi leírás")
vin: Optional[str] = Field(None, alias="E", description="Alvázszám (17 karakter)")
# G, F - Tömeg adatok
weight_kg: Optional[int] = Field(None, alias="G", description="Saját tömeg")
max_weight_kg: Optional[int] = Field(None, alias="F.1", description="Együttes tömeg")
# P, V - Motor és Környezetvédelem
engine_capacity: Optional[int] = Field(None, alias="P.1", description="Hengerűrtartalom (cm3)")
engine_power: Optional[float] = Field(None, alias="P.2", description="Teljesítmény (kW)")
fuel_type: Optional[str] = Field(None, alias="P.3", description="Hajtóanyag")
engine_code: Optional[str] = Field(None, alias="P.5", description="Motorkód")
env_category: Optional[str] = Field(None, alias="V.9", description="Környezetvédelmi osztály")
# R, S, H - Egyéb
color: Optional[str] = Field(None, alias="R", description="Szín")
seats: Optional[int] = Field(None, alias="S.1", description="Ülések száma")
expiry_date: Optional[str] = Field(None, alias="H", description="Műszaki érvényesség")
transmission_type: Optional[str] = Field(None, description="Sebességváltó fajtája")
class Config:
populate_by_name = True
class OcrResponse(BaseModel):
success: bool
message: str
data: Optional[RegistrationDocumentExtracted] = None

View File

@@ -1,20 +1,45 @@
from pydantic import BaseModel
from typing import Optional
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
class ServiceCreateInternal(BaseModel):
name: str
postal_code: str
name: str = Field(..., description="A szolgáltató neve")
# --- HIERARCHIA ---
# Ha a robot felismeri, hogy egy lánc része, itt tároljuk a szülő ID-t
parent_id: Optional[int] = Field(None, description="Szülő egység ID-ja (pl. Franchise központ)")
# --- CÍM ADATOK ---
postal_code: Optional[str] = None
city: str
street_name: str
street_type: str
house_number: str
street_name: Optional[str] = None
street_type: Optional[str] = "utca"
house_number: Optional[str] = None
stairwell: Optional[str] = None
floor: Optional[str] = None
door: Optional[str] = None
hrsz: Optional[str] = None
full_address: Optional[str] = Field(None, description="Eredeti, nyers cím szövege")
# --- ELÉRHETŐSÉG ---
contact_phone: Optional[str] = None
email: Optional[str] = None
website: Optional[str] = None
source: str
# --- SOCIAL & AI ---
# A Deep Dive fázishoz előkészítve
social_links: Optional[Dict[str, str]] = Field(default_factory=dict)
vibe_analysis: Optional[Dict[str, Any]] = Field(default_factory=dict)
# --- IDENTITÁS ÉS FORRÁS ---
source: str # 'google', 'osm', 'manual', 'fb_scraper'
external_id: Optional[str] = None
# Ez a robot "horgonya" a duplikációk ellen
fingerprint: str = Field(..., description="Egyedi ujjlenyomat: Hash(Name+City+Street)")
trust_score: int = Field(30, ge=0, le=100)
raw_data: Optional[Dict[str, Any]] = {}
class Config:
from_attributes = True

View File

@@ -0,0 +1,64 @@
# app/services/ai_ocr_service.py
import json
import httpx
import base64
from app.schemas.evidence import RegistrationDocumentExtracted
class AiOcrService:
OLLAMA_URL = "http://service_finder_ollama:11434/api/generate"
MODEL_NAME = "llama3.2-vision"
@classmethod
async def extract_registration_data(cls, clean_image_bytes: bytes) -> RegistrationDocumentExtracted:
base64_image = base64.b64encode(clean_image_bytes).decode('utf-8')
prompt = """
Te egy magyar hatósági okmány-szakértő AI vagy. A feladatod a mellékelt magyar forgalmi engedély (kép) összes adatának kinyerése.
Keresd meg és olvasd le az adatokat az alábbi hatósági kódok alapján:
- A: Rendszám (kötőjellel, pl: ABC-123 vagy AA-BB-123)
- B: Első nyilvántartásba vétel dátuma (YYYY.MM.DD)
- C.1.1: Családi név vagy cégnév
- C.1.2: Utónév
- C.1.3: Teljes lakcím (Irsz, Város, Utca, Házszám)
- C.4: Jogosultság (a = tulajdonos, b = üzembentartó)
- D.1: Gyártmány (pl. TOYOTA, VOLKSWAGEN)
- D.2: Jármű típusa
- D.3: Kereskedelmi leírás (pl. COROLLA, GOLF)
- E: Alvázszám (pontosan 17 karakter)
- G: Saját tömeg (kg)
- F.1: Együttes tömeg (kg)
- P.1: Hengerűrtartalom (cm3)
- P.2: Teljesítmény (kW)
- P.3: Hajtóanyag (pl. Benzin, Gázolaj, Elektromos)
- P.5: Motorkód
- V.9: Környezetvédelmi osztály kódja
- R: Szín
- S.1: Ülések száma
- H: Műszaki érvényesség vége (YYYY.MM.DD)
- Sebességváltó: Keresd a 0, 1, 2, 3 kódokat (0=mechanikus, 2=automata).
VÁLASZ FORMÁTUMA: Kizárólag érvényes JSON. Ha egy adat nem olvasható, az értéke null legyen.
"""
payload = {
"model": cls.MODEL_NAME,
"prompt": prompt,
"images": [base64_image],
"stream": False,
"format": "json"
}
async with httpx.AsyncClient(timeout=90.0) as client:
try:
response = await client.post(cls.OLLAMA_URL, json=payload)
response.raise_for_status()
ai_response_text = response.json().get("response", "{}")
data_dict = json.loads(ai_response_text)
return RegistrationDocumentExtracted(**data_dict)
except Exception as e:
print(f"Robot 3 AI Hiba: {e}")
raise ValueError(f"AI hiba az adatkivonás során: {str(e)}")

View File

@@ -3,24 +3,20 @@ import json
import logging
import asyncio
import re
from typing import Dict, Any, Optional
from google import genai
from google.genai import types
import base64
import httpx
from typing import Dict, Any, Optional, List
from sqlalchemy import select
from app.db.session import SessionLocal
from app.models import SystemParameter
from app.models.system import SystemParameter
logger = logging.getLogger("AI-Service")
class AIService:
"""
AI Service v1.2.5 - Final Integrated Edition
- Robot 2: Technikai dúsítás (Search + Regex JSON parsing)
- Robot 3: OCR (Controlled JSON generation)
"""
api_key = os.getenv("GEMINI_API_KEY")
client = genai.Client(api_key=api_key) if api_key else None
PRIMARY_MODEL = "gemini-2.0-flash"
OLLAMA_BASE_URL = "http://ollama:11434/api/generate"
TEXT_MODEL = "qwen2.5-coder:32b"
VISION_MODEL = "llava:7b"
DVLA_API_KEY = os.getenv("DVLA_API_KEY")
@classmethod
async def get_config_delay(cls) -> float:
@@ -29,83 +25,71 @@ class AIService:
stmt = select(SystemParameter).where(SystemParameter.key == "AI_REQUEST_DELAY")
res = await db.execute(stmt)
param = res.scalar_one_or_none()
return float(param.value) if param else 1.0
except Exception: return 1.0
return float(param.value) if param else 0.1
except Exception:
return 0.1
@classmethod
async def get_clean_vehicle_data(cls, make: str, raw_model: str, v_type: str) -> Optional[Dict[str, Any]]:
"""Robot 2: Adatbányászat Google Search segítségével."""
if not cls.client: return None
async def get_gold_data_from_research(cls, make: str, model: str, raw_context: str) -> Optional[Dict[str, Any]]:
await asyncio.sleep(await cls.get_config_delay())
search_tool = types.Tool(google_search=types.GoogleSearch())
prompt = f"""
KERESS RÁ az interneten: {make} {raw_model} ({v_type}) pontos gyári modellkódja és technikai adatai.
Adj választ szigorúan csak egy JSON blokkban:
FELADAT: A mellékelt kutatási adatokból állíts össze egy hiteles technikai adatlapot.
JÁRMŰ: {make} {model}
KUTATÁSI ADATOK (Szemetesláda tartalom):
{raw_context}
SZIGORÚ SZABÁLYOK:
1. Csak a megerősített adatokat töltsd ki.
2. Ha lóerőt (hp/bhp) találsz, váltsd át kW-ra (hp * 0.745).
3. A 'marketing_name' maradjon 50 karakter alatt.
VÁLASZ FORMÁTUM (Tiszta JSON):
{{
"marketing_name": "tiszta név",
"synonyms": ["név1", "név2"],
"technical_code": "gyári kód",
"year_from": int,
"year_to": int_vagy_null,
"marketing_name": "string",
"technical_code": "string",
"ccm": int,
"kw": int,
"maintenance": {{ "oil_type": "string", "oil_qty": float, "spark_plug": "string", "coolant": "string" }}
"maintenance": {{
"oil_type": "string",
"oil_qty_liters": float,
"spark_plug": "string",
"final_drive": "string"
}},
"tires": {{
"front": "string",
"rear": "string"
}},
"is_duplicate_potential": bool
}}
FONTOS: A 'technical_code' NEM lehet üres. Ha nem találod, adj 'N/A' értéket!
"""
return await cls._execute_ai_call(prompt, make, model)
# Search tool használata esetén a response_mime_type tilos!
config = types.GenerateContentConfig(
system_instruction="Profi járműtechnikai adatbányász vagy. Csak tiszta JSON-t válaszolsz markdown kódblokk nélkül.",
tools=[search_tool],
temperature=0.1
)
@classmethod
async def _execute_ai_call(cls, prompt: str, make: str, model: str) -> Optional[Dict[str, Any]]:
payload = {
"model": cls.TEXT_MODEL,
"prompt": prompt,
"stream": False,
"format": "json",
"options": {"temperature": 0.1}
}
try:
response = cls.client.models.generate_content(model=cls.PRIMARY_MODEL, contents=prompt, config=config)
text = response.text
# Tisztítás: ha az AI mégis tenne bele markdown jeleket
clean_json = re.sub(r'```json\s*|```', '', text).strip()
res_json = json.loads(clean_json)
if isinstance(res_json, list) and len(res_json) > 0: res_json = res_json[0]
return res_json if isinstance(res_json, dict) else None
async with httpx.AsyncClient(timeout=120.0) as client:
response = await client.post(cls.OLLAMA_BASE_URL, json=payload)
response.raise_for_status()
res_json = response.json()
return json.loads(res_json.get("response", "{}"))
except Exception as e:
logger.error(f"❌ AI hiba ({make} {raw_model}): {e}")
logger.error(f"❌ AI hiba ({make} {model}): {e}")
return None
@classmethod
async def analyze_document_image(cls, image_data: bytes, doc_type: str) -> Optional[Dict[str, Any]]:
"""Robot 3: OCR funkció - Forgalmi, Személyi, Számla, Odometer."""
if not cls.client: return None
async def get_clean_vehicle_data(cls, make: str, raw_model: str, v_type: str, sources: Dict[str, Any]) -> Optional[Dict[str, Any]]:
await asyncio.sleep(await cls.get_config_delay())
prompts = {
"identity": "Személyes okmány adatok (név, szám, lejárat).",
"vehicle_reg": "Forgalmi adatok (rendszám, alvázszám, kW, ccm).",
"invoice": "Számla adatok (partner, végösszeg, dátum).",
"odometer": "Csak a kilométeróra állása számként."
}
# Itt maradhat a response_mime_type, mert nem használunk Search-öt
config = types.GenerateContentConfig(
system_instruction="Profi OCR dokumentum-elemző vagy. Csak tiszta JSON-t válaszolsz.",
response_mime_type="application/json"
)
try:
response = cls.client.models.generate_content(
model=cls.PRIMARY_MODEL,
contents=[
f"Elemezd ezt a képet ({doc_type}): {prompts.get(doc_type, 'OCR')}",
types.Part.from_bytes(data=image_data, mime_type="image/jpeg")
],
config=config
)
res_json = json.loads(response.text)
if isinstance(res_json, list) and len(res_json) > 0: res_json = res_json[0]
return res_json if isinstance(res_json, dict) else None
except Exception as e:
logger.error(f"❌ OCR hiba: {e}")
return None
prompt = f"""
FELADAT: Normalizáld a jármű adatait.
GYÁRTÓ: {make} | MODELL: {raw_model}
ADATOK: {json.dumps(sources)}
(JSON válasz marketing_name, synonyms, technical_code, ccm, kw, year_from, year_to)
"""
return await cls._execute_ai_call(prompt, make, raw_model)

View File

@@ -0,0 +1,141 @@
import os
import json
import logging
import asyncio
import re
import base64
import httpx
from typing import Dict, Any, Optional, List
from sqlalchemy import select
from app.db.session import SessionLocal
from app.models import SystemParameter
logger = logging.getLogger("AI-Service")
class AIService:
"""
AI Service v1.3.5 - Private High-Performance Edition
- Engine: Local Ollama (GPU Accelerated)
- Features: DVLA Integration, 50-char Normalization, Private OCR
"""
# A Docker belső hálózatán a szerviznév 'ollama'
OLLAMA_BASE_URL = "http://ollama:11434/api/generate"
TEXT_MODEL = "vehicle-pro"
VISION_MODEL = "llava:7b"
DVLA_API_KEY = os.getenv("DVLA_API_KEY")
@classmethod
async def get_config_delay(cls) -> float:
"""Késleltetés lekérése az adatbázisból."""
try:
async with SessionLocal() as db:
stmt = select(SystemParameter).where(SystemParameter.key == "AI_REQUEST_DELAY")
res = await db.execute(stmt)
param = res.scalar_one_or_none()
return float(param.value) if param else 0.1
except Exception:
return 0.1
@classmethod
async def get_clean_vehicle_data(cls, make: str, raw_model: str, v_type: str, sources: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Robot 2: Adat-összefésülés és normalizálás."""
# Várjunk egy kicsit a GPU kímélése érdekében
await asyncio.sleep(await cls.get_config_delay())
prompt = f"""
FELADAT: Normalizáld a jármű adatait több forrás alapján.
GYÁRTÓ: {make}
NYERS MODELLNÉV: {raw_model}
FORRÁSOK NYERS ADATAI: {json.dumps(sources, ensure_ascii=False)}
SZIGORÚ SZABÁLYOK:
1. 'marketing_name': MAXIMUM 50 KARAKTER!
2. 'synonyms': Gyűjtsd ide az összes többi névváltozatot.
3. 'technical_code': Keresd meg a gyári kódokat.
VÁLASZ FORMÁTUM (Csak tiszta JSON):
{{
"marketing_name": "string (max 50)",
"synonyms": ["string"],
"technical_code": "string",
"ccm": int,
"kw": int,
"euro_class": int,
"year_from": int,
"year_to": int vagy null,
"maintenance": {{
"oil_type": "string",
"oil_qty": float,
"spark_plug": "string"
}},
"is_duplicate_potential": bool
}}
"""
payload = {
"model": cls.TEXT_MODEL,
"prompt": prompt,
"stream": False,
"format": "json",
"options": {"temperature": 0.1}
}
try:
async with httpx.AsyncClient(timeout=90.0) as client:
logger.info(f"📡 AI kérés küldése: {make} {raw_model}...")
response = await client.post(cls.OLLAMA_BASE_URL, json=payload)
response.raise_for_status()
res_json = response.json()
clean_data = json.loads(res_json.get("response", "{}"))
if clean_data.get("marketing_name"):
clean_data["marketing_name"] = clean_data["marketing_name"][:50].strip()
return clean_data
except Exception as e:
logger.error(f"❌ AI hiba ({make} {raw_model}): {e}")
return None
@classmethod
async def get_dvla_data(cls, vrm: str) -> Optional[Dict[str, Any]]:
"""Brit rendszám alapú adatok lekérése."""
if not cls.DVLA_API_KEY: return None
url = "https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles"
headers = {"x-api-key": cls.DVLA_API_KEY, "Content-Type": "application/json"}
try:
async with httpx.AsyncClient() as client:
resp = await client.post(url, json={"registrationNumber": vrm}, headers=headers)
return resp.json() if resp.status_code == 200 else None
except Exception as e:
logger.error(f"❌ DVLA API hiba: {e}")
return None
@classmethod
async def analyze_document_image(cls, image_data: bytes, doc_type: str) -> Optional[Dict[str, Any]]:
"""Robot 3: Helyi OCR és dokumentum elemzés (Llava:7b)."""
await asyncio.sleep(await cls.get_config_delay())
prompts = {
"identity": "Extract ID card data (name, id_number, expiry) as JSON.",
"vehicle_reg": "Extract vehicle registration (plate, VIN, power_kw, engine_ccm) as JSON.",
"invoice": "Extract invoice details (vendor, total_amount, date) as JSON.",
"odometer": "Identify the number on the odometer and return as JSON: {'value': int}."
}
img_b64 = base64.b64encode(image_data).decode('utf-8')
payload = {
"model": cls.VISION_MODEL,
"prompt": prompts.get(doc_type, "Perform OCR and return JSON"),
"images": [img_b64],
"stream": False,
"format": "json"
}
try:
async with httpx.AsyncClient(timeout=120.0) as client:
response = await client.post(cls.OLLAMA_BASE_URL, json=payload)
res_data = response.json()
clean_json = res_data.get("response", "{}")
match = re.search(r'\{.*\}', clean_json, re.DOTALL)
return json.loads(match.group()) if match else json.loads(clean_json)
except Exception as e:
logger.error(f"❌ Helyi OCR hiba: {e}")
return None

View File

@@ -0,0 +1,111 @@
import os
import json
import logging
import asyncio
import re
from typing import Dict, Any, Optional
from google import genai
from google.genai import types
from sqlalchemy import select
from app.db.session import SessionLocal
from app.models import SystemParameter
logger = logging.getLogger("AI-Service")
class AIService:
"""
AI Service v1.2.5 - Final Integrated Edition
- Robot 2: Technikai dúsítás (Search + Regex JSON parsing)
- Robot 3: OCR (Controlled JSON generation)
"""
api_key = os.getenv("GEMINI_API_KEY")
client = genai.Client(api_key=api_key) if api_key else None
PRIMARY_MODEL = "gemini-2.0-flash"
@classmethod
async def get_config_delay(cls) -> float:
try:
async with SessionLocal() as db:
stmt = select(SystemParameter).where(SystemParameter.key == "AI_REQUEST_DELAY")
res = await db.execute(stmt)
param = res.scalar_one_or_none()
return float(param.value) if param else 1.0
except Exception: return 1.0
@classmethod
async def get_clean_vehicle_data(cls, make: str, raw_model: str, v_type: str) -> Optional[Dict[str, Any]]:
"""Robot 2: Adatbányászat Google Search segítségével."""
if not cls.client: return None
await asyncio.sleep(await cls.get_config_delay())
search_tool = types.Tool(google_search=types.GoogleSearch())
prompt = f"""
KERESS RÁ az interneten: {make} {raw_model} ({v_type}) pontos gyári modellkódja és technikai adatai.
Adj választ szigorúan csak egy JSON blokkban:
{{
"marketing_name": "tiszta név",
"synonyms": ["név1", "név2"],
"technical_code": "gyári kód",
"year_from": int,
"year_to": int_vagy_null,
"ccm": int,
"kw": int,
"maintenance": {{ "oil_type": "string", "oil_qty": float, "spark_plug": "string", "coolant": "string" }}
}}
FONTOS: A 'technical_code' NEM lehet üres. Ha nem találod, adj 'N/A' értéket!
"""
# Search tool használata esetén a response_mime_type tilos!
config = types.GenerateContentConfig(
system_instruction="Profi járműtechnikai adatbányász vagy. Csak tiszta JSON-t válaszolsz markdown kódblokk nélkül.",
tools=[search_tool],
temperature=0.1
)
try:
response = cls.client.models.generate_content(model=cls.PRIMARY_MODEL, contents=prompt, config=config)
text = response.text
# Tisztítás: ha az AI mégis tenne bele markdown jeleket
clean_json = re.sub(r'```json\s*|```', '', text).strip()
res_json = json.loads(clean_json)
if isinstance(res_json, list) and len(res_json) > 0: res_json = res_json[0]
return res_json if isinstance(res_json, dict) else None
except Exception as e:
logger.error(f"❌ AI hiba ({make} {raw_model}): {e}")
return None
@classmethod
async def analyze_document_image(cls, image_data: bytes, doc_type: str) -> Optional[Dict[str, Any]]:
"""Robot 3: OCR funkció - Forgalmi, Személyi, Számla, Odometer."""
if not cls.client: return None
await asyncio.sleep(await cls.get_config_delay())
prompts = {
"identity": "Személyes okmány adatok (név, szám, lejárat).",
"vehicle_reg": "Forgalmi adatok (rendszám, alvázszám, kW, ccm).",
"invoice": "Számla adatok (partner, végösszeg, dátum).",
"odometer": "Csak a kilométeróra állása számként."
}
# Itt maradhat a response_mime_type, mert nem használunk Search-öt
config = types.GenerateContentConfig(
system_instruction="Profi OCR dokumentum-elemző vagy. Csak tiszta JSON-t válaszolsz.",
response_mime_type="application/json"
)
try:
response = cls.client.models.generate_content(
model=cls.PRIMARY_MODEL,
contents=[
f"Elemezd ezt a képet ({doc_type}): {prompts.get(doc_type, 'OCR')}",
types.Part.from_bytes(data=image_data, mime_type="image/jpeg")
],
config=config
)
res_json = json.loads(response.text)
if isinstance(res_json, list) and len(res_json) > 0: res_json = res_json[0]
return res_json if isinstance(res_json, dict) else None
except Exception as e:
logger.error(f"❌ OCR hiba: {e}")
return None

View File

@@ -0,0 +1,27 @@
import httpx
import logging
logger = logging.getLogger("DVLA-Service")
class DVLAService:
API_URL = "https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles"
API_KEY = "IDE_MÁSOLD_BE_AZ_API_KULCSOT"
@classmethod
async def get_vehicle_details(cls, vrm: str):
"""VRM az angol rendszám (pl. AB12 CDE)"""
headers = {
"x-api-key": cls.API_KEY,
"Content-Type": "application/json"
}
payload = {"registrationNumber": vrm}
async with httpx.AsyncClient() as client:
try:
response = await client.post(cls.API_URL, json=payload, headers=headers)
if response.status_code == 200:
return response.json()
return None
except Exception as e:
logger.error(f"❌ DVLA hiba: {e}")
return None

View File

@@ -0,0 +1,62 @@
import cv2
import numpy as np
from typing import Optional
class DocumentImageProcessor:
"""
Saját fejlesztésű képtisztító pipeline OCR-hez.
A nyers (mobillal fotózott) képekből kontrasztos, fekete-fehér, zajmentes változatot készít,
amelyet az AI már közel 100%-os pontossággal tud olvasni.
"""
@staticmethod
def process_for_ocr(image_bytes: bytes) -> Optional[bytes]:
try:
# 1. Kép betöltése a memóriából (FastAPI UploadFile bytes-ból)
# A képet nem mentjük a lemezre, villámgyorsan a RAM-ban dolgozzuk fel.
nparr = np.frombuffer(image_bytes, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if img is None:
raise ValueError("A képet nem sikerült dekódolni.")
# 2. Szürkeárnyalatossá alakítás (A színek csak zavarják a szövegfelismerést)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 3. Kép átméretezése (Felskálázás)
# Az AI és az OCR motorok a minimum 300 DPI körüli képeket szeretik.
height, width = gray.shape
if width < 1000 or height < 1000:
gray = cv2.resize(gray, None, fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
# 4. Kontraszt növelése (CLAHE - Contrast Limited Adaptive Histogram Equalization)
# Ez eltünteti a vaku okozta becsillanásokat és kiemeli a halvány betűket.
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
contrast = clahe.apply(gray)
# 5. Enyhe homályosítás (Denoising / Noise Reduction)
# Eltünteti a papír textúráját (pl. a forgalmi engedély vízjelét vagy a blokk gyűrődéseit).
blur = cv2.GaussianBlur(contrast, (5, 5), 0)
# 6. Adaptív Küszöbérték (Binarization)
# Minden pixel környezetét külön vizsgálja. Ez küszöböli ki azt, amikor a fotó egyik
# sarka sötét (pl. árnyékot vet a telefon), a másik meg világos.
thresh = cv2.adaptiveThreshold(
blur,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
11, # Blokk méret (páratlan szám)
2 # Konstans levonás
)
# 7. Visszakódolás bájt formátumba (PNG), hogy átadhassuk az AI-nak
success, encoded_image = cv2.imencode('.png', thresh)
if not success:
raise ValueError("Nem sikerült a feldolgozott képet PNG-be kódolni.")
return encoded_image.tobytes()
except Exception as e:
print(f"Hiba a képfeldolgozás során: {str(e)}")
return None

View File

@@ -0,0 +1,29 @@
import uuid
from minio import Minio
from app.core.config import settings
class StorageService:
client = Minio(
settings.MINIO_ENDPOINT,
access_key=settings.MINIO_ROOT_USER,
secret_key=settings.MINIO_ROOT_PASSWORD,
secure=settings.MINIO_SECURE
)
BUCKET_NAME = "vehicle-documents"
@classmethod
async def upload_document(cls, file_bytes: bytes, file_name: str, folder: str) -> str:
if not cls.client.bucket_exists(cls.BUCKET_NAME):
cls.client.make_bucket(cls.BUCKET_NAME)
# Egyedi fájlnév generálása az ütközések elkerülésére
unique_name = f"{folder}/{uuid.uuid4()}_{file_name}"
from io import BytesIO
cls.client.put_object(
cls.BUCKET_NAME,
unique_name,
BytesIO(file_bytes),
len(file_bytes)
)
return f"{cls.BUCKET_NAME}/{unique_name}"

View File

@@ -0,0 +1,16 @@
from sqlalchemy import Column, Integer, String, Text, Boolean, UniqueConstraint
# JAVÍTÁS: Közvetlenül a base_class-ból importálunk, hogy elkerüljük a körkörös importot
from app.db.base_class import Base
class Translation(Base):
__tablename__ = "translations"
__table_args__ = (
UniqueConstraint("key", "lang_code", name="uq_translation_key_lang"),
{"schema": "data"}
)
id = Column(Integer, primary_key=True, index=True)
key = Column(String(100), nullable=False, index=True)
lang_code = Column(String(5), nullable=False, index=True)
value = Column(Text, nullable=False)
is_published = Column(Boolean, default=False)

View File

@@ -0,0 +1,111 @@
import asyncio
import logging
from sqlalchemy import select, update, func, and_, case # JAVÍTVA: and_ és case importálva
from app.db.session import SessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition
from app.services.ai_service import AIService
# Logolás finomhangolása
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s')
logger = logging.getLogger("Robot-Alchemist-v2.2")
class AlchemistBot:
def __init__(self):
self.batch_size = 5 # GPU VRAM kímélése (Ollama párhuzamosítás mellett)
self.delay_between_records = 12 # Quadro P4000 hűtési idő/késleltetés
async def synthesize_vehicle(self, vehicle_id: int):
"""AI dúsítás végrehajtása a begyűjtött kontextusból."""
async with SessionLocal() as db:
res = await db.execute(select(VehicleModelDefinition).where(VehicleModelDefinition.id == vehicle_id))
v = res.scalar_one_or_none()
if not v or not v.raw_search_context:
logger.warning(f"⚠️ Nincs kontextus az ID:{vehicle_id} rekordhoz, átugrás.")
return
make, model = v.make, v.marketing_name
logger.info(f"🧪 Arany dúsítás indul (AI Synthesis): {make} {model}")
# Státusz zárolása a feldolgozás idejére
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == vehicle_id)
.values(status='ai_synthesis_in_progress')
)
await db.commit()
# AI hívás: Gold-Data kinyerése a "szemetesládából"
gold_data = await AIService.get_gold_data_from_research(make, model, v.raw_search_context)
async with SessionLocal() as db:
if gold_data:
# Értékek kinyerése és normalizálása
ccm = gold_data.get("ccm")
kw = gold_data.get("kw")
m_name = gold_data.get("marketing_name", model)[:50]
t_code = gold_data.get("technical_code")
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == vehicle_id)
.values(
marketing_name=m_name,
technical_code=t_code or v.technical_code,
engine_capacity=ccm,
power_kw=kw,
features_json=gold_data, # A teljes technikai JSON (olaj, gumi, stb.)
status='gold_enriched',
updated_at=func.now()
)
)
logger.info(f"✨ GOLD ENRICHED: {make} {m_name} ({ccm} ccm, {kw} kW)")
else:
# Hiba esetén visszatesszük a sorba, növelve a kísérletek számát
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == vehicle_id)
.values(
status='awaiting_ai_synthesis',
attempts=v.attempts + 1,
last_error="AI extraction failed or returned empty"
)
)
logger.warning(f"⚠️ Sikertelen dúsítás: {make} {model}")
await db.commit()
async def run(self):
logger.info("🚀 Robot 2.2 (Alchemist) ONLINE - Prioritásos feldolgozás")
while True:
async with SessionLocal() as db:
# --- PRIORITÁSI LOGIKA (Megegyezik a Researcher botéval) ---
priorities = case(
(and_(VehicleModelDefinition.vehicle_type == 'car',
VehicleModelDefinition.make.in_(['SUZUKI', 'TOYOTA', 'SKODA', 'VOLKSWAGEN', 'OPEL'])), 1),
(VehicleModelDefinition.vehicle_type == 'car', 2),
(and_(VehicleModelDefinition.vehicle_type == 'motorcycle',
VehicleModelDefinition.make.in_(['HONDA', 'YAMAHA', 'SUZUKI', 'KAWASAKI'])), 3),
else_=4
)
# Lekérdezés prioritás szerint, majd a legrégebben frissített rekordok szerint
stmt = select(VehicleModelDefinition.id).where(
VehicleModelDefinition.status == 'awaiting_ai_synthesis'
).order_by(priorities, VehicleModelDefinition.updated_at.asc()).limit(self.batch_size)
res = await db.execute(stmt)
ids = [r[0] for r in res.fetchall()]
if not ids:
# Ha üres a tartály, pihenünk és várunk a porszívóra
await asyncio.sleep(20)
continue
for vid in ids:
await self.synthesize_vehicle(vid)
# Quadro P4000 hűtés és Ollama API tehermentesítés
await asyncio.sleep(self.delay_between_records)
if __name__ == "__main__":
asyncio.run(AlchemistBot().run())

View File

@@ -1,208 +1,136 @@
import asyncio
import httpx
import logging
import json
import os
import datetime
from sqlalchemy import text
import sys
from sqlalchemy import text, select
from app.db.session import SessionLocal
from app.models.asset import AssetCatalog
from app.models.vehicle_definitions import VehicleModelDefinition
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("Robot-v1.0.13-Global-Hunter")
# Logolás beállítása
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
logger = logging.getLogger("Hunter-v2.4-Paginator")
class CatalogMaster:
"""
Master Hunter Robot v1.0.13 - Global Hunter Edition
- Holland (RDW), Brit (DVLA) és Amerikai (NHTSA) adatbázis integráció.
- Ratio-Filter: Kiszűri a 0.19-es kW/kg arányszámokat.
- Multi-field Power Discovery: Minden lehetséges mezőből kinyeri a kW-ot.
- Dinamikus évjárat kezelés a duplikációk ellen.
"""
# API Végpontok
class CatalogHunter:
RDW_MAIN = "https://opendata.rdw.nl/resource/m9d7-ebf2.json"
RDW_FUEL = "https://opendata.rdw.nl/resource/8ys7-d773.json"
RDW_AXLE = "https://opendata.rdw.nl/resource/3huj-srit.json"
RDW_BODY = "https://opendata.rdw.nl/resource/vezc-m2t6.json"
UK_DVLA = "https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles"
US_NHTSA = "https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValuesBatch/"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
UK_API_KEY = os.getenv("UK_DVLA_API_KEY")
HEADERS_RDW = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
HEADERS_UK = {"x-api-key": UK_API_KEY, "Content-Type": "application/json"} if UK_API_KEY else {}
CATEGORY_MAP = {
"Personenauto": "car",
"Motorfiets": "motorcycle",
"Bedrijfsauto": "truck",
"Vrachtwagen": "truck",
"Opleggertrekker": "truck",
"Bus": "bus",
"Aanhangwagen": "trailer",
"Oplegger": "trailer",
"Landbouw- of bosbouwtrekker": "agricultural",
"camper": "camper"
@classmethod
async def get_total_count(cls, client, make_name):
"""Lekéri, összesen hány rekord létezik az adott márkához."""
query_filter = f"upper(merk) like '%{make_name.upper()}%'"
params = {
"$where": query_filter,
"$select": "count(*)"
}
@classmethod
def clean_kw(cls, val):
"""Speciális kW tisztító: ignorálja az 1.0 alatti arányszámokat."""
try:
if val is None: return None
f_val = float(str(val).replace(',', '.'))
if 0 < f_val < 1.0: return None # Ez csak arányszám (kW/kg)
v = int(f_val)
return v if v > 0 else None
except (ValueError, TypeError):
return None
@classmethod
def clean_int(cls, val):
"""Általános egész szám tisztító."""
try:
if val is None: return None
return int(float(str(val).replace(',', '.')))
except (ValueError, TypeError):
return None
@classmethod
async def fetch_api(cls, url, params=None, headers=None, method="GET", json_data=None):
"""Univerzális API hívó sebességkorlátozással."""
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
try:
await asyncio.sleep(1.2) # Biztonsági késleltetés
if method == "POST":
resp = await client.post(url, json=json_data, timeout=30)
else:
resp = await client.get(url, params=params, timeout=30)
return resp.json() if resp.status_code in [200, 201] else []
resp = await client.get(cls.RDW_MAIN, params=params, headers=cls.HEADERS_RDW)
if resp.status_code == 200:
data = resp.json()
return int(data[0]['count'])
return 0
except Exception as e:
logger.error(f"❌ API Hiba ({url}): {e}")
return []
@classmethod
async def get_deep_tech(cls, plate, main_kw=None, vin=None):
"""Nemzetközi dúsítás: Holland -> Brit -> Amerikai sorrendben."""
res = {"kw": cls.clean_kw(main_kw), "fuel": "Unknown", "axles": None, "body": "Standard", "euro": None}
# 1. HOLLAND (RDW) DÚSÍTÁS
fuel_data = await cls.fetch_api(cls.RDW_FUEL, {"kenteken": plate}, headers=cls.HEADERS_RDW)
if fuel_data:
f0 = fuel_data[0]
if not res["kw"]:
res["kw"] = cls.clean_kw(f0.get("nettomaximumvermogen") or f0.get("netto_maximum_vermogen"))
res["fuel"] = f0.get("brandstof_omschrijving", "Unknown")
res["euro"] = f0.get("uitlaatemissieniveau")
# 2. BRIT (DVLA) ELLENŐRZÉS (Ha van UK kulcs és még hiányzik adat)
if cls.UK_API_KEY and (not res["kw"] or not res["euro"]):
uk_data = await cls.fetch_api(cls.UK_DVLA, method="POST", json_data={"registrationNumber": plate}, headers=cls.HEADERS_UK)
if uk_data:
res["kw"] = res["kw"] or cls.clean_kw(uk_data.get("engineCapacity")) # Brit adatok finomítása
res["euro"] = res["euro"] or uk_data.get("euroStatus")
# 3. AMERIKAI (NHTSA) KUTATÁS (Ha van alvázszám)
if vin and len(vin) == 17:
us_data = await cls.fetch_api(cls.US_NHTSA, params={"format": "json", "data": vin})
if us_data and "Results" in us_data:
# Az amerikai adatbázisból kinyerjük a lóerőt (HP), ha a kW még mindig nincs meg
hp = us_data["Results"][0].get("EngineHP")
if hp and not res["kw"]:
res["kw"] = int(float(hp) * 0.7457) # HP -> kW konverzió
# RDW Extra adatok (Tengely, Karosszéria)
axle = await cls.fetch_api(cls.RDW_AXLE, {"kenteken": plate}, headers=cls.HEADERS_RDW)
if axle: res["axles"] = cls.clean_int(axle[0].get("aantal_assen"))
body = await cls.fetch_api(cls.RDW_BODY, {"kenteken": plate}, headers=cls.HEADERS_RDW)
if body: res["body"] = body[0].get("carrosserie_omschrijving", "Standard")
return res
logger.error(f"⚠️ Nem sikerült a számlálás: {e}")
return 0
@classmethod
async def process_make(cls, db, task_id, make_name):
logger.info(f"🚀 >>> {make_name} GlobalHunter v1.0.13 INDUL...")
offset, limit, total_saved = 0, 1000, 0
unique_variants = {}
clean_make = make_name.strip().upper()
while True:
params = {"merk": make_name.upper(), "$limit": limit, "$offset": offset}
main_data = await cls.fetch_api(cls.RDW_MAIN, params, headers=cls.HEADERS_RDW)
if not main_data: break
for item in main_data:
plate = item.get("kenteken")
if not plate: continue
model = str(item.get("handelsbenaming", "Unknown")).upper()
ccm = cls.clean_int(item.get("cilinderinhoud"))
weight = cls.clean_int(item.get("massa_ledig_voertuig") or item.get("massa_rijklaar"))
kw_candidate = item.get("netto_maximum_vermogen") or item.get("vermogen_massarijklaar")
raw_date = item.get("datum_eerste_toelating")
prod_year = int(str(raw_date)[:4]) if raw_date else 2024
v_class = cls.CATEGORY_MAP.get(item.get("voertuigsoort"), "other")
if "kampeerwagen" in str(item.get("inrichting", "")).lower(): v_class = "camper"
# Variáns kulcs: Modell + CCM + Súly + kW + Év = Egyedi technikai ujjlenyomat
variant_key = f"{model}-{ccm}-{weight}-{v_class}-{kw_candidate}-{prod_year}"
if variant_key not in unique_variants:
unique_variants[variant_key] = {
"model": model, "ccm": ccm, "weight": weight, "v_class": v_class,
"plate": plate, "main_kw": kw_candidate, "prod_year": prod_year,
"vin": item.get("vin") # Ha az RDW-ben benne van a VIN
}
if len(main_data) < limit or offset > 90000: break
offset += limit
logger.info(f"📊 {len(unique_variants)} egyedi variáns kutatása indul...")
for key, v in unique_variants.items():
deep = await cls.get_deep_tech(v["plate"], main_kw=v["main_kw"], vin=v["vin"])
try:
db_item = AssetCatalog(
make=make_name.upper(), model=v["model"], vehicle_class=v["v_class"],
fuel_type=deep["fuel"], power_kw=deep["kw"], engine_capacity=v["ccm"],
max_weight_kg=v["weight"], axle_count=deep["axles"], body_type=deep["body"],
year_from=v["prod_year"], euro_class=deep["euro"],
factory_data={
"source": "GlobalHunter-v1.0.13",
"sample_plate": v["plate"],
"enriched_at": str(datetime.datetime.now())
}
)
db.add(db_item)
await db.commit()
total_saved += 1
if total_saved % 50 == 0: logger.info(f"{total_saved} variáns elmentve.")
except Exception:
await db.rollback()
continue
async with httpx.AsyncClient(timeout=60) as client:
# 1. LÉPÉS: Megszámoljuk az összes rekordot
total_available = await cls.get_total_count(client, clean_make)
logger.info(f"🚀 >>> {clean_make} feltérképezése: {total_available} variáns található az RDW-ben.")
if total_available == 0:
logger.warning(f"⚠️ {clean_make} márkához nem érkezett adat az API-tól.")
await db.execute(text("UPDATE data.catalog_discovery SET status = 'processed' WHERE id = :id"), {"id": task_id})
await db.commit()
logger.info(f"🏁 {make_name} KÉSZ. {total_saved} rekord rögzítve.")
return
# 2. LÉPÉS: Lapozás (Pagination)
limit = 1000
offset = 0
total_added = 0
while offset < total_available:
logger.info(f"📑 Lapozás: {clean_make} | {offset} -> {offset + limit} (Összesen: {total_available})")
query_filter = f"upper(merk) like '%{clean_make}%'"
params = {
"$where": query_filter,
"$limit": limit,
"$offset": offset,
"$order": ":id" # Socrata stabil lapozáshoz javasolt
}
resp = await client.get(cls.RDW_MAIN, params=params, headers=cls.HEADERS_RDW)
if resp.status_code != 200:
logger.error(f"❌ Hiba a lapozásnál ({offset}): {resp.status_code}")
break
batch = resp.json()
if not batch: break
# Feldolgozás
for item in batch:
res_make = str(item.get("merk", clean_make)).upper()
model = str(item.get("handelsbenaming", "Unknown")).upper()
ccm = int(float(item.get("cilinderinhoud") or 0))
kw = int(float(item.get("netto_maximum_vermogen") or 0))
# Deduplikáció check
stmt = select(VehicleModelDefinition.id).where(
VehicleModelDefinition.make == res_make,
VehicleModelDefinition.marketing_name == model,
VehicleModelDefinition.engine_capacity == ccm,
VehicleModelDefinition.power_kw == kw
).limit(1)
exists = (await db.execute(stmt)).scalar_one_or_none()
if not exists:
db.add(VehicleModelDefinition(
make=res_make,
technical_code=item.get("kenteken"),
marketing_name=model,
engine_capacity=ccm,
power_kw=kw if kw > 0 else None,
status="unverified",
source="HUNTER-v2.4-PAGINATED"
))
total_added += 1
await db.commit() # Lapvégi mentés
offset += limit
# 3. LÉPÉS: Befejezés
await db.execute(text("UPDATE data.catalog_discovery SET status = 'processed' WHERE id = :id"), {"id": task_id})
await db.commit()
logger.info(f"{clean_make} KÉSZ. {total_available} rekord átnézve, {total_added} új variáns stagingbe mentve.")
@classmethod
async def run(cls):
logger.info("🤖 Robot 1.0.13 (Global Hunter) ONLINE")
logger.info("🤖 Robot 1 (Hunter) ONLINE - Paginator v2.4")
while True:
async with SessionLocal() as db:
res = await db.execute(text("SELECT id, make FROM data.catalog_discovery WHERE status = 'pending' LIMIT 1"))
query = text("""
SELECT id, make FROM data.catalog_discovery
WHERE status = 'pending'
ORDER BY
CASE WHEN make IN ('SUZUKI', 'TOYOTA', 'SKODA', 'VOLKSWAGEN', 'OPEL') THEN 1 ELSE 2 END,
id ASC
LIMIT 1 FOR UPDATE SKIP LOCKED
""")
res = await db.execute(query)
task = res.fetchone()
if task:
await cls.process_make(db, task[0], task[1])
else:
logger.info("😴 Várólista üres. Alvás 60 mp...")
await asyncio.sleep(60)
await asyncio.sleep(1)
await asyncio.sleep(20)
if __name__ == "__main__":
asyncio.run(CatalogMaster.run())
asyncio.run(CatalogHunter.run())

View File

@@ -0,0 +1,208 @@
import asyncio
import httpx
import logging
import json
import os
import datetime
from sqlalchemy import text
from app.db.session import SessionLocal
from app.models.asset import AssetCatalog
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("Robot-v1.0.13-Global-Hunter")
class CatalogMaster:
"""
Master Hunter Robot v1.0.13 - Global Hunter Edition
- Holland (RDW), Brit (DVLA) és Amerikai (NHTSA) adatbázis integráció.
- Ratio-Filter: Kiszűri a 0.19-es kW/kg arányszámokat.
- Multi-field Power Discovery: Minden lehetséges mezőből kinyeri a kW-ot.
- Dinamikus évjárat kezelés a duplikációk ellen.
"""
# API Végpontok
RDW_MAIN = "https://opendata.rdw.nl/resource/m9d7-ebf2.json"
RDW_FUEL = "https://opendata.rdw.nl/resource/8ys7-d773.json"
RDW_AXLE = "https://opendata.rdw.nl/resource/3huj-srit.json"
RDW_BODY = "https://opendata.rdw.nl/resource/vezc-m2t6.json"
UK_DVLA = "https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles"
US_NHTSA = "https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValuesBatch/"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
UK_API_KEY = os.getenv("UK_DVLA_API_KEY")
HEADERS_RDW = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
HEADERS_UK = {"x-api-key": UK_API_KEY, "Content-Type": "application/json"} if UK_API_KEY else {}
CATEGORY_MAP = {
"Personenauto": "car",
"Motorfiets": "motorcycle",
"Bedrijfsauto": "truck",
"Vrachtwagen": "truck",
"Opleggertrekker": "truck",
"Bus": "bus",
"Aanhangwagen": "trailer",
"Oplegger": "trailer",
"Landbouw- of bosbouwtrekker": "agricultural",
"camper": "camper"
}
@classmethod
def clean_kw(cls, val):
"""Speciális kW tisztító: ignorálja az 1.0 alatti arányszámokat."""
try:
if val is None: return None
f_val = float(str(val).replace(',', '.'))
if 0 < f_val < 1.0: return None # Ez csak arányszám (kW/kg)
v = int(f_val)
return v if v > 0 else None
except (ValueError, TypeError):
return None
@classmethod
def clean_int(cls, val):
"""Általános egész szám tisztító."""
try:
if val is None: return None
return int(float(str(val).replace(',', '.')))
except (ValueError, TypeError):
return None
@classmethod
async def fetch_api(cls, url, params=None, headers=None, method="GET", json_data=None):
"""Univerzális API hívó sebességkorlátozással."""
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
try:
await asyncio.sleep(1.2) # Biztonsági késleltetés
if method == "POST":
resp = await client.post(url, json=json_data, timeout=30)
else:
resp = await client.get(url, params=params, timeout=30)
return resp.json() if resp.status_code in [200, 201] else []
except Exception as e:
logger.error(f"❌ API Hiba ({url}): {e}")
return []
@classmethod
async def get_deep_tech(cls, plate, main_kw=None, vin=None):
"""Nemzetközi dúsítás: Holland -> Brit -> Amerikai sorrendben."""
res = {"kw": cls.clean_kw(main_kw), "fuel": "Unknown", "axles": None, "body": "Standard", "euro": None}
# 1. HOLLAND (RDW) DÚSÍTÁS
fuel_data = await cls.fetch_api(cls.RDW_FUEL, {"kenteken": plate}, headers=cls.HEADERS_RDW)
if fuel_data:
f0 = fuel_data[0]
if not res["kw"]:
res["kw"] = cls.clean_kw(f0.get("nettomaximumvermogen") or f0.get("netto_maximum_vermogen"))
res["fuel"] = f0.get("brandstof_omschrijving", "Unknown")
res["euro"] = f0.get("uitlaatemissieniveau")
# 2. BRIT (DVLA) ELLENŐRZÉS (Ha van UK kulcs és még hiányzik adat)
if cls.UK_API_KEY and (not res["kw"] or not res["euro"]):
uk_data = await cls.fetch_api(cls.UK_DVLA, method="POST", json_data={"registrationNumber": plate}, headers=cls.HEADERS_UK)
if uk_data:
res["kw"] = res["kw"] or cls.clean_kw(uk_data.get("engineCapacity")) # Brit adatok finomítása
res["euro"] = res["euro"] or uk_data.get("euroStatus")
# 3. AMERIKAI (NHTSA) KUTATÁS (Ha van alvázszám)
if vin and len(vin) == 17:
us_data = await cls.fetch_api(cls.US_NHTSA, params={"format": "json", "data": vin})
if us_data and "Results" in us_data:
# Az amerikai adatbázisból kinyerjük a lóerőt (HP), ha a kW még mindig nincs meg
hp = us_data["Results"][0].get("EngineHP")
if hp and not res["kw"]:
res["kw"] = int(float(hp) * 0.7457) # HP -> kW konverzió
# RDW Extra adatok (Tengely, Karosszéria)
axle = await cls.fetch_api(cls.RDW_AXLE, {"kenteken": plate}, headers=cls.HEADERS_RDW)
if axle: res["axles"] = cls.clean_int(axle[0].get("aantal_assen"))
body = await cls.fetch_api(cls.RDW_BODY, {"kenteken": plate}, headers=cls.HEADERS_RDW)
if body: res["body"] = body[0].get("carrosserie_omschrijving", "Standard")
return res
@classmethod
async def process_make(cls, db, task_id, make_name):
logger.info(f"🚀 >>> {make_name} GlobalHunter v1.0.13 INDUL...")
offset, limit, total_saved = 0, 1000, 0
unique_variants = {}
while True:
params = {"merk": make_name.upper(), "$limit": limit, "$offset": offset}
main_data = await cls.fetch_api(cls.RDW_MAIN, params, headers=cls.HEADERS_RDW)
if not main_data: break
for item in main_data:
plate = item.get("kenteken")
if not plate: continue
model = str(item.get("handelsbenaming", "Unknown")).upper()
ccm = cls.clean_int(item.get("cilinderinhoud"))
weight = cls.clean_int(item.get("massa_ledig_voertuig") or item.get("massa_rijklaar"))
kw_candidate = item.get("netto_maximum_vermogen") or item.get("vermogen_massarijklaar")
raw_date = item.get("datum_eerste_toelating")
prod_year = int(str(raw_date)[:4]) if raw_date else 2024
v_class = cls.CATEGORY_MAP.get(item.get("voertuigsoort"), "other")
if "kampeerwagen" in str(item.get("inrichting", "")).lower(): v_class = "camper"
# Variáns kulcs: Modell + CCM + Súly + kW + Év = Egyedi technikai ujjlenyomat
variant_key = f"{model}-{ccm}-{weight}-{v_class}-{kw_candidate}-{prod_year}"
if variant_key not in unique_variants:
unique_variants[variant_key] = {
"model": model, "ccm": ccm, "weight": weight, "v_class": v_class,
"plate": plate, "main_kw": kw_candidate, "prod_year": prod_year,
"vin": item.get("vin") # Ha az RDW-ben benne van a VIN
}
if len(main_data) < limit or offset > 90000: break
offset += limit
logger.info(f"📊 {len(unique_variants)} egyedi variáns kutatása indul...")
for key, v in unique_variants.items():
deep = await cls.get_deep_tech(v["plate"], main_kw=v["main_kw"], vin=v["vin"])
try:
db_item = AssetCatalog(
make=make_name.upper(), model=v["model"], vehicle_class=v["v_class"],
fuel_type=deep["fuel"], power_kw=deep["kw"], engine_capacity=v["ccm"],
max_weight_kg=v["weight"], axle_count=deep["axles"], body_type=deep["body"],
year_from=v["prod_year"], euro_class=deep["euro"],
factory_data={
"source": "GlobalHunter-v1.0.13",
"sample_plate": v["plate"],
"enriched_at": str(datetime.datetime.now())
}
)
db.add(db_item)
await db.commit()
total_saved += 1
if total_saved % 50 == 0: logger.info(f"{total_saved} variáns elmentve.")
except Exception:
await db.rollback()
continue
await db.execute(text("UPDATE data.catalog_discovery SET status = 'processed' WHERE id = :id"), {"id": task_id})
await db.commit()
logger.info(f"🏁 {make_name} KÉSZ. {total_saved} rekord rögzítve.")
@classmethod
async def run(cls):
logger.info("🤖 Robot 1.0.13 (Global Hunter) ONLINE")
while True:
async with SessionLocal() as db:
res = await db.execute(text("SELECT id, make FROM data.catalog_discovery WHERE status = 'pending' LIMIT 1"))
task = res.fetchone()
if task:
await cls.process_make(db, task[0], task[1])
else:
logger.info("😴 Várólista üres. Alvás 60 mp...")
await asyncio.sleep(60)
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(CatalogMaster.run())

View File

@@ -0,0 +1,270 @@
import asyncio
import httpx
import logging
import json
import os
import datetime
import sys
from sqlalchemy import text
from app.db.session import SessionLocal
from app.models.asset import AssetCatalog
# --- KÉNYSZERÍTETT IDŐBÉLYEGES LOGOLÁS ---
# Töröljük az esetleges korábbi konfigurációkat, hogy az időbélyeg garantált legyen
for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s.%(msecs)03d [%(levelname)s] %(name)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
stream=sys.stdout
)
logger = logging.getLogger("Robot-v1.4.1-Powerhouse")
class CatalogMaster:
"""
Master Hunter Robot v1.4.1 - Powerhouse Edition
- Párhuzamos Holland (RDW) és Amerikai (NHTSA Batch) Discovery.
- Garantált időbélyeges naplózás.
- Multi-Worker Safe (FOR UPDATE SKIP LOCKED).
- Rate Limit (429) védelem.
"""
# API Végpontok
RDW_MAIN = "https://opendata.rdw.nl/resource/m9d7-ebf2.json"
RDW_FUEL = "https://opendata.rdw.nl/resource/8ys7-d773.json"
RDW_AXLE = "https://opendata.rdw.nl/resource/3huj-srit.json"
RDW_BODY = "https://opendata.rdw.nl/resource/vezc-m2t6.json"
US_BATCH = "https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMakeYear/make/{make}/modelyear/{year}?format=json"
# BRIT API (Token után aktiválható)
UK_DVLA = "https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
UK_API_KEY = os.getenv("UK_DVLA_API_KEY")
HEADERS_RDW = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
HEADERS_UK = {"x-api-key": UK_API_KEY, "Content-Type": "application/json"} if UK_API_KEY else {}
CATEGORY_MAP = {
"Personenauto": "car",
"Motorfiets": "motorcycle",
"Bedrijfsauto": "truck",
"Vrachtwagen": "truck",
"Opleggertrekker": "truck",
"Bus": "bus",
"Aanhangwagen": "trailer",
"Oplegger": "trailer",
"Landbouw- of bosbouwtrekker": "agricultural",
"camper": "camper"
}
# Szabályozzuk a párhuzamos dúsítást (egyszerre max 5 kérés robotpéldányonként)
semaphore = asyncio.Semaphore(5)
@classmethod
def clean_kw(cls, val):
try:
if val is None: return None
f_val = float(str(val).replace(',', '.'))
if 0 < f_val < 1.0: return None
v = int(f_val)
return v if v > 0 else None
except (ValueError, TypeError):
return None
@classmethod
def clean_int(cls, val):
try:
if val is None: return None
return int(float(str(val).replace(',', '.')))
except (ValueError, TypeError):
return None
@classmethod
async def fetch_api(cls, url, params=None, headers=None, method="GET", json_data=None):
"""Intelligens API hívó 429-es védelemmel és időzített logolással."""
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
for attempt in range(3):
try:
if method == "POST":
resp = await client.post(url, json=json_data, timeout=30)
else:
resp = await client.get(url, params=params, timeout=30)
if resp.status_code == 429:
wait_time = (attempt + 1) * 5
logger.warning(f"⚠️ RATE LIMIT! Várakozás {wait_time}mp: {url}")
await asyncio.sleep(wait_time)
continue
return resp.json() if resp.status_code in [200, 201] else []
except Exception as e:
logger.error(f"❌ API Hiba ({url}): {e}")
await asyncio.sleep(2)
return []
@classmethod
async def get_deep_tech(cls, plate, main_kw=None, vin=None):
"""Mély dúsítás több forrásból párhuzamosan."""
async with cls.semaphore:
res = {"kw": cls.clean_kw(main_kw), "fuel": "Unknown", "axles": None, "body": "Standard", "euro": None}
# --- 1. HOLLAND (RDW) DÚSÍTÁS ---
fuel_task = cls.fetch_api(cls.RDW_FUEL, {"kenteken": plate}, headers=cls.HEADERS_RDW)
axle_task = cls.fetch_api(cls.RDW_AXLE, {"kenteken": plate}, headers=cls.HEADERS_RDW)
fuel_data, axle_data = await asyncio.gather(fuel_task, axle_task)
if fuel_data:
f0 = fuel_data[0]
if not res["kw"]:
res["kw"] = cls.clean_kw(f0.get("nettomaximumvermogen") or f0.get("netto_maximum_vermogen"))
res["fuel"] = f0.get("brandstof_omschrijving", "Unknown")
res["euro"] = f0.get("uitlaatemissieniveau")
if axle_data:
res["axles"] = cls.clean_int(axle_data[0].get("aantal_assen"))
# --- 2. BRIT (DVLA) ELLENŐRZÉS (AKTIVÁLHATÓ KULCCSAL) ---
"""
if cls.UK_API_KEY and (not res["kw"] or not res["euro"]):
uk_data = await cls.fetch_api(cls.UK_DVLA, method="POST",
json_data={"registrationNumber": plate},
headers=cls.HEADERS_UK)
if uk_data and not isinstance(uk_data, list):
res["kw"] = res["kw"] or cls.clean_kw(uk_data.get("engineCapacity"))
res["euro"] = res["euro"] or uk_data.get("euroStatus")
"""
return res
@classmethod
async def discover_holland(cls, make_name, limit=1000):
"""Holland Discovery ág: rendszámok gyűjtése."""
offset, variants = 0, {}
while True:
params = {"merk": make_name.upper(), "$limit": limit, "$offset": offset}
data = await cls.fetch_api(cls.RDW_MAIN, params, headers=cls.HEADERS_RDW)
if not data: break
for item in data:
plate = item.get("kenteken")
if not plate: continue
model = str(item.get("handelsbenaming", "Unknown")).upper()
ccm = cls.clean_int(item.get("cilinderinhoud"))
weight = cls.clean_int(item.get("massa_ledig_voertuig") or item.get("massa_rijklaar"))
kw = item.get("netto_maximum_vermogen") or item.get("vermogen_massarijklaar")
raw_date = item.get("datum_eerste_toelating")
year = int(str(raw_date)[:4]) if raw_date else 2024
v_class = cls.CATEGORY_MAP.get(item.get("voertuigsoort"), "other")
key = f"{model}-{ccm}-{weight}-{v_class}-{kw}-{year}"
if key not in variants:
variants[key] = {
"model": model, "ccm": ccm, "weight": weight, "v_class": v_class,
"plate": plate, "main_kw": kw, "prod_year": year, "vin": item.get("vin")
}
if len(data) < limit: break
offset += limit
return variants
@classmethod
async def discover_usa_batch(cls, make_name):
"""Amerikai NHTSA Batch Discovery: Típusok gyűjtése."""
variants = {}
years = range(datetime.datetime.now().year - 5, datetime.datetime.now().year + 1)
async def fetch_year(year):
url = cls.US_BATCH.format(make=make_name.upper(), year=year)
logger.info(f"🇺🇸 USA Batch Discovery indítása: {make_name} ({year})")
data = await cls.fetch_api(url)
if data and "Results" in data:
for m in data["Results"]:
m_name = m.get("Model_Name", "Unknown").upper()
key = f"US-{m_name}-{year}"
if key not in variants:
variants[key] = {
"model": m_name, "ccm": None, "weight": None, "v_class": "car",
"plate": "US-DISCOVERY", "main_kw": None, "prod_year": year, "vin": None
}
await asyncio.gather(*(fetch_year(y) for y in years))
return variants
@classmethod
async def process_make(cls, db, task_id, make_name):
logger.info(f"🚀 >>> {make_name} Powerhouse v1.4.1 INDUL...")
# Párhuzamos Discovery
holland_task = cls.discover_holland(make_name)
usa_task = cls.discover_usa_batch(make_name)
holland_variants, usa_variants = await asyncio.gather(holland_task, usa_task)
all_variants = {**usa_variants, **holland_variants}
logger.info(f"📊 Összefésült variánsok száma: {len(all_variants)}")
async def enrich_and_save(v):
deep = await cls.get_deep_tech(v["plate"], main_kw=v["main_kw"], vin=v["vin"])
try:
db_item = AssetCatalog(
make=make_name.upper(), model=v["model"], vehicle_class=v["v_class"],
fuel_type=deep["fuel"], power_kw=deep["kw"], engine_capacity=v["ccm"],
max_weight_kg=v["weight"], axle_count=deep["axles"], body_type=deep["body"],
year_from=v["prod_year"], euro_class=deep["euro"],
factory_data={
"source": "Powerhouse-v1.4.1",
"discovery_nl": v["plate"] != "US-DISCOVERY",
"enriched_at": str(datetime.datetime.now())
}
)
return db_item
except Exception:
return None
# Párhuzamos dúsítás (Semaphore korláttal)
results = await asyncio.gather(*(enrich_and_save(v) for v in all_variants.values()))
total_saved = 0
for item in results:
if item:
db.add(item)
total_saved += 1
await db.commit()
await db.execute(text("UPDATE data.catalog_discovery SET status = 'processed' WHERE id = :id"), {"id": task_id})
await db.commit()
logger.info(f"🏁 {make_name} KÉSZ. {total_saved} egyedi rekord rögzítve.")
@classmethod
async def run(cls):
logger.info("🤖 Robot 1.4.1 (Powerhouse) ONLINE - Multi-Worker Safe Mode")
while True:
async with SessionLocal() as db:
# SKIP LOCKED védelem a párhuzamos futtatáshoz
query = text("""
SELECT id, make FROM data.catalog_discovery
WHERE status = 'pending'
LIMIT 1
FOR UPDATE SKIP LOCKED
""")
res = await db.execute(query)
task = res.fetchone()
if task:
task_id, make_name = task
await db.execute(
text("UPDATE data.catalog_discovery SET status = 'running' WHERE id = :id"),
{"id": task_id}
)
await db.commit()
await cls.process_make(db, task_id, make_name)
else:
logger.info("😴 Várólista üres vagy minden feladat foglalt. Alvás 60mp...")
await asyncio.sleep(60)
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(CatalogMaster.run())

View File

@@ -0,0 +1,272 @@
import asyncio
import httpx
import logging
import json
import os
import datetime
from sqlalchemy import text
from app.db.session import SessionLocal
from app.models.asset import AssetCatalog
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("Robot-v1.4-Powerhouse")
class CatalogMaster:
"""
Master Hunter Robot v1.4 - Powerhouse Edition
- Párhuzamos Holland (RDW) és Amerikai (NHTSA Batch) Discovery.
- Előkészített, kikommentelt Brit (DVLA) integráció.
- Async Semaphore: Párhuzamos technikai dúsítás (egyszerre 10 szálon).
- Intelligens összefésülés a globális források között.
"""
# API Végpontok
RDW_MAIN = "https://opendata.rdw.nl/resource/m9d7-ebf2.json"
RDW_FUEL = "https://opendata.rdw.nl/resource/8ys7-d773.json"
RDW_AXLE = "https://opendata.rdw.nl/resource/3huj-srit.json"
RDW_BODY = "https://opendata.rdw.nl/resource/vezc-m2t6.json"
# AMERIKAI BATCH API: Egyetlen hívással az összes modell évjárat szerint
US_BATCH = "https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMakeYear/make/{make}/modelyear/{year}?format=json"
# BRIT API (Kikapcsolva a tokenig)
# UK_DVLA = "https://driver-vehicle-licensing.api.gov.uk/vehicle-enquiry/v1/vehicles"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
UK_API_KEY = os.getenv("UK_DVLA_API_KEY") # Jövőbeli token helye
HEADERS_RDW = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
# HEADERS_UK = {"x-api-key": UK_API_KEY, "Content-Type": "application/json"} if UK_API_KEY else {}
CATEGORY_MAP = {
"Personenauto": "car",
"Motorfiets": "motorcycle",
"Bedrijfsauto": "truck",
"Vrachtwagen": "truck",
"Opleggertrekker": "truck",
"Bus": "bus",
"Aanhangwagen": "trailer",
"Oplegger": "trailer",
"Landbouw- of bosbouwtrekker": "agricultural",
"camper": "camper"
}
# Szabályozzuk a párhuzamos dúsítást, hogy ne tiltsanak le (egyszerre max 10 kérés)
semaphore = asyncio.Semaphore(5)
@classmethod
def clean_kw(cls, val):
try:
if val is None: return None
f_val = float(str(val).replace(',', '.'))
if 0 < f_val < 1.0: return None
v = int(f_val)
return v if v > 0 else None
except (ValueError, TypeError):
return None
@classmethod
def clean_int(cls, val):
try:
if val is None: return None
return int(float(str(val).replace(',', '.')))
except (ValueError, TypeError):
return None
@classmethod
async def fetch_api(cls, url, params=None, headers=None, method="GET", json_data=None):
async with httpx.AsyncClient(headers=headers, follow_redirects=True) as client:
for attempt in range(3): # 3-szor próbáljuk újra, ha kell
try:
if method == "POST":
resp = await client.post(url, json=json_data, timeout=30)
else:
resp = await client.get(url, params=params, timeout=30)
if resp.status_code == 429: # HOPPÁ, túl gyorsak vagyunk!
wait_time = (attempt + 1) * 5 # Egyre többet vár: 5s, 10s...
logger.warning(f"⚠️ RDW limit elérve! Pihenő {wait_time} mp...")
await asyncio.sleep(wait_time)
continue
return resp.json() if resp.status_code in [200, 201] else []
except Exception as e:
logger.error(f"❌ API Hiba ({url}): {e}")
await asyncio.sleep(2)
return []
@classmethod
async def get_deep_tech(cls, plate, main_kw=None, vin=None):
"""Mély dúsítás párhuzamos forrásokból."""
async with cls.semaphore:
res = {"kw": cls.clean_kw(main_kw), "fuel": "Unknown", "axles": None, "body": "Standard", "euro": None}
# --- 1. HOLLAND (RDW) DÚSÍTÁS ---
fuel_task = cls.fetch_api(cls.RDW_FUEL, {"kenteken": plate}, headers=cls.HEADERS_RDW)
axle_task = cls.fetch_api(cls.RDW_AXLE, {"kenteken": plate}, headers=cls.HEADERS_RDW)
# Holland adatok párhuzamos lekérése
fuel_data, axle_data = await asyncio.gather(fuel_task, axle_task)
if fuel_data:
f0 = fuel_data[0]
if not res["kw"]:
res["kw"] = cls.clean_kw(f0.get("nettomaximumvermogen") or f0.get("netto_maximum_vermogen"))
res["fuel"] = f0.get("brandstof_omschrijving", "Unknown")
res["euro"] = f0.get("uitlaatemissieniveau")
if axle_data:
res["axles"] = cls.clean_int(axle_data[0].get("aantal_assen"))
# --- 2. BRIT (DVLA) ELLENŐRZÉS (KIKOMMENTELVE A TOKENIG) ---
"""
if cls.UK_API_KEY and (not res["kw"] or not res["euro"]):
uk_data = await cls.fetch_api(cls.UK_DVLA, method="POST",
json_data={"registrationNumber": plate},
headers=cls.HEADERS_UK)
if uk_data and not isinstance(uk_data, list):
res["kw"] = res["kw"] or cls.clean_kw(uk_data.get("engineCapacity"))
res["euro"] = res["euro"] or uk_data.get("euroStatus")
"""
return res
@classmethod
async def discover_holland(cls, make_name, limit=1000):
"""Holland Discovery ág."""
offset, variants = 0, {}
while True:
params = {"merk": make_name.upper(), "$limit": limit, "$offset": offset}
data = await cls.fetch_api(cls.RDW_MAIN, params, headers=cls.HEADERS_RDW)
if not data: break
for item in data:
plate = item.get("kenteken")
if not plate: continue
model = str(item.get("handelsbenaming", "Unknown")).upper()
ccm = cls.clean_int(item.get("cilinderinhoud"))
weight = cls.clean_int(item.get("massa_ledig_voertuig") or item.get("massa_rijklaar"))
kw = item.get("netto_maximum_vermogen") or item.get("vermogen_massarijklaar")
raw_date = item.get("datum_eerste_toelating")
year = int(str(raw_date)[:4]) if raw_date else 2024
v_class = cls.CATEGORY_MAP.get(item.get("voertuigsoort"), "other")
key = f"{model}-{ccm}-{weight}-{v_class}-{kw}-{year}"
if key not in variants:
variants[key] = {
"model": model, "ccm": ccm, "weight": weight, "v_class": v_class,
"plate": plate, "main_kw": kw, "prod_year": year, "vin": item.get("vin")
}
if len(data) < limit: break
offset += limit
return variants
@classmethod
async def discover_usa_batch(cls, make_name):
"""Amerikai NHTSA Batch Discovery ág (2020-2025 évjáratokra)."""
variants = {}
# Az utolsó 5 évjáratot nézzük a legfrissebb modellekért
years = range(datetime.datetime.now().year - 5, datetime.datetime.now().year + 1)
async def fetch_year(year):
url = cls.US_BATCH.format(make=make_name.upper(), year=year)
data = await cls.fetch_api(url)
if data and "Results" in data:
for m in data["Results"]:
m_name = m.get("Model_Name", "Unknown").upper()
# US adatoknál nincs rendszám, de a Robot 2 dúsítani fogja ha kell
key = f"US-{m_name}-{year}"
variants[key] = {
"model": m_name, "ccm": None, "weight": None, "v_class": "car",
"plate": "US-DISCOVERY", "main_kw": None, "prod_year": year, "vin": None
}
await asyncio.gather(*(fetch_year(y) for y in years))
return variants
@classmethod
async def process_make(cls, db, task_id, make_name):
logger.info(f"🚀 >>> {make_name} Powerhouse v1.4 INDUL...")
# PÁRHUZAMOS DISCOVERY: Holland és USA egyszerre
holland_task = cls.discover_holland(make_name)
usa_task = cls.discover_usa_batch(make_name)
holland_variants, usa_variants = await asyncio.gather(holland_task, usa_task)
# Összefésülés (Holland élvez elsőbbséget a rendszám miatt)
all_variants = {**usa_variants, **holland_variants}
logger.info(f"📊 Összesen {len(all_variants)} egyedi variáns (NL: {len(holland_variants)}, US: {len(usa_variants)})")
# PÁRHUZAMOS DÚSÍTÁS
async def enrich_and_save(v):
deep = await cls.get_deep_tech(v["plate"], main_kw=v["main_kw"], vin=v["vin"])
try:
db_item = AssetCatalog(
make=make_name.upper(), model=v["model"], vehicle_class=v["v_class"],
fuel_type=deep["fuel"], power_kw=deep["kw"], engine_capacity=v["ccm"],
max_weight_kg=v["weight"], axle_count=deep["axles"], body_type=deep["body"],
year_from=v["prod_year"], euro_class=deep["euro"],
factory_data={
"source": "Powerhouse-v1.4",
"discovery_nl": v["plate"] != "US-DISCOVERY",
"enriched_at": str(datetime.datetime.now())
}
)
return db_item
except Exception:
return None
# Egyszerre indítjuk a dúsításokat (A semaphore korlátozza a szálakat)
results = await asyncio.gather(*(enrich_and_save(v) for v in all_variants.values()))
# Mentés
total_saved = 0
for item in results:
if item:
db.add(item)
total_saved += 1
await db.commit()
await db.execute(text("UPDATE data.catalog_discovery SET status = 'processed' WHERE id = :id"), {"id": task_id})
await db.commit()
logger.info(f"🏁 {make_name} KÉSZ. {total_saved} rekord rögzítve.")
@classmethod
async def run(cls):
logger.info("🤖 Robot 1.4 (Powerhouse) ONLINE - Multi-Worker Safe")
while True:
async with SessionLocal() as db:
# 1. 'FOR UPDATE SKIP LOCKED' - Megfogjuk a sort és lelakatoljuk,
# de a többi robot átugorja, amit mi már fogunk.
query = text("""
SELECT id, make FROM data.catalog_discovery
WHERE status = 'pending'
LIMIT 1
FOR UPDATE SKIP LOCKED
""")
res = await db.execute(query)
task = res.fetchone()
if task:
task_id, make_name = task
# 2. Azonnal átállítjuk 'running'-ra a tranzakción belül,
# így senki más nem nyúl hozzá.
await db.execute(
text("UPDATE data.catalog_discovery SET status = 'running' WHERE id = :id"),
{"id": task_id}
)
await db.commit() # Itt véglegesítjük a foglalást
# 3. Indulhat a tényleges munka
await cls.process_make(db, task_id, make_name)
else:
logger.info("😴 Várólista üres (vagy minden sor foglalt). Alvás 60 mp...")
await asyncio.sleep(60)
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(CatalogMaster.run())

View File

@@ -0,0 +1,117 @@
import asyncio
import logging
import warnings
import os
from sqlalchemy import select, update, and_, func, or_, case # Explicit case import
from app.db.session import SessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition
import httpx
# 1. KRITIKUS JAVÍTÁS: A figyelmeztetések globális elnyomása az import előtt
warnings.filterwarnings("ignore", category=RuntimeWarning, module='duckduckgo_search')
from duckduckgo_search import DDGS
# Logolás beállítása, hogy lássuk a haladást
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s')
logger = logging.getLogger("Robot-Researcher-v2.1")
class ResearcherBot:
def __init__(self):
self.batch_size = 15
self.max_parallel_queries = 5
async def fetch_source(self, label, query):
"""Egyedi forrás lekérése a DuckDuckGo-tól."""
try:
def search():
# Az újabb verziókban a DDGS() hívás így a legstabilabb
with DDGS() as ddgs:
results = ddgs.text(query, max_results=3)
return [r['body'] for r in results] if results else []
results = await asyncio.to_thread(search)
if not results:
return f"=== SOURCE: {label} | NO DATA FOUND ===\n\n"
content = f"=== SOURCE: {label} | QUERY: {query} ===\n"
content += "\n---\n".join(results)
content += "\n=== END SOURCE ===\n\n"
return content
except Exception as e:
logger.error(f"❌ Keresési hiba ({label}): {e}")
return f"=== SOURCE: {label} ERROR: {str(e)} ===\n\n"
async def research_vehicle(self, vehicle_id):
async with SessionLocal() as db:
res = await db.execute(select(VehicleModelDefinition).where(VehicleModelDefinition.id == vehicle_id))
v = res.scalar_one_or_none()
if not v: return
make, model = v.make, v.marketing_name
# Jelöljük be, hogy a kutatás folyamatban van
await db.execute(update(VehicleModelDefinition).where(VehicleModelDefinition.id == vehicle_id).values(status='research_in_progress'))
await db.commit()
logger.info(f"🔎 Kutatás indul: {make} {model}")
queries = [
("TECH_SPECS", f"{make} {model} technical specifications engine power"),
("MAINTENANCE", f"{make} {model} service manual oil capacity spark plug"),
("TIRES_BRAKES", f"{make} {model} tire size brake pad type"),
("FLUIDS", f"{make} {model} coolant quantity transmission oil")
]
tasks = [self.fetch_source(label, q) for label, q in queries]
search_results = await asyncio.gather(*tasks)
full_context = "".join(search_results)
async with SessionLocal() as db:
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == vehicle_id)
.values(
raw_search_context=full_context,
status='awaiting_ai_synthesis', # Itt adjuk át a Robot 2.2-nek (Alchemist)
updated_at=func.now()
)
)
await db.commit()
logger.info(f"✅ Kutatás kész, adat a tartályban: {make} {model}")
async def run(self):
logger.info("🚀 Robot 2.1 (Researcher) ONLINE")
while True:
async with SessionLocal() as db:
# 2. KRITIKUS JAVÍTÁS: func.case helyett az explicit case() használata
# Ez javítja a "TypeError: got an unexpected keyword argument 'else_'" hibát
priorities = case(
(and_(VehicleModelDefinition.vehicle_type == 'car',
VehicleModelDefinition.make.in_(['SUZUKI', 'TOYOTA', 'SKODA', 'VOLKSWAGEN', 'OPEL'])), 1),
(VehicleModelDefinition.vehicle_type == 'car', 2),
(and_(VehicleModelDefinition.vehicle_type == 'motorcycle',
VehicleModelDefinition.make.in_(['HONDA', 'YAMAHA', 'SUZUKI', 'KAWASAKI'])), 3),
else_=4
)
stmt = select(VehicleModelDefinition.id).where(
or_(VehicleModelDefinition.status == 'unverified', VehicleModelDefinition.status == 'awaiting_research')
).order_by(priorities).limit(self.batch_size)
res = await db.execute(stmt)
ids = [r[0] for r in res.fetchall()]
if not ids:
logger.info("💤 Nincs több feldolgozandó feladat, pihenés...")
await asyncio.sleep(60)
continue
# Batch feldolgozás indítása párhuzamosan
await asyncio.gather(*[self.research_vehicle(rid) for rid in ids])
# Rövid szünet a keresőmotorok kímélése érdekében
await asyncio.sleep(2)
if __name__ == "__main__":
asyncio.run(ResearcherBot().run())

View File

@@ -0,0 +1,83 @@
import asyncio
import httpx
import logging
import os
from sqlalchemy import text
from app.db.session import SessionLocal
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s]: %(message)s')
logger = logging.getLogger("Robot-0-Strategist")
class Robot0Strategist:
RDW_API = "https://opendata.rdw.nl/resource/m9d7-ebf2.json"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
HEADERS = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
# Holland típusok leképezése a mi kategóriáinkra a kért sorrendben
CATEGORIES = [
{"name": "car", "rdw_types": ["'Personenauto'"]},
{"name": "motorcycle", "rdw_types": ["'Motorfiets'"]},
{"name": "truck", "rdw_types": ["'Bedrijfswagen'", "'Vrachtwagen'", "'Opleggertrekker'"]},
{"name": "other", "rdw_types": ["NOT IN ('Personenauto', 'Motorfiets', 'Bedrijfswagen', 'Vrachtwagen', 'Opleggertrekker')"]}
]
async def get_popular_makes(self, vehicle_class, rdw_types):
"""Lekéri az adott kategória legnépszerűbb márkáit az RDW-től."""
# SQL-szerű szűrés az API-n keresztül
type_filter = " OR ".join([f"voertuigsoort = {t}" for t in rdw_types])
if "NOT IN" in rdw_types[0]: # Speciális kezelés az 'egyéb' kategóriához
type_filter = f"voertuigsoort {rdw_types[0]}"
params = {
"$select": "merk, count(*)",
"$where": type_filter,
"$group": "merk",
"$order": "count DESC",
"$limit": 500 # Kategóriánként az 500 legfontosabb márka bőven elég
}
async with httpx.AsyncClient(timeout=30) as client:
try:
resp = await client.get(self.RDW_API, params=params, headers=self.HEADERS)
if resp.status_code == 200:
return resp.json()
return []
except Exception as e:
logger.error(f"❌ Hiba a {vehicle_class} lekérdezésekor: {e}")
return []
async def run(self):
logger.info("🚀 Robot 0 (Strategist) INDUL - Piaci alapú sorrend felállítása...")
async with SessionLocal() as db:
# 1. Töröljük a jelenlegi várólistát, hogy tiszta lappal induljunk (opcionális)
# await db.execute(text("DELETE FROM data.catalog_discovery WHERE status = 'pending'"))
for category in self.CATEGORIES:
v_class = category["name"]
logger.info(f"📊 {v_class.upper()} kategória elemzése...")
makes = await self.get_popular_makes(v_class, category["rdw_types"])
added_count = 0
for item in makes:
make_name = item.get("merk")
if not make_name: continue
# Beillesztés a Discovery táblába
# A prioritást az ID-k sorrendje fogja adni, amit Robot 1 követ
await db.execute(text("""
INSERT INTO data.catalog_discovery (make, model, vehicle_class, status, source)
VALUES (:make, 'ALL_MODELS', :class, 'pending', 'ROBOT-0-POPULARITY')
ON CONFLICT (make, model, vehicle_class) DO UPDATE
SET status = 'pending' WHERE catalog_discovery.status != 'processed'
"""), {"make": make_name.upper(), "class": v_class})
added_count += 1
await db.commit()
logger.info(f"{v_class.upper()}: {added_count} márka sorba állítva a népszerűség alapján.")
logger.info("🏁 Robot 0 végzett. A Discovery tábla készen áll a Robot 1 (Hunter) számára!")
if __name__ == "__main__":
asyncio.run(Robot0Strategist().run())

View File

@@ -2,159 +2,159 @@ import asyncio
import httpx
import logging
import os
import hashlib
from datetime import datetime, timezone
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, text
from sqlalchemy import select, text, update
from app.db.session import SessionLocal
# Modellek - Az új v1.3 struktúra
from app.models.service import ServiceStaging, DiscoveryParameter
# Naplózás
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger("Robot-v1.3-ContinentalScout")
logger = logging.getLogger("Robot-v1.3.1-ContinentalScout")
class ServiceHunter:
"""
Robot v1.3.0: Continental Scout.
EU-szintű felderítő motor, Discovery tábla alapú vezérléssel.
Robot v1.3.1: Continental Scout (Grid Search Edition)
- Dinamikus rácsbejárás a sűrű területek lefedésére.
- Ujjlenyomat-alapú deduplikáció.
- Bővített kulcsszókezelés.
"""
OVERPASS_URL = "http://overpass-api.de/api/interpreter"
PLACES_NEW_URL = "https://places.googleapis.com/v1/places:searchNearby"
GEOCODE_URL = "https://maps.googleapis.com/maps/api/geocode/json"
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
@classmethod
async def get_coordinates(cls, city, country_code):
"""Város központjának lekérése a keresés indításához."""
params = {"address": f"{city}, {country_code}", "key": cls.GOOGLE_API_KEY}
async with httpx.AsyncClient() as client:
resp = await client.get(cls.GEOCODE_URL, params=params)
if resp.status_code == 200:
results = resp.json().get("results")
if results:
loc = results[0]["geometry"]["location"]
return loc["lat"], loc["lng"]
return None, None
def generate_fingerprint(cls, name: str, city: str, street: str) -> str:
"""Egyedi ujjlenyomat készítése a duplikációk kiszűrésére."""
raw_string = f"{str(name).lower()}|{str(city).lower()}|{str(street).lower()[:5]}"
return hashlib.md5(raw_string.encode()).hexdigest()
@classmethod
async def get_city_bounds(cls, city, country_code):
"""Város befoglaló téglalapjának (Bounding Box) lekérése Nominatim-al."""
url = "https://nominatim.openstreetmap.org/search"
params = {"city": city, "country": country_code, "format": "json"}
async with httpx.AsyncClient(headers={"User-Agent": "ServiceFinder-Scout/1.0"}) as client:
resp = await client.get(url, params=params)
if resp.status_code == 200 and resp.json():
bbox = resp.json()[0].get("boundingbox") # [min_lat, max_lat, min_lon, max_lon]
return [float(x) for x in bbox]
return None
@classmethod
async def run_grid_search(cls, db, task):
"""Rács-alapú bejárás a városon belül."""
bbox = await cls.get_city_bounds(task.city, task.country_code)
if not bbox: return
# 1km-es lépések generálása (kb. 0.01 fok)
lat_step = 0.015
lon_step = 0.02
curr_lat = bbox[0]
while curr_lat < bbox[1]:
curr_lon = bbox[2]
while curr_lon < bbox[3]:
logger.info(f"🛰️ Rács-cella pásztázása: {curr_lat}, {curr_lon} - Kulcsszó: {task.keyword}")
places = await cls.get_google_places(curr_lat, curr_lon, task.keyword)
for p in places:
# Adatok kinyerése és tisztítása
name = p.get('displayName', {}).get('text')
full_addr = p.get('formattedAddress', '')
# Ujjlenyomat generálás
f_print = cls.generate_fingerprint(name, task.city, full_addr)
await cls.save_to_staging(db, {
"external_id": p.get('id'),
"name": name,
"full_address": full_addr,
"phone": p.get('internationalPhoneNumber'),
"website": p.get('websiteUri'),
"fingerprint": f_print,
"city": task.city,
"source": "google",
"raw": p,
"trust": 30
})
curr_lon += lon_step
await asyncio.sleep(0.5) # API védelem
curr_lat += lat_step
@classmethod
async def get_google_places(cls, lat, lon, keyword):
"""Google Places New API - Javított, 400-as hiba elleni védelemmel."""
"""Google Places New API hívás rács-pontra."""
if not cls.GOOGLE_API_KEY: return []
headers = {
"Content-Type": "application/json",
"X-Goog-Api-Key": cls.GOOGLE_API_KEY,
"X-Goog-FieldMask": "places.displayName,places.id,places.types,places.internationalPhoneNumber,places.websiteUri,places.formattedAddress"
"X-Goog-FieldMask": "places.displayName,places.id,places.internationalPhoneNumber,places.websiteUri,places.formattedAddress"
}
# A 'keyword' a TextQuery-hez kellene, a SearchNearby-nél típusokat (includedTypes) használunk.
# EU szintű trükk: Ha nincs pontos típus, a 'car_repair' az alapértelmezett.
payload = {
"includedTypes": ["car_repair", "gas_station", "car_wash", "motorcycle_repair"],
"includedTypes": ["car_repair", "motorcycle_repair"],
"maxResultCount": 20,
"locationRestriction": {
"circle": {
"center": {"latitude": lat, "longitude": lon},
"radius": 5000.0 # 5km körzet
"radius": 1500.0 # 1.5km sugarú kör a fedés érdekében
}
}
}
async with httpx.AsyncClient() as client:
resp = await client.post(cls.PLACES_NEW_URL, json=payload, headers=headers)
if resp.status_code == 200:
return resp.json().get("places", [])
else:
logger.error(f"❌ Google API hiba ({resp.status_code}): {resp.text}")
return []
return resp.json().get("places", []) if resp.status_code == 200 else []
@classmethod
async def save_to_staging(cls, db: AsyncSession, data: dict):
"""Mentés a Staging táblába 9-mezős bontással."""
stmt = select(ServiceStaging).where(ServiceStaging.external_id == str(data['external_id']))
if (await db.execute(stmt)).scalar_one_or_none(): return
"""Mentés ujjlenyomat ellenőrzéssel."""
# 1. Megnézzük, létezik-e már ez az ujjlenyomat
stmt = select(ServiceStaging).where(ServiceStaging.fingerprint == data['fingerprint'])
existing = (await db.execute(stmt)).scalar_one_or_none()
if existing:
# Csak a bizalmi pontot növeljük és az utolsó észlelést frissítjük
existing.trust_score += 5
return
new_entry = ServiceStaging(
name=data['name'],
source=data['source'],
external_id=str(data['external_id']),
# Itt történik a 9-mezős bontás (ha érkezik adat)
postal_code=data.get('zip'),
city=data.get('city'),
street_name=data.get('street'),
street_type=data.get('street_type', 'utca'),
house_number=data.get('number'),
full_address=data.get('full_address'),
contact_phone=data.get('phone'),
website=data.get('website'),
fingerprint=data['fingerprint'],
city=data['city'],
full_address=data['full_address'],
contact_phone=data['phone'],
website=data['website'],
raw_data=data.get('raw', {}),
status="pending",
trust_score=data.get('trust', 10)
trust_score=data.get('trust', 30)
)
db.add(new_entry)
await db.flush()
@classmethod
async def run(cls):
logger.info("🤖 Robot v1.3.0: Continental Scout elindult...")
logger.info("🤖 Continental Scout v1.3.1 - Grid Engine INDUL...")
while True:
async with SessionLocal() as db:
try:
await db.execute(text("SET search_path TO data, public"))
# 1. Paraméterek lekérése a táblából
stmt = select(DiscoveryParameter).where(DiscoveryParameter.is_active == True)
tasks = (await db.execute(stmt)).scalars().all()
for task in tasks:
logger.info(f"🔎 Felderítés: {task.city} ({task.country_code}) -> {task.keyword}")
# Koordináták beszerzése a kereséshez
lat, lon = await cls.get_coordinates(task.city, task.country_code)
if not lat: continue
# --- GOOGLE FÁZIS ---
google_places = await cls.get_google_places(lat, lon, task.keyword)
for p in google_places:
await cls.save_to_staging(db, {
"external_id": p.get('id'),
"name": p.get('displayName', {}).get('text'),
"full_address": p.get('formattedAddress'),
"phone": p.get('internationalPhoneNumber'),
"website": p.get('websiteUri'),
"source": "google",
"raw": p,
"trust": 30
})
# --- OSM FÁZIS (EU kompatibilis lekérdezés) ---
osm_query = f"""[out:json][timeout:60];
(nwr["amenity"~"car_repair|fuel"](around:5000, {lat}, {lon}););
out center;"""
async with httpx.AsyncClient() as client:
resp = await client.post(cls.OVERPASS_URL, data={"data": osm_query})
if resp.status_code == 200:
for el in resp.json().get("elements", []):
t = el.get("tags", {})
await cls.save_to_staging(db, {
"external_id": f"osm_{el['id']}",
"name": t.get('name', 'Ismeretlen szerviz'),
"city": t.get('addr:city', task.city),
"zip": t.get('addr:postcode'),
"street": t.get('addr:street'),
"number": t.get('addr:housenumber'),
"source": "osm",
"raw": el,
"trust": 15
})
logger.info(f"🔎 Mélyfúrás indítása: {task.city} -> {task.keyword}")
await cls.run_grid_search(db, task)
task.last_run_at = datetime.now(timezone.utc)
await db.commit()
logger.info(f"{task.city} felderítve.")
except Exception as e:
logger.error(f"💥 Kritikus hiba a ciklusban: {e}")
logger.error(f"💥 Hiba: {e}")
await db.rollback()
logger.info("😴 Minden aktív feladat kész. Alvás 1 órán át...")
await asyncio.sleep(3600)
if __name__ == "__main__":

View File

@@ -3,113 +3,169 @@ import httpx
import logging
import os
import datetime
from sqlalchemy import select, and_
from sqlalchemy.exc import IntegrityError
import random
import sys
from sqlalchemy import select, and_, update, text, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import SessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition
from app.models.asset import AssetCatalog
from app.services.ai_service import AIService
from duckduckgo_search import DDGS
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("Robot-Bulk-Master")
# --- SZIGORÚ NAPLÓZÁS KONFIGURÁCIÓ ---
for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s.%(msecs)03d [%(levelname)s] Alchemist: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
stream=sys.stdout
)
logger = logging.getLogger("Robot-Enricher-v1.3.0")
class TechEnricher:
API_URL = "https://opendata.rdw.nl/resource/kyri-nuah.json"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
HEADERS = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
"""
Industrial TechEnricher v1.3.0
- Fix: Deadlock elkerülése izolált session-kezeléssel.
- Logika: Napi 500 AI hívás, Smart Merge, Web Fallback.
"""
@classmethod
async def fetch_rdw_tech_data(cls, make, model):
params = {"merk": make.upper(), "handelsbenaming": str(model).strip().upper(), "$limit": 1}
async with httpx.AsyncClient(headers=cls.HEADERS) as client:
def __init__(self):
self.max_attempts = 5
self.batch_size = 15
self.daily_ai_limit = 500
self.ai_calls_today = 0
self.last_reset_date = datetime.date.today()
def check_budget(self) -> bool:
if datetime.date.today() > self.last_reset_date:
self.ai_calls_today = 0
self.last_reset_date = datetime.date.today()
return self.ai_calls_today < self.daily_ai_limit
def is_data_sane(self, data: dict) -> bool:
try:
resp = await client.get(cls.API_URL, params=params, timeout=15)
return resp.json()[0] if resp.status_code == 200 and resp.json() else None
except: return None
if not data: return False
ccm = int(data.get("ccm", 0) or 0)
kw = int(data.get("kw", 0) or 0)
if ccm > 15000 or kw > 2000: return False
return True
except: return False
@classmethod
async def run(cls):
logger.info("🚀 Master-Merge Robot FOLYAMATOS ÜZEMMÓD INDUL...")
async def get_web_wisdom(self, make: str, model: str) -> str:
"""Keresés a neten izolált szálon (nem blokkolja az aszinkron loopot)."""
query = f"{make} {model} technical specs maintenance oil qty tire size"
try:
def sync_search():
with DDGS() as ddgs:
return "\n".join([r['body'] for r in ddgs.text(query, max_results=3)])
return await asyncio.to_thread(sync_search)
except Exception as e:
logger.warning(f"🌐 Web hiba ({make}): {e}")
return ""
while True: # Folyamatos ciklus, amíg el nem fogy az adat
async with SessionLocal() as main_db:
stmt = select(VehicleModelDefinition.id).where(
VehicleModelDefinition.status == "unverified"
).limit(50) # Egyszerre 50 ID-t foglalunk le
res = await main_db.execute(stmt)
ids = res.scalars().all()
if not ids:
logger.info("🏁 Minden rekord feldolgozva. A robot megáll.")
break
logger.info(f"📦 Új csomag indítása: {len(ids)} rekord.")
for m_id in ids:
async def process_single_record(self, record_id: int):
"""
Dúsítási folyamat 3 szigorúan elválasztott lépésben a fagyás ellen:
1. Adat lekérése és DB bezárása.
2. AI munka (DB nélkül).
3. Mentés új sessionben.
"""
# --- 1. LÉPÉS: ADAT LEKÉRÉSE ---
async with SessionLocal() as db:
stmt = select(VehicleModelDefinition).where(VehicleModelDefinition.id == record_id)
res = await db.execute(stmt)
rec = res.scalar_one_or_none()
if not rec: return
make, m_name, v_type = rec.make, rec.marketing_name, (rec.vehicle_type or "car")
logger.info(f"🧪 >>> Dúsítás indítása: {make} {m_name}")
# --- 2. LÉPÉS: AI MUNKA (DB session itt nincs nyitva!) ---
try:
current = await db.get(VehicleModelDefinition, m_id)
if not current: continue
# AIService hívása a kötelező 4. 'sources' paraméterrel
ai_data = await AIService.get_clean_vehicle_data(make, m_name, v_type, {})
logger.info(f"🧪 Feldolgozás: {current.make} {current.marketing_name} (ID: {m_id})")
if not ai_data or not ai_data.get("kw"):
logger.info(f"🔍 AI bizonytalan, webes dúsítás indul: {make} {m_name}")
web_info = await self.get_web_wisdom(make, m_name)
ai_data = await AIService.get_clean_vehicle_data(make, m_name, v_type, {"web_context": web_info})
rdw_data = await cls.fetch_rdw_tech_data(current.make, current.marketing_name)
if rdw_data:
current.engine_capacity = int(float(rdw_data.get("cilinderinhoud", 0))) or current.engine_capacity
current.power_kw = int(float(rdw_data.get("netto_maximum_vermogen_kw", 0))) or current.power_kw
if not ai_data: raise ValueError("Az AI nem adott értékelhető választ.")
ai_data = await AIService.get_clean_vehicle_data(current.make, current.marketing_name, current.vehicle_type)
# --- 3. LÉPÉS: MENTÉS (Új session nyitása) ---
async with SessionLocal() as db:
# MDM (AssetCatalog) Smart Merge
cat_stmt = select(AssetCatalog).where(and_(
AssetCatalog.make == make.upper(),
AssetCatalog.model == ai_data.get("marketing_name", m_name)[:50],
AssetCatalog.power_kw == ai_data.get("kw")
)).limit(1)
if ai_data:
tech_code = ai_data.get("technical_code") or "N/A"
new_ccm = ai_data.get("ccm") or current.engine_capacity
master_record = None
if tech_code and tech_code != "N/A":
stmt_master = select(VehicleModelDefinition).where(and_(
VehicleModelDefinition.make == current.make,
VehicleModelDefinition.technical_code == tech_code,
VehicleModelDefinition.engine_capacity == new_ccm,
VehicleModelDefinition.status == 'ai_enriched',
VehicleModelDefinition.id != m_id
if not (await db.execute(cat_stmt)).scalar_one_or_none():
db.add(AssetCatalog(
make=make.upper(),
model=ai_data.get("marketing_name", m_name)[:50],
power_kw=ai_data.get("kw"),
engine_capacity=ai_data.get("ccm"),
factory_data=ai_data
))
master_record = (await db.execute(stmt_master)).scalar_one_or_none()
logger.info(f"✅ Mentve az MDM-be: {make} {m_name}")
if master_record:
logger.info(f"🔗 Merge: ID:{m_id} -> Master ID:{master_record.id}")
syns = set(master_record.synonyms or [])
syns.update(ai_data.get("synonyms", []))
syns.add(current.marketing_name)
master_record.synonyms = list(syns)
current.status = "duplicate"
current.parent_id = master_record.id
else:
current.technical_code = tech_code if tech_code != "N/A" else f"N/A-{m_id}"
current.marketing_name = ai_data.get("marketing_name", current.marketing_name)
current.engine_capacity = new_ccm
current.power_kw = ai_data.get("kw") or current.power_kw
current.year_from = ai_data.get("year_from")
current.year_to = ai_data.get("year_to")
current.synonyms = ai_data.get("synonyms", [])
if ai_data.get("maintenance"):
old_spec = current.specifications or {}
old_spec.update(ai_data.get("maintenance"))
current.specifications = old_spec
current.status = "ai_enriched"
else:
if not current.technical_code:
current.technical_code = f"UNKNOWN-{m_id}"
current.updated_at = datetime.datetime.now()
# Staging frissítése
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == record_id)
.values(
status="ai_enriched",
technical_code=ai_data.get("technical_code") or f"GEN-{record_id}",
engine_capacity=ai_data.get("ccm"),
power_kw=ai_data.get("kw"),
updated_at=func.now()
)
)
await db.commit()
logger.info(f"✅ Mentve (ID: {m_id})")
self.ai_calls_today += 1
except Exception as e:
await db.rollback()
logger.error(f"❌ Hiba ID:{m_id}: {e}")
finally:
await db.close()
logger.error(f"🚨 Hiba a(z) {record_id} rekordnál: {e}")
async with SessionLocal() as db:
await db.execute(update(VehicleModelDefinition).where(VehicleModelDefinition.id == record_id).values(
attempts=VehicleModelDefinition.attempts + 1,
last_error=str(e)[:200],
status=text("CASE WHEN attempts >= 4 THEN 'suspended' ELSE 'unverified' END"),
updated_at=func.now()
))
await db.commit()
async def run(self):
logger.info(f"🚀 Robot 2 v1.3.0 ONLINE (Limit: {self.daily_ai_limit})")
while True:
if not self.check_budget():
await asyncio.sleep(3600); continue
try:
async with SessionLocal() as db:
# Csak az ID-kat kérjük le, hogy ne tartsuk nyitva a session-t a dúsítás alatt
stmt = select(VehicleModelDefinition.id).where(and_(
VehicleModelDefinition.status == "unverified",
VehicleModelDefinition.attempts < self.max_attempts
)).limit(self.batch_size)
ids = [r[0] for r in (await db.execute(stmt)).fetchall()]
if not ids:
await asyncio.sleep(60); continue
logger.info(f"📦 Batch indul: {len(ids)} rekord.")
for rid in ids:
await self.process_single_record(rid)
await asyncio.sleep(random.uniform(10.0, 30.0)) # VGA kímélése
except Exception as e:
logger.error(f"🚨 Főciklus hiba: {e}")
await asyncio.sleep(30)
if __name__ == "__main__":
asyncio.run(TechEnricher.run())
enricher = TechEnricher()
asyncio.run(enricher.run())

View File

@@ -0,0 +1,64 @@
import asyncio
import logging
import sys
import datetime
from sqlalchemy import select, and_, text, update
from sqlalchemy.orm import joinedload
from app.db.session import SessionLocal
from app.models.asset import Asset, AssetCatalog
from app.services.ai_service import AIService
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Auditor: %(message)s', stream=sys.stdout)
logger = logging.getLogger("VIN-Auditor-v1.3.0")
class VINAuditor:
"""
VIN Auditor v1.3.0
- Alvázszám alapú hitelesítés és MDM szinkron.
"""
@classmethod
async def audit_asset(cls, asset_id: int):
# 1. ADATGYŰJTÉS ÉS SESSION ZÁRÁS
async with SessionLocal() as db:
stmt = select(Asset).options(joinedload(Asset.catalog)).where(Asset.id == asset_id)
asset = (await db.execute(stmt)).scalar_one_or_none()
if not asset or not asset.vin: return
make, vin, current_kw = asset.catalog.make, asset.vin, asset.catalog.power_kw
# 2. AI FÁZIS (Izolált hívás)
try:
logger.info(f"🛡️ VIN Audit indul: {vin}")
truth = await AIService.get_clean_vehicle_data(make, vin, "vin_audit", {"vin": vin})
if truth and truth.get("kw"):
# 3. MENTÉSI FÁZIS (Új session)
async with SessionLocal() as db:
real_kw = int(truth["kw"])
if abs(real_kw - (current_kw or 0)) >= 5:
# Új variáns mentése
new_v = AssetCatalog(make=make.upper(), model=truth.get("marketing_name", "Unknown"), power_kw=real_kw)
db.add(new_v)
await db.flush()
await db.execute(update(Asset).where(Asset.id == asset_id).values(catalog_id=new_v.id, is_verified=True))
else:
await db.execute(update(Asset).where(Asset.id == asset_id).values(is_verified=True))
await db.commit()
logger.info(f"✅ Audit sikeres: {vin}")
except Exception as e:
logger.error(f"🚨 Auditor hiba: {e}")
async def run(self):
logger.info("🛡️ Auditor v1.3.0 ONLINE")
while True:
try:
async with SessionLocal() as db:
stmt = select(Asset.id).where(and_(Asset.is_verified == False, Asset.vin.isnot(None))).limit(1)
aid = (await db.execute(stmt)).scalar_one_or_none()
if aid: await self.audit_asset(aid)
else: await asyncio.sleep(60)
except: await asyncio.sleep(30)
if __name__ == "__main__":
asyncio.run(VINAuditor().run())

View File

@@ -0,0 +1,340 @@
"""fix_system_params_final
Revision ID: 105626809486
Revises: 835cc89dadc7
Create Date: 2026-02-22 07:26:15.174460
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '105626809486'
down_revision: Union[str, Sequence[str], None] = '835cc89dadc7'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', type_='foreignkey')
op.create_foreign_key(None, 'asset_assignments', 'branches', ['branch_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_operator_person_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_operator_org_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_owner_person_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_owner_org_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
op.create_foreign_key(None, 'assets', 'persons', ['owner_person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['owner_org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'persons', ['operator_person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['operator_org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('branches_organization_id_fkey'), 'branches', type_='foreignkey')
op.drop_constraint(op.f('branches_address_id_fkey'), 'branches', type_='foreignkey')
op.create_foreign_key(None, 'branches', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'branches', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', type_='foreignkey')
op.create_foreign_key(None, 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', type_='foreignkey')
op.create_foreign_key(None, 'financial_ledger', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['related_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.drop_constraint(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.create_foreign_key(None, 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('operational_logs_user_id_fkey'), 'operational_logs', type_='foreignkey')
op.create_foreign_key(None, 'operational_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='SET NULL')
op.drop_constraint(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.drop_constraint(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.create_foreign_key(None, 'org_sales_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_sales_assignments', 'users', ['agent_user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('organization_financials_organization_id_fkey'), 'organization_financials', type_='foreignkey')
op.create_foreign_key(None, 'organization_financials', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_person_id_fkey'), 'organization_members', type_='foreignkey')
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('ratings_target_organization_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_user_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_branch_id_fkey'), 'ratings', type_='foreignkey')
op.create_foreign_key(None, 'ratings', 'organizations', ['target_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'users', ['target_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'branches', ['target_branch_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['actor_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['target_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', type_='foreignkey')
op.drop_constraint(op.f('service_expertises_service_id_fkey'), 'service_expertises', type_='foreignkey')
op.create_foreign_key(None, 'service_expertises', 'service_profiles', ['service_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_profiles_organization_id_fkey'), 'service_profiles', type_='foreignkey')
op.drop_constraint(op.f('service_profiles_parent_id_fkey'), 'service_profiles', type_='foreignkey')
op.create_foreign_key(None, 'service_profiles', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_profiles', 'service_profiles', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.add_column('system_parameters', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False))
op.alter_column('system_parameters', 'value',
existing_type=postgresql.JSON(astext_type=sa.Text()),
type_=postgresql.JSONB(astext_type=sa.Text()),
existing_nullable=False)
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_current_sales_agent_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_referred_by_id_fkey'), 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['referred_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['current_sales_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.drop_constraint(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.create_foreign_key(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'])
op.drop_constraint(None, 'vehicle_catalog', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'])
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('users_referred_by_id_fkey'), 'users', 'users', ['referred_by_id'], ['id'])
op.create_foreign_key(op.f('users_current_sales_agent_id_fkey'), 'users', 'users', ['current_sales_agent_id'], ['id'])
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
op.alter_column('system_parameters', 'value',
existing_type=postgresql.JSONB(astext_type=sa.Text()),
type_=postgresql.JSON(astext_type=sa.Text()),
existing_nullable=False)
op.drop_column('system_parameters', 'id')
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_profiles_parent_id_fkey'), 'service_profiles', 'service_profiles', ['parent_id'], ['id'])
op.create_foreign_key(op.f('service_profiles_organization_id_fkey'), 'service_profiles', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_expertises_service_id_fkey'), 'service_expertises', 'service_profiles', ['service_id'], ['id'])
op.create_foreign_key(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'])
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', 'users', ['actor_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', 'users', ['target_id'], ['id'])
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('ratings_target_branch_id_fkey'), 'ratings', 'branches', ['target_branch_id'], ['id'])
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_user_id_fkey'), 'ratings', 'users', ['target_user_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_organization_id_fkey'), 'ratings', 'organizations', ['target_organization_id'], ['id'])
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
existing_nullable=True)
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_members_person_id_fkey'), 'organization_members', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
existing_nullable=True)
op.drop_constraint(None, 'organization_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_financials_organization_id_fkey'), 'organization_financials', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', 'users', ['agent_user_id'], ['id'])
op.create_foreign_key(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'operational_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('operational_logs_user_id_fkey'), 'operational_logs', 'users', ['user_id'], ['id'], ondelete='SET NULL')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'])
op.create_foreign_key(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'])
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', 'users', ['related_agent_id'], ['id'])
op.drop_constraint(None, 'feature_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('branches_address_id_fkey'), 'branches', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('branches_organization_id_fkey'), 'branches', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
op.create_foreign_key(op.f('assets_owner_org_id_fkey'), 'assets', 'organizations', ['owner_org_id'], ['id'])
op.create_foreign_key(op.f('assets_owner_person_id_fkey'), 'assets', 'persons', ['owner_person_id'], ['id'])
op.create_foreign_key(op.f('assets_operator_org_id_fkey'), 'assets', 'organizations', ['operator_org_id'], ['id'])
op.create_foreign_key(op.f('assets_operator_person_id_fkey'), 'assets', 'persons', ['operator_person_id'], ['id'])
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', 'branches', ['branch_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
# ### end Alembic commands ###

View File

@@ -0,0 +1,308 @@
"""Add robot protection fields v1.2.4
Revision ID: 492a65da864d
Revises: c64b951dbb86
Create Date: 2026-02-18 13:05:23.918947
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '492a65da864d'
down_revision: Union[str, Sequence[str], None] = 'c64b951dbb86'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', type_='foreignkey')
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'branches', ['branch_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('branches_organization_id_fkey'), 'branches', type_='foreignkey')
op.drop_constraint(op.f('branches_address_id_fkey'), 'branches', type_='foreignkey')
op.create_foreign_key(None, 'branches', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'branches', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', type_='foreignkey')
op.create_foreign_key(None, 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', type_='foreignkey')
op.create_foreign_key(None, 'financial_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['related_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.drop_constraint(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.create_foreign_key(None, 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('operational_logs_user_id_fkey'), 'operational_logs', type_='foreignkey')
op.create_foreign_key(None, 'operational_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='SET NULL')
op.drop_constraint(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.drop_constraint(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.create_foreign_key(None, 'org_sales_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_sales_assignments', 'users', ['agent_user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('organization_financials_organization_id_fkey'), 'organization_financials', type_='foreignkey')
op.create_foreign_key(None, 'organization_financials', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_person_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
op.create_foreign_key(None, 'organization_members', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['target_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['actor_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', type_='foreignkey')
op.drop_constraint(op.f('service_expertises_service_id_fkey'), 'service_expertises', type_='foreignkey')
op.create_foreign_key(None, 'service_expertises', 'service_profiles', ['service_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_profiles_organization_id_fkey'), 'service_profiles', type_='foreignkey')
op.create_foreign_key(None, 'service_profiles', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('users_referred_by_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_current_sales_agent_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'users', ['referred_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['current_sales_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('vehicle_model_definitions', sa.Column('is_manual', sa.Boolean(), server_default=sa.text('false'), nullable=True))
op.add_column('vehicle_model_definitions', sa.Column('attempts', sa.Integer(), server_default=sa.text('0'), nullable=True))
op.add_column('vehicle_model_definitions', sa.Column('last_error', sa.Text(), nullable=True))
op.create_index(op.f('ix_data_vehicle_model_definitions_attempts'), 'vehicle_model_definitions', ['attempts'], unique=False, schema='data')
op.create_index(op.f('ix_data_vehicle_model_definitions_is_manual'), 'vehicle_model_definitions', ['is_manual'], unique=False, schema='data')
op.drop_constraint(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.drop_constraint(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'])
op.create_foreign_key(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_index(op.f('ix_data_vehicle_model_definitions_is_manual'), table_name='vehicle_model_definitions', schema='data')
op.drop_index(op.f('ix_data_vehicle_model_definitions_attempts'), table_name='vehicle_model_definitions', schema='data')
op.drop_column('vehicle_model_definitions', 'last_error')
op.drop_column('vehicle_model_definitions', 'attempts')
op.drop_column('vehicle_model_definitions', 'is_manual')
op.drop_constraint(None, 'vehicle_catalog', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'])
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('users_current_sales_agent_id_fkey'), 'users', 'users', ['current_sales_agent_id'], ['id'])
op.create_foreign_key(op.f('users_referred_by_id_fkey'), 'users', 'users', ['referred_by_id'], ['id'])
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_profiles_organization_id_fkey'), 'service_profiles', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_expertises_service_id_fkey'), 'service_expertises', 'service_profiles', ['service_id'], ['id'])
op.create_foreign_key(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'])
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', 'users', ['target_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', 'users', ['actor_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'])
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
existing_nullable=True)
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('organization_members_person_id_fkey'), 'organization_members', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
existing_nullable=True)
op.drop_constraint(None, 'organization_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_financials_organization_id_fkey'), 'organization_financials', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', 'users', ['agent_user_id'], ['id'])
op.drop_constraint(None, 'operational_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('operational_logs_user_id_fkey'), 'operational_logs', 'users', ['user_id'], ['id'], ondelete='SET NULL')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'])
op.create_foreign_key(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'])
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', 'users', ['related_agent_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', 'persons', ['person_id'], ['id'])
op.drop_constraint(None, 'feature_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('branches_address_id_fkey'), 'branches', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('branches_organization_id_fkey'), 'branches', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', 'branches', ['branch_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
# ### end Alembic commands ###

View File

@@ -0,0 +1,356 @@
"""pipeline_v2_upgrade
Revision ID: 54cbd5c9e003
Revises: d362d1cb0b38
Create Date: 2026-02-20 11:45:15.360508
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '54cbd5c9e003'
down_revision: Union[str, Sequence[str], None] = 'd362d1cb0b38'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
op.create_foreign_key(None, 'asset_assignments', 'branches', ['branch_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('branches_organization_id_fkey'), 'branches', type_='foreignkey')
op.drop_constraint(op.f('branches_address_id_fkey'), 'branches', type_='foreignkey')
op.create_foreign_key(None, 'branches', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'branches', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', type_='foreignkey')
op.create_foreign_key(None, 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', type_='foreignkey')
op.create_foreign_key(None, 'financial_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['related_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.drop_constraint(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.create_foreign_key(None, 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('operational_logs_user_id_fkey'), 'operational_logs', type_='foreignkey')
op.create_foreign_key(None, 'operational_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='SET NULL')
op.drop_constraint(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.drop_constraint(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.create_foreign_key(None, 'org_sales_assignments', 'users', ['agent_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_sales_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('organization_financials_organization_id_fkey'), 'organization_financials', type_='foreignkey')
op.create_foreign_key(None, 'organization_financials', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_person_id_fkey'), 'organization_members', type_='foreignkey')
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_organization_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_user_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_branch_id_fkey'), 'ratings', type_='foreignkey')
op.create_foreign_key(None, 'ratings', 'branches', ['target_branch_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'users', ['target_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'organizations', ['target_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['target_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['actor_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', type_='foreignkey')
op.drop_constraint(op.f('service_expertises_service_id_fkey'), 'service_expertises', type_='foreignkey')
op.create_foreign_key(None, 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_expertises', 'service_profiles', ['service_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_profiles_parent_id_fkey'), 'service_profiles', type_='foreignkey')
op.drop_constraint(op.f('service_profiles_organization_id_fkey'), 'service_profiles', type_='foreignkey')
op.create_foreign_key(None, 'service_profiles', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_profiles', 'service_profiles', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.add_column('translations', sa.Column('lang', sa.String(length=5), nullable=True))
op.alter_column('translations', 'key',
existing_type=sa.VARCHAR(length=100),
type_=sa.String(length=255),
nullable=True)
op.alter_column('translations', 'value',
existing_type=sa.TEXT(),
nullable=True)
op.drop_index(op.f('ix_data_translations_lang_code'), table_name='translations')
op.drop_constraint(op.f('uq_translation_key_lang'), 'translations', type_='unique')
op.create_index(op.f('ix_data_translations_lang'), 'translations', ['lang'], unique=False, schema='data')
op.drop_column('translations', 'is_published')
op.drop_column('translations', 'lang_code')
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('users_current_sales_agent_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_referred_by_id_fkey'), 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'users', ['referred_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['current_sales_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('vehicle_model_definitions', sa.Column('raw_search_context', sa.Text(), nullable=True))
op.add_column('vehicle_model_definitions', sa.Column('research_metadata', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), nullable=False))
op.alter_column('vehicle_model_definitions', 'status',
existing_type=sa.VARCHAR(length=20),
type_=sa.String(length=30),
existing_nullable=True,
existing_server_default=sa.text("'unverified'::character varying"))
op.create_index(op.f('ix_data_vehicle_model_definitions_status'), 'vehicle_model_definitions', ['status'], unique=False, schema='data')
op.drop_constraint(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.drop_constraint(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.create_foreign_key(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'])
op.drop_index(op.f('ix_data_vehicle_model_definitions_status'), table_name='vehicle_model_definitions', schema='data')
op.alter_column('vehicle_model_definitions', 'status',
existing_type=sa.String(length=30),
type_=sa.VARCHAR(length=20),
existing_nullable=True,
existing_server_default=sa.text("'unverified'::character varying"))
op.drop_column('vehicle_model_definitions', 'research_metadata')
op.drop_column('vehicle_model_definitions', 'raw_search_context')
op.drop_constraint(None, 'vehicle_catalog', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'])
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('users_referred_by_id_fkey'), 'users', 'users', ['referred_by_id'], ['id'])
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('users_current_sales_agent_id_fkey'), 'users', 'users', ['current_sales_agent_id'], ['id'])
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
op.add_column('translations', sa.Column('lang_code', sa.VARCHAR(length=5), autoincrement=False, nullable=False))
op.add_column('translations', sa.Column('is_published', sa.BOOLEAN(), autoincrement=False, nullable=True))
op.drop_index(op.f('ix_data_translations_lang'), table_name='translations', schema='data')
op.create_unique_constraint(op.f('uq_translation_key_lang'), 'translations', ['key', 'lang_code'], postgresql_nulls_not_distinct=False)
op.create_index(op.f('ix_data_translations_lang_code'), 'translations', ['lang_code'], unique=False)
op.alter_column('translations', 'value',
existing_type=sa.TEXT(),
nullable=False)
op.alter_column('translations', 'key',
existing_type=sa.String(length=255),
type_=sa.VARCHAR(length=100),
nullable=False)
op.drop_column('translations', 'lang')
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_profiles_organization_id_fkey'), 'service_profiles', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('service_profiles_parent_id_fkey'), 'service_profiles', 'service_profiles', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_expertises_service_id_fkey'), 'service_expertises', 'service_profiles', ['service_id'], ['id'])
op.create_foreign_key(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'])
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', 'users', ['target_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', 'users', ['actor_id'], ['id'])
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('ratings_target_branch_id_fkey'), 'ratings', 'branches', ['target_branch_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_user_id_fkey'), 'ratings', 'users', ['target_user_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_organization_id_fkey'), 'ratings', 'organizations', ['target_organization_id'], ['id'])
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
existing_nullable=True)
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_members_person_id_fkey'), 'organization_members', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
existing_nullable=True)
op.drop_constraint(None, 'organization_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_financials_organization_id_fkey'), 'organization_financials', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', 'users', ['agent_user_id'], ['id'])
op.drop_constraint(None, 'operational_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('operational_logs_user_id_fkey'), 'operational_logs', 'users', ['user_id'], ['id'], ondelete='SET NULL')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'])
op.create_foreign_key(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'])
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', 'users', ['related_agent_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', 'persons', ['person_id'], ['id'])
op.drop_constraint(None, 'feature_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('branches_address_id_fkey'), 'branches', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('branches_organization_id_fkey'), 'branches', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', 'branches', ['branch_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
# ### end Alembic commands ###

View File

@@ -0,0 +1,338 @@
"""add_scope_columns_to_system_parameters
Revision ID: 835cc89dadc7
Revises: dd910cabe24e
Create Date: 2026-02-21 21:48:40.720825
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '835cc89dadc7'
down_revision: Union[str, Sequence[str], None] = 'dd910cabe24e'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'branches', ['branch_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('assets_operator_org_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_owner_org_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_owner_person_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_operator_person_id_fkey'), 'assets', type_='foreignkey')
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'persons', ['owner_person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'persons', ['operator_person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['owner_org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['operator_org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('branches_address_id_fkey'), 'branches', type_='foreignkey')
op.drop_constraint(op.f('branches_organization_id_fkey'), 'branches', type_='foreignkey')
op.create_foreign_key(None, 'branches', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'branches', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', type_='foreignkey')
op.create_foreign_key(None, 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', type_='foreignkey')
op.create_foreign_key(None, 'financial_ledger', 'users', ['related_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.drop_constraint(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.create_foreign_key(None, 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('operational_logs_user_id_fkey'), 'operational_logs', type_='foreignkey')
op.create_foreign_key(None, 'operational_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='SET NULL')
op.drop_constraint(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.drop_constraint(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.create_foreign_key(None, 'org_sales_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_sales_assignments', 'users', ['agent_user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('organization_financials_organization_id_fkey'), 'organization_financials', type_='foreignkey')
op.create_foreign_key(None, 'organization_financials', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_person_id_fkey'), 'organization_members', type_='foreignkey')
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_user_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_organization_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_branch_id_fkey'), 'ratings', type_='foreignkey')
op.create_foreign_key(None, 'ratings', 'users', ['target_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'branches', ['target_branch_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'organizations', ['target_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['actor_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['target_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', type_='foreignkey')
op.drop_constraint(op.f('service_expertises_service_id_fkey'), 'service_expertises', type_='foreignkey')
op.create_foreign_key(None, 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_expertises', 'service_profiles', ['service_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_profiles_organization_id_fkey'), 'service_profiles', type_='foreignkey')
op.drop_constraint(op.f('service_profiles_parent_id_fkey'), 'service_profiles', type_='foreignkey')
op.create_foreign_key(None, 'service_profiles', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_profiles', 'service_profiles', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.add_column('system_parameters', sa.Column('scope_level', sa.String(length=30), server_default=sa.text("'global'"), nullable=True))
op.add_column('system_parameters', sa.Column('scope_id', sa.String(length=50), nullable=True))
op.create_index(op.f('ix_data_system_parameters_scope_level'), 'system_parameters', ['scope_level'], unique=False, schema='data')
op.create_unique_constraint('uix_param_scope', 'system_parameters', ['key', 'scope_level', 'scope_id'], schema='data')
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_current_sales_agent_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_referred_by_id_fkey'), 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'users', ['current_sales_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['referred_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.drop_constraint(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.create_foreign_key(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'])
op.drop_constraint(None, 'vehicle_catalog', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'])
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('users_referred_by_id_fkey'), 'users', 'users', ['referred_by_id'], ['id'])
op.create_foreign_key(op.f('users_current_sales_agent_id_fkey'), 'users', 'users', ['current_sales_agent_id'], ['id'])
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
op.drop_constraint('uix_param_scope', 'system_parameters', schema='data', type_='unique')
op.drop_index(op.f('ix_data_system_parameters_scope_level'), table_name='system_parameters', schema='data')
op.drop_column('system_parameters', 'scope_id')
op.drop_column('system_parameters', 'scope_level')
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_profiles_parent_id_fkey'), 'service_profiles', 'service_profiles', ['parent_id'], ['id'])
op.create_foreign_key(op.f('service_profiles_organization_id_fkey'), 'service_profiles', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_expertises_service_id_fkey'), 'service_expertises', 'service_profiles', ['service_id'], ['id'])
op.create_foreign_key(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'])
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', 'users', ['target_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', 'users', ['actor_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'])
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('ratings_target_branch_id_fkey'), 'ratings', 'branches', ['target_branch_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_organization_id_fkey'), 'ratings', 'organizations', ['target_organization_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_user_id_fkey'), 'ratings', 'users', ['target_user_id'], ['id'])
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
existing_nullable=True)
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_members_person_id_fkey'), 'organization_members', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
existing_nullable=True)
op.drop_constraint(None, 'organization_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_financials_organization_id_fkey'), 'organization_financials', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', 'users', ['agent_user_id'], ['id'])
op.create_foreign_key(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'operational_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('operational_logs_user_id_fkey'), 'operational_logs', 'users', ['user_id'], ['id'], ondelete='SET NULL')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'])
op.create_foreign_key(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'])
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', 'users', ['related_agent_id'], ['id'])
op.drop_constraint(None, 'feature_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('branches_organization_id_fkey'), 'branches', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('branches_address_id_fkey'), 'branches', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('assets_operator_person_id_fkey'), 'assets', 'persons', ['operator_person_id'], ['id'])
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
op.create_foreign_key(op.f('assets_owner_person_id_fkey'), 'assets', 'persons', ['owner_person_id'], ['id'])
op.create_foreign_key(op.f('assets_owner_org_id_fkey'), 'assets', 'organizations', ['owner_org_id'], ['id'])
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
op.create_foreign_key(op.f('assets_operator_org_id_fkey'), 'assets', 'organizations', ['operator_org_id'], ['id'])
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', 'branches', ['branch_id'], ['id'])
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
# ### end Alembic commands ###

View File

@@ -0,0 +1,373 @@
"""Unified Master Schema v1.3.2
Revision ID: d362d1cb0b38
Revises: 492a65da864d
Create Date: 2026-02-18 23:00:05.907043
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = 'd362d1cb0b38'
down_revision: Union[str, Sequence[str], None] = '492a65da864d'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('ratings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('author_id', sa.Integer(), nullable=False),
sa.Column('target_organization_id', sa.Integer(), nullable=True),
sa.Column('target_user_id', sa.Integer(), nullable=True),
sa.Column('target_branch_id', sa.UUID(), nullable=True),
sa.Column('score', sa.Numeric(precision=3, scale=2), nullable=False),
sa.Column('comment', sa.Text(), nullable=True),
sa.Column('images', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'[]'::jsonb"), nullable=True),
sa.Column('is_verified', sa.Boolean(), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
sa.ForeignKeyConstraint(['author_id'], ['data.users.id'], ),
sa.ForeignKeyConstraint(['target_branch_id'], ['data.branches.id'], ),
sa.ForeignKeyConstraint(['target_organization_id'], ['data.organizations.id'], ),
sa.ForeignKeyConstraint(['target_user_id'], ['data.users.id'], ),
sa.PrimaryKeyConstraint('id'),
schema='data'
)
op.create_index('idx_rating_branch', 'ratings', ['target_branch_id'], unique=False, schema='data')
op.create_index('idx_rating_org', 'ratings', ['target_organization_id'], unique=False, schema='data')
op.create_index('idx_rating_user', 'ratings', ['target_user_id'], unique=False, schema='data')
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'branches', ['branch_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('branches_address_id_fkey'), 'branches', type_='foreignkey')
op.drop_constraint(op.f('branches_organization_id_fkey'), 'branches', type_='foreignkey')
op.create_foreign_key(None, 'branches', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'branches', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', type_='foreignkey')
op.create_foreign_key(None, 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', type_='foreignkey')
op.create_foreign_key(None, 'financial_ledger', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['related_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.drop_constraint(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.create_foreign_key(None, 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('operational_logs_user_id_fkey'), 'operational_logs', type_='foreignkey')
op.create_foreign_key(None, 'operational_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='SET NULL')
op.drop_constraint(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.drop_constraint(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.create_foreign_key(None, 'org_sales_assignments', 'users', ['agent_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_sales_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('organization_financials_organization_id_fkey'), 'organization_financials', type_='foreignkey')
op.create_foreign_key(None, 'organization_financials', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organization_members_person_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
op.create_foreign_key(None, 'organization_members', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('organizations', sa.Column('is_anonymized', sa.Boolean(), server_default=sa.text('false'), nullable=True))
op.add_column('organizations', sa.Column('anonymized_at', sa.DateTime(timezone=True), nullable=True))
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['target_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['actor_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', type_='foreignkey')
op.drop_constraint(op.f('service_expertises_service_id_fkey'), 'service_expertises', type_='foreignkey')
op.create_foreign_key(None, 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_expertises', 'service_profiles', ['service_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('service_profiles', sa.Column('parent_id', sa.Integer(), nullable=True))
op.add_column('service_profiles', sa.Column('fingerprint', sa.String(length=255), nullable=True))
op.add_column('service_profiles', sa.Column('vibe_analysis', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), nullable=True))
op.add_column('service_profiles', sa.Column('social_links', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), nullable=True))
op.add_column('service_profiles', sa.Column('contact_email', sa.String(), nullable=True))
op.add_column('service_profiles', sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
op.add_column('service_profiles', sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True))
op.execute("UPDATE data.service_profiles SET fingerprint = 'legacy_' || id::text")
op.alter_column('service_profiles', 'fingerprint', nullable=False)
op.alter_column('service_profiles', 'verification_log',
existing_type=postgresql.JSON(astext_type=sa.Text()),
type_=postgresql.JSONB(astext_type=sa.Text()),
existing_nullable=True,
existing_server_default=sa.text("'{}'::jsonb"))
op.alter_column('service_profiles', 'opening_hours',
existing_type=postgresql.JSON(astext_type=sa.Text()),
type_=postgresql.JSONB(astext_type=sa.Text()),
existing_nullable=True,
existing_server_default=sa.text("'{}'::jsonb"))
op.create_index('idx_service_fingerprint', 'service_profiles', ['fingerprint'], unique=True, schema='data')
op.create_index(op.f('ix_data_service_profiles_fingerprint'), 'service_profiles', ['fingerprint'], unique=False, schema='data')
op.drop_constraint(op.f('service_profiles_organization_id_fkey'), 'service_profiles', type_='foreignkey')
op.create_foreign_key(None, 'service_profiles', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_profiles', 'service_profiles', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('service_staging', sa.Column('fingerprint', sa.String(length=255), nullable=True), schema='data')
op.execute("UPDATE data.service_staging SET fingerprint = 'staging_' || id::text")
op.alter_column('service_staging', 'fingerprint', nullable=False, schema='data')
op.create_index('idx_staging_fingerprint', 'service_staging', ['fingerprint'], unique=True, schema='data')
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('users_referred_by_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_current_sales_agent_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'users', ['referred_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['current_sales_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.drop_constraint(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'])
op.create_foreign_key(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'vehicle_catalog', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'])
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('users_current_sales_agent_id_fkey'), 'users', 'users', ['current_sales_agent_id'], ['id'])
op.create_foreign_key(op.f('users_referred_by_id_fkey'), 'users', 'users', ['referred_by_id'], ['id'])
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_index('idx_staging_fingerprint', table_name='service_staging', schema='data')
op.drop_column('service_staging', 'fingerprint')
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_profiles_organization_id_fkey'), 'service_profiles', 'organizations', ['organization_id'], ['id'])
op.drop_index(op.f('ix_data_service_profiles_fingerprint'), table_name='service_profiles', schema='data')
op.drop_index('idx_service_fingerprint', table_name='service_profiles', schema='data')
op.alter_column('service_profiles', 'opening_hours',
existing_type=postgresql.JSONB(astext_type=sa.Text()),
type_=postgresql.JSON(astext_type=sa.Text()),
existing_nullable=True,
existing_server_default=sa.text("'{}'::jsonb"))
op.alter_column('service_profiles', 'verification_log',
existing_type=postgresql.JSONB(astext_type=sa.Text()),
type_=postgresql.JSON(astext_type=sa.Text()),
existing_nullable=True,
existing_server_default=sa.text("'{}'::jsonb"))
op.drop_column('service_profiles', 'updated_at')
op.drop_column('service_profiles', 'created_at')
op.drop_column('service_profiles', 'contact_email')
op.drop_column('service_profiles', 'social_links')
op.drop_column('service_profiles', 'vibe_analysis')
op.drop_column('service_profiles', 'fingerprint')
op.drop_column('service_profiles', 'parent_id')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_expertises_service_id_fkey'), 'service_expertises', 'service_profiles', ['service_id'], ['id'])
op.create_foreign_key(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'])
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', 'users', ['actor_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', 'users', ['target_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'])
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
existing_nullable=True)
op.drop_column('organizations', 'anonymized_at')
op.drop_column('organizations', 'is_anonymized')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('organization_members_person_id_fkey'), 'organization_members', 'persons', ['person_id'], ['id'])
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
existing_nullable=True)
op.drop_constraint(None, 'organization_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_financials_organization_id_fkey'), 'organization_financials', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', 'users', ['agent_user_id'], ['id'])
op.drop_constraint(None, 'operational_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('operational_logs_user_id_fkey'), 'operational_logs', 'users', ['user_id'], ['id'], ondelete='SET NULL')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'])
op.create_foreign_key(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'])
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', 'users', ['related_agent_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'feature_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('branches_organization_id_fkey'), 'branches', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('branches_address_id_fkey'), 'branches', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', 'branches', ['branch_id'], ['id'])
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_index('idx_rating_user', table_name='ratings', schema='data')
op.drop_index('idx_rating_org', table_name='ratings', schema='data')
op.drop_index('idx_rating_branch', table_name='ratings', schema='data')
op.drop_table('ratings', schema='data')
# ### end Alembic commands ###

View File

@@ -0,0 +1,344 @@
"""add_ownership_twin_and_gdpr_uuid
Revision ID: dd910cabe24e
Revises: 54cbd5c9e003
Create Date: 2026-02-21 07:57:20.406746
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = 'dd910cabe24e'
down_revision: Union[str, Sequence[str], None] = '54cbd5c9e003'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', type_='foreignkey')
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'branches', ['branch_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('asset_costs', sa.Column('registration_uuid', sa.UUID(), nullable=True))
op.create_index(op.f('ix_data_asset_costs_registration_uuid'), 'asset_costs', ['registration_uuid'], unique=False, schema='data')
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('asset_events', sa.Column('registration_uuid', sa.UUID(), nullable=True))
op.create_index(op.f('ix_data_asset_events_registration_uuid'), 'asset_events', ['registration_uuid'], unique=False, schema='data')
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
op.add_column('assets', sa.Column('registration_uuid', sa.UUID(), nullable=False))
op.add_column('assets', sa.Column('is_corporate', sa.Boolean(), server_default=sa.text('false'), nullable=True))
op.add_column('assets', sa.Column('owner_person_id', sa.BigInteger(), nullable=True))
op.add_column('assets', sa.Column('owner_org_id', sa.Integer(), nullable=True))
op.add_column('assets', sa.Column('operator_person_id', sa.BigInteger(), nullable=True))
op.add_column('assets', sa.Column('operator_org_id', sa.Integer(), nullable=True))
op.create_index(op.f('ix_data_assets_registration_uuid'), 'assets', ['registration_uuid'], unique=False, schema='data')
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
op.create_foreign_key(None, 'assets', 'organizations', ['operator_org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['owner_org_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'persons', ['owner_person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'persons', ['operator_person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('branches_address_id_fkey'), 'branches', type_='foreignkey')
op.drop_constraint(op.f('branches_organization_id_fkey'), 'branches', type_='foreignkey')
op.create_foreign_key(None, 'branches', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'branches', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', type_='foreignkey')
op.create_foreign_key(None, 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', type_='foreignkey')
op.drop_constraint(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', type_='foreignkey')
op.create_foreign_key(None, 'financial_ledger', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['related_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'financial_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.drop_constraint(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', type_='foreignkey')
op.create_foreign_key(None, 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('operational_logs_user_id_fkey'), 'operational_logs', type_='foreignkey')
op.create_foreign_key(None, 'operational_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='SET NULL')
op.drop_constraint(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.drop_constraint(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', type_='foreignkey')
op.create_foreign_key(None, 'org_sales_assignments', 'users', ['agent_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_sales_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('organization_financials_organization_id_fkey'), 'organization_financials', type_='foreignkey')
op.create_foreign_key(None, 'organization_financials', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_person_id_fkey'), 'organization_members', type_='foreignkey')
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organization_members', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
existing_nullable=True)
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_organization_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_branch_id_fkey'), 'ratings', type_='foreignkey')
op.drop_constraint(op.f('ratings_target_user_id_fkey'), 'ratings', type_='foreignkey')
op.create_foreign_key(None, 'ratings', 'users', ['target_user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'organizations', ['target_organization_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'ratings', 'branches', ['target_branch_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.drop_constraint(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', type_='foreignkey')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['actor_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['target_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', type_='foreignkey')
op.drop_constraint(op.f('service_expertises_service_id_fkey'), 'service_expertises', type_='foreignkey')
op.create_foreign_key(None, 'service_expertises', 'service_profiles', ['service_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_profiles_organization_id_fkey'), 'service_profiles', type_='foreignkey')
op.drop_constraint(op.f('service_profiles_parent_id_fkey'), 'service_profiles', type_='foreignkey')
op.create_foreign_key(None, 'service_profiles', 'service_profiles', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'service_profiles', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('users_referred_by_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_current_sales_agent_id_fkey'), 'users', type_='foreignkey')
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
op.create_foreign_key(None, 'users', 'users', ['referred_by_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'users', 'users', ['current_sales_agent_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.drop_constraint(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'vehicle_model_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_model_definitions_parent_id_fkey'), 'vehicle_model_definitions', 'vehicle_model_definitions', ['parent_id'], ['id'])
op.create_foreign_key(op.f('vehicle_model_definitions_vehicle_type_id_fkey'), 'vehicle_model_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'vehicle_catalog', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('vehicle_catalog_master_definition_id_fkey'), 'vehicle_catalog', 'vehicle_model_definitions', ['master_definition_id'], ['id'])
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('users_current_sales_agent_id_fkey'), 'users', 'users', ['current_sales_agent_id'], ['id'])
op.create_foreign_key(op.f('users_referred_by_id_fkey'), 'users', 'users', ['referred_by_id'], ['id'])
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_profiles', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_profiles_parent_id_fkey'), 'service_profiles', 'service_profiles', ['parent_id'], ['id'])
op.create_foreign_key(op.f('service_profiles_organization_id_fkey'), 'service_profiles', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.drop_constraint(None, 'service_expertises', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('service_expertises_service_id_fkey'), 'service_expertises', 'service_profiles', ['service_id'], ['id'])
op.create_foreign_key(op.f('service_expertises_expertise_id_fkey'), 'service_expertises', 'expertise_tags', ['expertise_id'], ['id'])
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'security_audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('security_audit_logs_target_id_fkey'), 'security_audit_logs', 'users', ['target_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_confirmed_by_id_fkey'), 'security_audit_logs', 'users', ['confirmed_by_id'], ['id'])
op.create_foreign_key(op.f('security_audit_logs_actor_id_fkey'), 'security_audit_logs', 'users', ['actor_id'], ['id'])
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('ratings_target_user_id_fkey'), 'ratings', 'users', ['target_user_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_branch_id_fkey'), 'ratings', 'branches', ['target_branch_id'], ['id'])
op.create_foreign_key(op.f('ratings_target_organization_id_fkey'), 'ratings', 'organizations', ['target_organization_id'], ['id'])
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
op.alter_column('organizations', 'org_type',
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
existing_nullable=True)
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('organization_members_person_id_fkey'), 'organization_members', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
op.alter_column('organization_members', 'role',
existing_type=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole', schema='data', inherit_schema=True),
type_=postgresql.ENUM('OWNER', 'ADMIN', 'FLEET_MANAGER', 'DRIVER', 'MECHANIC', 'RECEPTIONIST', name='orguserrole'),
existing_nullable=True)
op.drop_constraint(None, 'organization_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('organization_financials_organization_id_fkey'), 'organization_financials', 'organizations', ['organization_id'], ['id'])
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'org_sales_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('org_sales_assignments_organization_id_fkey'), 'org_sales_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('org_sales_assignments_agent_user_id_fkey'), 'org_sales_assignments', 'users', ['agent_user_id'], ['id'])
op.drop_constraint(None, 'operational_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('operational_logs_user_id_fkey'), 'operational_logs', 'users', ['user_id'], ['id'], ondelete='SET NULL')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.drop_constraint(None, 'model_feature_maps', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('model_feature_maps_feature_id_fkey'), 'model_feature_maps', 'feature_definitions', ['feature_id'], ['id'])
op.create_foreign_key(op.f('model_feature_maps_model_id_fkey'), 'model_feature_maps', 'vehicle_model_definitions', ['model_id'], ['id'])
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.drop_constraint(None, 'financial_ledger', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('financial_ledger_user_id_fkey'), 'financial_ledger', 'users', ['user_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_person_id_fkey'), 'financial_ledger', 'persons', ['person_id'], ['id'])
op.create_foreign_key(op.f('financial_ledger_related_agent_id_fkey'), 'financial_ledger', 'users', ['related_agent_id'], ['id'])
op.drop_constraint(None, 'feature_definitions', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('feature_definitions_vehicle_type_id_fkey'), 'feature_definitions', 'vehicle_types', ['vehicle_type_id'], ['id'])
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.drop_constraint(None, 'branches', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('branches_organization_id_fkey'), 'branches', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('branches_address_id_fkey'), 'branches', 'addresses', ['address_id'], ['id'])
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
op.drop_index(op.f('ix_data_assets_registration_uuid'), table_name='assets', schema='data')
op.drop_column('assets', 'operator_org_id')
op.drop_column('assets', 'operator_person_id')
op.drop_column('assets', 'owner_org_id')
op.drop_column('assets', 'owner_person_id')
op.drop_column('assets', 'is_corporate')
op.drop_column('assets', 'registration_uuid')
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
op.drop_index(op.f('ix_data_asset_events_registration_uuid'), table_name='asset_events', schema='data')
op.drop_column('asset_events', 'registration_uuid')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
op.drop_index(op.f('ix_data_asset_costs_registration_uuid'), table_name='asset_costs', schema='data')
op.drop_column('asset_costs', 'registration_uuid')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_branch_id_fkey'), 'asset_assignments', 'branches', ['branch_id'], ['id'])
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
# ### end Alembic commands ###

View File

@@ -26,3 +26,8 @@ cryptography
GeoAlchemy2>=0.14.0
google-generativeai
google-genai
rapidfuzz
duckduckgo-search>=6.0.0
Shapely>=2.0.0
opencv-python-headless==4.9.0.80
numpy<2.0.0

View File

@@ -5,14 +5,11 @@ services:
context: ./backend
dockerfile: Dockerfile
container_name: service_finder_migrate
# Az env_file használatával a Docker automatikusan beemeli a változókat a konténerbe
# Nincs szükség a ${VARIABLE} formátumra az environment alatt, így elkerüljük a "blank string" hibát
env_file: .env
volumes:
- ./backend:/app
environment:
- PYTHONPATH=/app
# Kényszerítjük az Alembic-et, hogy a .env-ben megadott MIGRATION_DATABASE_URL-t használja
command: >
bash -c "alembic upgrade head"
networks:
@@ -69,7 +66,7 @@ services:
- default
restart: unless-stopped
# 5. FRONTEND (Vue/React)
# 5. FRONTEND
service_frontend:
build:
context: ./frontend
@@ -87,8 +84,9 @@ services:
# 6. KATALÓGUS ROBOT (Discovery)
catalog_robot:
build: ./backend
container_name: service_finder_robot_catalog
command: python -u -m app.workers.catalog_robot
deploy:
replicas: 1
volumes:
- ./backend:/app
env_file: .env
@@ -124,7 +122,6 @@ services:
ports:
- "5678:5678"
env_file: .env
# Itt az n8n belső változóit hagyjuk, de a jelszót az env_file-ból fogja venni
volumes:
- ./n8n/data:/home/node/.n8n
networks:
@@ -143,7 +140,7 @@ services:
networks:
- default
# 9. BROWSERLESS (Chrome for Scraping)
# 9. BROWSERLESS
browserless:
image: browserless/chrome:latest
container_name: service_finder_browserless
@@ -153,11 +150,13 @@ services:
networks:
- default
# 10. TECHNIKAI ADATOK DÚSÍTÓ ROBOT (AI & RDW)
enricher_robot:
# 10. ROBOT 2.1 - RESEARCHER (Porszívó - Hálózati kutató)
# Mivel I/O bound (netre vár), futtathatjuk több példányban (pl. 3 szálon)
robot_researcher:
build: ./backend
container_name: service_finder_robot_enricher
command: python -u -m app.workers.technical_enricher
command: python -u -m app.workers.researcher_v2_1
deploy:
replicas: 3
volumes:
- ./backend:/app
env_file: .env
@@ -169,6 +168,70 @@ services:
- shared_db_net
restart: always
# 11. ROBOT 2.2 - ALCHEMIST (Vegyész - GPU AI dúsító)
# Ez használja a GPU-t, ebből általában 1 példány elég a VRAM miatt
robot_alchemist:
build: ./backend
command: python -u -m app.workers.alchemist_v2_2
deploy:
replicas: 1
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
volumes:
- ./backend:/app
env_file: .env
depends_on:
migrate:
condition: service_completed_successfully
ollama:
condition: service_started
networks:
- default
- shared_db_net
restart: always
# 12. AI a szerveren :)
ollama:
image: ollama/ollama:latest
container_name: service_finder_ollama
restart: always
volumes:
- ./ollama_data:/root/.ollama
ports:
- "11434:11434"
environment:
- OLLAMA_KEEP_ALIVE=24h
- OLLAMA_ORIGINS="*"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
networks:
- default
- shared_db_net
# 13. VIN AUDITOR
vin_auditor:
build: ./backend
container_name: service_finder_vin_auditor
command: python -u -m app.workers.vin_auditor
restart: always
env_file: .env
depends_on:
ollama:
condition: service_started
networks:
- default
- shared_db_net
networks:
default:
driver: bridge

View File

@@ -0,0 +1,222 @@
Íme a hiányzó láncszemek, amiket ki kell dolgoznunk:
🔗 1. Ajánlatkérés és Időpontfoglalás (Booking & Quoting Flow)
A V01-ben: A 01-es dokumentum említi: "Marketplace: Szervizkereső, Ajánlatkérés, Időpontfoglalás."
A Hiány: Az adatbázisban van service_profiles, branches, sőt pending_actions is, de nincs dedikált tábla/logika a foglalásokra és árajánlatokra.
Amit meg kell írni: Hogyan kér a User ajánlatot? (Kiválasztja az autóját a garázsból -> leírja a hibát -> a rendszer kiküldi a 10 km-en belüli megfelelő expertise_tags-el rendelkező szervizeknek -> a szerviz ajánlatot ad -> User elfogadja -> bekerül az asset_events-be, mint jövőbeli esemény).
🔗 2. Költség-taxonómia és "Munkába járás" (TCO & Accounting)
A V01-ben: TCO (Total Cost of Ownership) számítás szerepel a PREMIUM csomagnál. Említetted a munkába járás elszámolását is.
A Hiány: A data.asset_costs tábla létezik, de nincs definiálva a szigorú Költségkategória fa (üzemanyag, javítás, autópálya-matrica, biztosítás, értékcsökkenés).
Amit meg kell írni: Fix kategória ID-k definiálása a frontend drop-down menüjéhez, illetve a Munkába járás modul logikája (Otthon-Munkahely távolság x Munkanapok száma x Állami/NAV norma = Havi adómentes térítés kalkulációja a B2B flottáknak).
🔗 3. Az "Evidence Store" és az OCR Robot (Robot 3) Folyamata
A V01-ben: A 22-es fájl említi a Robot 3-at (OCR & Document AI), ami feldolgozza a számlákat.
A Hiány: Nincs technikai leírás arról, hogyan lesz a MinIO-ba feltöltött fotóból hitelesített szervizbejegyzés.
Amit meg kell írni: A fotó feltöltésre kerül a documents táblába -> A Robot 3 (Gemini 2.0 Multimodal) kiolvassa a dátumot, összeget, km-órát és szerviz nevét -> Létrehoz egy asset_events rekordot unverified státusszal -> Összeköti a számlán lévő szervizt a service_profiles táblával -> Megadja a Gamification pontokat a Usernek.
🔗 4. B2B Flotta Szerepkörök (Organization Members Logika)
A V01-ben: A "Mindenki flottatulajdonos" elv megvan (Privát vs Céges flotta).
A Hiány: A data.organization_members táblád létezik, de nincsenek definiálva a Flottán belüli szerepkörök.
Amit meg kell írni: Ki mit láthat egy cégen belül?
Fleet Manager (Admin): Látja az összes autót, minden költséget, ő veszi a Krediteket.
Driver (Sofőr): Csak a rá szignált (asset_assignments) autót látja, tud tankolást (költséget) rögzíteni, de a cég egyenlegéhez nem fér hozzá.
🔗 5. Telemetria és Futásteljesítmény (Odometer Tracking)
A V01-ben: A data.asset_telemetry tábla bent van az adatbázisban, és a Prediktív naptárhoz (Robot 2.3) kell a km-óra állás.
A Hiány: Honnan jön a km-óra állás, ha nincs OBD2 hardver bedugva az autóba?
Amit meg kell írni: Az "Okos becslés" (Smart Odometer) algoritmusa. Minden tankolásnál (asset_costs) és szerviznél (asset_events) kötelező megadni a km-állást. A rendszer ebből számol egy napi átlagos futást, és ha hetekig nincs új adat, a háttérben "pörgeti" az órát, hogy a Robot 2.3 tudja, mikor kell szólni az olajcsere miatt.
🚀 FEJLESZTÉSI IRÁNYTŰ ÉS ÜZLETI MODELL (V2.0)
Ez a dokumentum rögzíti, hogy mit építünk meg azonnal az induláshoz (MVP), és mit hagyunk a skálázódási fázisra.
I. FÁZIS: AZ INDULÓ CSOMAG (MVP - Minimum Viable Product)
Ezek kellenek ahhoz, hogy a rendszer elinduljon, pénzt termeljen, és napi szinten használják a magánszemélyek és a KKV-k.
1. Onboarding és Garázs (Asset Creation)
Az első benyomás a legfontosabb. A felhasználó itt adja hozzá a járművét és az adatait.
FREE (Ingyenes): * Maximum 1 jármű rögzítése.
Adatok manuális bevitele (Rendszám / VIN alapján keresés a katalógusban).
Okmányok lejárati dátumának manuális rögzítése.
PREMIUM:
Több jármű (pl. 3-5 db) rögzítése.
Robot 3 "Magic Scan": A forgalmi engedély és a jogosítvány lefotózása -> az AI mindent automatikusan kitölt.
Dokumentumok titkosított tárolása a MinIO "Privát Széfben" (digitalizált irattárca).
2. Smart Trip Logger & Munkába járás (A napi horog)
Ez adja a napi használatot (DAU) és a KKV-k számára a fő értéket.
FREE: * Kézi Start/Stop gomb az utakhoz (GPS koordináták mentése).
Alapvető havi statisztika (megtett út km-ben).
PREMIUM:
Automata Munkába Járás Riport: A megadott Otthon-Munkahely cím alapján a ledolgozott napokból a hónap végén NAV-kompatibilis, adómentes költségtérítési PDF generálása.
Adaptív GPS útvonalrögzítés (ahogy korábban átbeszéltük).
Költségek (TCO) és tankolások kézi rögzítésének analitikája.
3. Guardian & Gamification (Őrangyal és Játékosítás)
A bizalomépítés és a proaktív segítség.
FREE: * Értesítés az okmányok (Jogsi, Forgalmi) lejártáról 30 nappal előtte.
Alap "Clean History" Badge (ha mindent kitölt).
PREMIUM:
Okos Szerviz Naptár: A Robot 2.3 a napi átlag futásból kiszámolja (Predikció), mikor kell olajat cserélni, és időben szól.
Napi/Heti "Checklist" értesítések (Mérj guminyomást!) -> Sikeres elvégzés esetén XP és Kredit jutalom (Gamification).
⚙️ A PÉNZÜGYI MOTOR (The Economy Engine) - MVP Követelmény
Ahogy említetted, az elv megvan (Triple Wallet), de a motort meg kell írni. Ennek a Backendben egy atombiztos, önálló modulként kell futnia (app/services/billing_engine.py).
A Pénzügyi Motor 3 fő komponense:
A Stripe Webhook Receiver: * Amikor a User kifizeti a csomagot bankkártyával, a Stripe küld egy jelet a Backendnek (checkout.session.completed).
A motor ekkor felébred.
Az Atomi Tranzakció (Double-Entry Ledger):
A motor nem csak átírja a Wallet egyenlegét, hanem először beír egy sort a data.financial_ledger táblába (Főkönyv): "+5000 HUF befizetés, Stripe_ID_xyz".
Ezután hozzáadja a Krediteket a wallets.purchased_credits mezőhöz. A két lépés egy adatbázis-tranzakcióban (SQL COMMIT) fut: ha az egyik elszáll, a másik is visszagurul (Rollback). Nincs elveszett pénz.
A Subscription Cron-Job (Az Éjjeli Őr):
Egy ütemezett feladat minden éjjel 00:01-kor végignézi a users táblában a subscription_expires_at dátumokat.
Akinél lejárt a PREMIUM, annak a rendszer automatikusan leveszi a rangját FREE-re, kikapcsolja a PDF generálást, és zárolja a 2. és 3. autóját (Read-only módba teszi).
II. FÁZIS: POST-LAUNCH (A skálázódás és a Szerviz-Szimbiózis)
Ezek a funkciók elengedhetetlenek a vízióhoz, de az indulás napján nem kellenek. Akkor kezdjük el fejleszteni őket, amikor már van 1000+ aktív autósunk az adatokkal.
4. Marketplace: Ajánlatkérés és Szervizkereső
Funkció: A User a Garázsból egy gombnyomással elküldi a hibát (pl. "Fékcsere") a 20 km-es körzetben lévő megbízható szervizeknek.
PREMIUM előny: A Premium userek ajánlatkérései "VIP" jelzéssel, a lista elején jelennek meg a szervizeknek.
5. Service Pro Modul (A szerelők felülete)
Funkció: A szerelő beírja a rendszámot, és a Robot 2.2 által gyűjtött "Arany Adatok" (olajmennyiség, nyomatékok) megjelennek neki (Quick-Scan).
Kölcsönhatás: A szerelő nálunk rögzíti a munkalapot, ami azonnal bekerül az autó Hitelesített Digitális Szervizkönyvébe. Ezzel az autó "Trust Score"-ja (bizalmi indexe) az egekbe szökik.
6. Deep Tech: Telemetria és AI Diagnosztika
Funkció: OBDII csatlakozó integráció a valós km-óra állás és hibakódok (DTC) olvasásához.
Kísérleti: G-erő mérése telefonnal (Vezetési stílus analitika) és AI Audio Engine (indítási hangból önindító/akku hiba predikciója). Ezt külön "Add-on" csomagként lehet értékesíteni a Flottakezelőknek.
🚀 FEJLESZTÉSI IRÁNYTŰ ÉS ÜZLETI MODELL (V2.0)
Ez a dokumentum rögzíti, hogy mit építünk meg azonnal az induláshoz (MVP), és mit hagyunk a skálázódási fázisra.
I. FÁZIS: AZ INDULÓ CSOMAG (MVP - Minimum Viable Product)
Ezek kellenek ahhoz, hogy a rendszer elinduljon, pénzt termeljen, és napi szinten használják a magánszemélyek és a KKV-k.
1. Onboarding és Garázs (Asset Creation)
Az első benyomás a legfontosabb. A felhasználó itt adja hozzá a járművét és az adatait.
FREE (Ingyenes): * Maximum 1 jármű rögzítése.
Adatok manuális bevitele (Rendszám / VIN alapján keresés a katalógusban).
Okmányok lejárati dátumának manuális rögzítése.
PREMIUM:
Több jármű (pl. 3-5 db) rögzítése.
Robot 3 "Magic Scan": A forgalmi engedély és a jogosítvány lefotózása -> az AI mindent automatikusan kitölt.
Dokumentumok titkosított tárolása a MinIO "Privát Széfben" (digitalizált irattárca).
2. Smart Trip Logger & Munkába járás (A napi horog)
Ez adja a napi használatot (DAU) és a KKV-k számára a fő értéket.
FREE: * Kézi Start/Stop gomb az utakhoz (GPS koordináták mentése).
Alapvető havi statisztika (megtett út km-ben).
PREMIUM:
Automata Munkába Járás Riport: A megadott Otthon-Munkahely cím alapján a ledolgozott napokból a hónap végén NAV-kompatibilis, adómentes költségtérítési PDF generálása.
Adaptív GPS útvonalrögzítés (ahogy korábban átbeszéltük).
Költségek (TCO) és tankolások kézi rögzítésének analitikája.
3. Guardian & Gamification (Őrangyal és Játékosítás)
A bizalomépítés és a proaktív segítség.
FREE: * Értesítés az okmányok (Jogsi, Forgalmi) lejártáról 30 nappal előtte.
Alap "Clean History" Badge (ha mindent kitölt).
PREMIUM:
Okos Szerviz Naptár: A Robot 2.3 a napi átlag futásból kiszámolja (Predikció), mikor kell olajat cserélni, és időben szól.
Napi/Heti "Checklist" értesítések (Mérj guminyomást!) -> Sikeres elvégzés esetén XP és Kredit jutalom (Gamification).
⚙️ A PÉNZÜGYI MOTOR (The Economy Engine) - MVP Követelmény
Ahogy említetted, az elv megvan (Triple Wallet), de a motort meg kell írni. Ennek a Backendben egy atombiztos, önálló modulként kell futnia (app/services/billing_engine.py).
A Pénzügyi Motor 3 fő komponense:
A Stripe Webhook Receiver: * Amikor a User kifizeti a csomagot bankkártyával, a Stripe küld egy jelet a Backendnek (checkout.session.completed).
A motor ekkor felébred.
Az Atomi Tranzakció (Double-Entry Ledger):
A motor nem csak átírja a Wallet egyenlegét, hanem először beír egy sort a data.financial_ledger táblába (Főkönyv): "+5000 HUF befizetés, Stripe_ID_xyz".
Ezután hozzáadja a Krediteket a wallets.purchased_credits mezőhöz. A két lépés egy adatbázis-tranzakcióban (SQL COMMIT) fut: ha az egyik elszáll, a másik is visszagurul (Rollback). Nincs elveszett pénz.
A Subscription Cron-Job (Az Éjjeli Őr):
Egy ütemezett feladat minden éjjel 00:01-kor végignézi a users táblában a subscription_expires_at dátumokat.
Akinél lejárt a PREMIUM, annak a rendszer automatikusan leveszi a rangját FREE-re, kikapcsolja a PDF generálást, és zárolja a 2. és 3. autóját (Read-only módba teszi).
II. FÁZIS: POST-LAUNCH (A skálázódás és a Szerviz-Szimbiózis)
Ezek a funkciók elengedhetetlenek a vízióhoz, de az indulás napján nem kellenek. Akkor kezdjük el fejleszteni őket, amikor már van 1000+ aktív autósunk az adatokkal.
4. Marketplace: Ajánlatkérés és Szervizkereső
Funkció: A User a Garázsból egy gombnyomással elküldi a hibát (pl. "Fékcsere") a 20 km-es körzetben lévő megbízható szervizeknek.
PREMIUM előny: A Premium userek ajánlatkérései "VIP" jelzéssel, a lista elején jelennek meg a szervizeknek.

View File

@@ -0,0 +1,25 @@
# 24. TCO, Költség-Taxonómia & Telemetria (v2.0)
Ez a dokumentum a járművek üzemeltetési költségeinek (Total Cost of Ownership), a telemetriai adatoknak és a speciális elszámolásoknak (pl. munkába járás) a logikáját rögzíti.
## 24.1 Költség-Taxonómia (`data.asset_costs`)
A rendszer szigorú kategóriarendszert használ a kiadások követésére. A frontend számára ezek a kategóriák fix azonosítóval (ID) rendelkeznek:
1. **Üzemanyag & Töltés (Fuel & EV):** Tankolások, villámtöltések.
2. **Karbantartás (Maintenance):** Kötelező szervizek, alkatrészek, munkadíjak.
3. **Adók & Díjak (Taxes & Fees):** Gépjárműadó, autópálya-matrica, biztosítás (KGFB, CASCO).
4. **Bírságok & Parkolás (Legal & Parking):** Parkolási díjak, gyorshajtás, pótdíjak.
5. **Értékcsökkenés (Depreciation):** Rendszer által becsült, vagy manuálisan megadott értékvesztés (csak PREMIUM/VIP szinten).
## 24.2 "Munkába Járás" Modul (Commuting Allowance)
A rendszer automatikusan számolja a munkavállalók adómentes költségtérítését a magánjármű céges használata után.
- **Bemenet:** A `users` táblában rögzített Otthoni és Munkahelyi cím távolsága (Google Maps API / OSM alapján).
- **Triggelés:** A felhasználó a naptárban (vagy gombnyomással) kijelöli az adott hónapban ledolgozott munkanapokat.
- **Kalkuláció:** `Napi oda-vissza távolság (km) × Ledolgozott napok × Állami Norma (HUF/km)`.
- **Kimenet:** Hó végén egy automatikus, exportálható PDF riport a bérszámfejtésnek.
## 24.3 Smart Odometer (Okos Futásteljesítmény)
Mivel nincs kötelező OBD2 hardver, a rendszer a `data.asset_telemetry` táblában egy okos algoritmust használ a kilométeróra állás (ODO) követésére:
- **Horgonypontok:** Minden tankolás, szerviz vagy vizsga alkalmával kötelező/ajánlott megadni az aktuális km-állást.
- **Napi Átlag (Daily Average):** A rendszer kiszámolja a két horgonypont között megtett napi átlagos távolságot.
- **Predikció (Ghost Telemetry):** Ha 14 napig nincs új adat, a rendszer a Napi Átlag alapján "láthatatlanul pörgeti" az órát, hogy a Robot 2.3 (Guardian) időben tudja küldeni a szervizértesítéseket.

View File

@@ -0,0 +1,19 @@
# 25. Marketplace: Ajánlatkérés és Időpontfoglalás (v2.0)
Ez a modul írja le a Szervizkeresőből kiinduló tranzakciós folyamatot, amely összeköti a járműtulajdonost a szolgáltatókkal.
## 25.1 A Foglalási Folyamat (Booking Flow)
A kommunikáció aszinkron, és a `data.pending_actions` táblára épül.
1. **Igény (Service Request):** - A User kiválasztja az autóját a Garázsból (`asset_id`).
- Kiválasztja a probléma típusát (pl. "Fékcsere", "Éves szerviz") a `service_specialties` fa alapján.
- Csatolhat fotót vagy hangüzenetet (MinIO).
2. **Geofenced Broadcast:** - A rendszer megkeresi a User által megadott sugáron belül (pl. 20 km) lévő, a megfelelő `expertise_tags`-el rendelkező szervizeket (`branches`).
- A szervizek (Providers) push/email értesítést kapnak: "Új ajánlatkérés a közeledben".
3. **Ajánlatadás (Quoting):** - A Provider megad egy árat, egy időpontot és egy validálási lejárati időt.
4. **Elfogadás & Esemény (Acceptance):** - A User elfogadja az egyik ajánlatot.
- Létrejön egy jövőbeli `asset_events` bejegyzés `status='scheduled'` jelzéssel.
## 25.2 Trust és Lemondási Logika
- **No-Show védelem:** Ha a User nem jelenik meg, a szerviz "No-show" gombot nyomhat. Ez 1 Penalty Point-ot ad a Person rekordhoz (csökkenti a Trust Score-t).
- **Service Cancellation:** Ha a szerviz mondja le az utolsó pillanatban, a szerviz kap levonást az értékeléséből, a User pedig kompenzációs Kreditet kap.

View File

@@ -0,0 +1,18 @@
# 26. Evidence Store & Robot 3 (OCR AI) (v2.0)
A jármű történetének hitelesítése (Digitális Szervizkönyv) a bizonyítékokon (Evidence) alapul.
## 26.1 A Bizonyítékok Életciklusa
1. **Feltöltés:** A User lefotózza a számlát/munkalapot. A fájl titkosítva bekerül a MinIO objektumtárba, az adatbázisban pedig létrejön egy rekord a `data.documents` táblában.
2. **Feldolgozás (Robot 3):** A feltöltés egy eseményt (Webhook) indít az n8n felé, ami felébreszti a Robot 3-at (Gemini 2.0 Multimodal).
3. **Kinyerés (Extraction):** Az AI az alábbi adatokat bányássza ki a képből:
- `date`: A szerviz dátuma.
- `total_cost`: Bruttó végösszeg.
- `odometer`: Kilométeróra állása.
- `service_name / tax_number`: A szolgáltató adószáma vagy neve.
## 26.2 Validációs Háló (Trust Matching)
Miután a Robot 3 kinyerte az adatokat, a rendszer megpróbálja összekötni azokat a meglévő adatbázissal:
- **Partner Match:** Ha a kibányászott adószám/név szerepel a `data.organizations` táblában (Regisztrált Szerviz), a rendszer azonnal értesíti a szervizt. Ha a szerviz jóváhagyja ("Igen, nálunk járt"), a bejegyzés **High Trust (Verified)** státuszt kap.
- **Sufni / Non-Partner Match:** Ha a szerviz nincs a rendszerben, a bejegyzés **Medium Trust** státuszt kap (bizonyíték van, de nem partner).
- **Gamification Jutalmazás:** Ha a feltöltés sikeres adatkinyerést eredményezett, a User +10 XP-t (Earned Credits) kap az adatrögzítésért.

View File

@@ -0,0 +1,20 @@
# 27. B2B Flotta és Szervezeti Szerepkörök (v2.0)
A rendszerben a privát flotta és a több ezer autós céges flotta technológiailag azonos (`data.organizations`), a különbséget a `data.organization_members` táblában lévő jogosultsági szintek adják.
## 27.1 Céges Szerepkörök (Roles)
Egy szervezeten (Company) belül az alábbi három fő szerepkör értelmezett:
1. **Owner (Cégtulajdonos / CEO):**
- Mindenhez hozzáfér, ő kötheti össze a céget a Stripe (fizetési) fiókkal.
- Látja a cég teljes egyenlegét (`wallets`), vehet Krediteket és oszthat ki Fleet Manager jogokat.
2. **Fleet Manager (Flottakezelő):**
- Látja a céghez rendelt összes járművet (`asset_assignments`).
- Látja a telemetriát, a költségeket (`asset_costs`) és a jövőbeli karbantartásokat.
- Új autót vehet fel a flottába, de Krediteket (pénzt) alapértelmezetten nem vásárolhat.
3. **Driver (Sofőr):**
- Kizárólag azokat az autókat látja, amelyek kifejezetten hozzá vannak rendelve az `asset_assignments` táblában (`driver_id`).
- Rögzíthet tankolást, feltölthet számlát (Evidence), de nem látja a cég többi autóját, és nincs hozzáférése a cég Pénztárcájához.
## 27.2 Privát Szféra Izolációja
Ha egy felhasználó (User) Driver-ként van hozzárendelve egy céges autóhoz, az ő saját, személyes autói (Privát Széf) teljesen láthatatlanok maradnak a Fleet Manager és a cégtulajdonos számára. A céges és privát adatok szigorú falakkal vannak elválasztva az API szintjén.

View File

@@ -31,3 +31,63 @@ A robotok az adatbázist használják "jelzőtáblának", elkerülve az ütköz
2. **R2** dúsít és validál.
3. **R3** (OCR) bizonyítékokat csatol a jármű életútjához.
4. Minden művelet a `ProcessLog` táblában kerül rögzítésre az auditálhatóság érdekében.
2026.02.18 Frissített Robot 2.0 Dúsítés több példányban
# 🤖 TechEnricher Robot (v1.2.6) - Dokumentáció
## 1. Célkitűzés
A TechEnricher feladata a `data.vehicle_model_definitions` táblában található nyers, tisztítatlan járműadatok automatizált dúsítása technikai adatokkal (CCM, kW, Évjárat, Szervizintervallumok) és a duplikációk intelligens felszámolása.
## 2. Technikai Architektúra
- **Motor:** Python 3.10+ Asynchronous IO
- **AI Integráció:** Google Gemini 2.0 Flash
- **Adatbázis Logika:** SQLAlchemy 2.0 + PostgreSQL Row Level Locking
- **Deduplikációs Kulcs:** `make` + `technical_code` + `vehicle_type`
## 3. Kulcsfunkciók
### 🛡️ Manuális Védelem (Manual Override)
A robot soha nem írja felül a manuálisan rögzített adatokat. Ha a rekord `is_manual` mezője `true`, a folyamat érintetlenül hagyja azt.
### 🔗 Intelligens Összefűzés (Smart Merge)
Az egyediségi kényszerek (Unique Constraints) megsértése helyett a robot felismeri az ütközéseket:
- Ha a technikai kód alapján már létezik rekord, az új bejegyzést `duplicate` státuszba helyezi.
- Létrehozza a kapcsolatot a `parent_id` mezőn keresztül.
- A Master rekord `synonyms` mezőjét automatikusan bővíti az új elnevezéssel.
### 🛑 Anti-Loop és Hibakezelés
- **Attempts:** Minden rekord maximum 3 esélyt kap.
- **Last Error:** A hibás tranzakciók okát a robot elmenti a rekordhoz.
- **Circuit Breaker:** 10 egymást követő kritikus hiba esetén a robot 15 percre "alvó üzemmódba" vált az API-kvóta védelme érdekében.
## 4. Működési Sorrend (Logic Flow)
1. **Fetch:** 20 rekord lekérése `SKIP LOCKED` módban.
2. **Pre-Map:** Adatok kimentése memóriába (Greenlet védelem).
3. **Group:** Csoportosítás márkák/modellek szerint.
4. **Enrich:** AI vagy Belső Cache hívás.
5. **Validate:** Sanity check a technikai adatokra.
6. **Commit:** Tranzakció mentése (Master/Duplicate logika szerint).
## 5. Skálázás
A robot felkészített a horizontális skálázásra:
```bash
docker compose up -d --scale enricher_robot=3
🤖 22.0 Robot 0: The Strategist (Market Prioritizer)Cél: A feldolgozási sorrend optimalizálása a piaci relevancia alapján.Logika: Lekéri a külső források (pl. RDW) statisztikáit, és darabszám szerint rangsorolja a márkákat.Működés: 1. Kategóriákra bont (Személyautó > Motor > Teherautó > Egyéb).2. Feltölti/Frissíti a data.catalog_discovery táblát.3. Biztosítja, hogy a Robot 1 a legnépszerűbb modellekkel kezdjen.
🤖 22.1 Robot 1: The Hunter (Paginator v2.4)Cél: Strukturált jármű-variánsok tömeges importálása.Működés: - Recon: Megszámolja az adott márkához tartozó összes rekordot ($select=count(*)).Paging: 1000-es csomagokban, módszeresen végiglapozza a teljes adatbázist.Deduplikáció: Csak az egyedi technikai variánsokat (Márka + Modell + ccm + kW) menti el a data.vehicle_model_definitions táblába.
🤖 22.2 Robot 2.1: The Researcher (Industrial-v2.1)Cél: Technikai kontextus és szervizspecifikációk "porszívózása" az internetről.Működés:DuckDuckGo/Google Search segítségével adatokat gyűjt: olajmennyiség, viszkozitás, gumiabroncs nyomás, vezérlés csereperiódus, gyújtógyertya típus.A nyers szöveget a raw_search_context mezőben tárolja el a későbbi feldolgozáshoz.
🤖 22.3 Robot 2.2: The Alchemist (AI-Gold v2.2)Cél: A kutatott szövegek strukturált JSON-ná (Arannyá) alakítása.Működés:LLM (Ollama/Gemini) segítségével kinyeri a technikai adatokat a kutatási kontextusból.Factory Data Mapping: Betölti a data.vehicle_catalog táblába a végleges adatokat.Validation: Ha az adat ellentmondásos, manual_review státuszba helyezi a rekordot.
🤖 22.4 Robot 2.3: The Guardian (Predictive Notifier)Cél: Napi monitorozás és prediktív értesítések generálása (07:00-08:00 UTC).Logika: - Okmányfigyelő: Figyeli a data.assets és data.persons táblák lejárati dátumait (Műszaki, Biztosítás, Adó, Orvosi).Karbantartási Naptár: Összeveti az utolsó szerviz óta eltelt időt/kilométert a factory_data intervallumaival.Értesítési Mátrix: - 30 nappal / 1000 km-rel előbb: Emlékeztető (Lite).7 nappal / 200 km-rel előbb: Sürgető (Action Required).Lejárat napján: Kritikus riasztás.Kimenet: Bejegyzés a data.notification_queue táblába (Email/Push).
🛠 22.5 Adatminőség & Validációs RangsorA rendszer megkülönbözteti az adatok forrását, ami befolyásolja a jármű "Trust Score"-ját:ForrásHitelességi szintHatása az Asset-rePartner SzervizHigh (Verified)Digitális szervizkönyv pecsétet kap.User (Számlával)MediumFeltöltött dokumentumhoz kötött, admin jóváhagyásra várhat.User (Csak adat)LowTájékoztató jellegű, a jármű értékét nem növeli hivatalosan.
💰 22.6 Költségcsoportosítás & Elszámolás (TCO)A költségek a data.asset_costs táblában rögzülnek az alábbi csoportosításban:Üzemanyag: Tankolások, fogyasztásmérés.Karbantartás: Alkatrész, munkadíj.Adók & Díjak: Biztosítás, adó, matrica.Munkába járás: Automatikus kalkuláció a munkanapok és a rögzített távolság alapján (NAV/Helyi norma szerinti térítés).

View File

@@ -17,3 +17,81 @@ WHERE data.process_logs.start_time >= $1::TIMESTAMP WITH TIME ZONE
2026-02-17 07:00:02,309 INFO sqlalchemy.engine.Engine ROLLBACK
2026-02-18 07:00:02,000 INFO sqlalchemy.engine.Engine select pg_catalog.version()
2026-02-18 07:00:02,000 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-18 07:00:02,002 INFO sqlalchemy.engine.Engine select current_schema()
2026-02-18 07:00:02,002 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-18 07:00:02,004 INFO sqlalchemy.engine.Engine show standard_conforming_strings
2026-02-18 07:00:02,004 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-18 07:00:02,004 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-02-18 07:00:02,080 INFO sqlalchemy.engine.Engine SELECT data.process_logs.id, data.process_logs.process_name, data.process_logs.start_time, data.process_logs.end_time, data.process_logs.items_processed, data.process_logs.items_failed, data.process_logs.details, data.process_logs.created_at
FROM data.process_logs
WHERE data.process_logs.start_time >= $1::TIMESTAMP WITH TIME ZONE
2026-02-18 07:00:02,080 INFO sqlalchemy.engine.Engine [generated in 0.00013s] (datetime.datetime(2026, 2, 17, 7, 0, 1, 932625),)
📊 REGGELI ROBOT JELENTÉS - 2026-02-18
========================================
✅ Feldolgozott modellek: 147
❌ Hibás/Sikertelen: 3
🧹 AI névtisztítások száma: 0
2026-02-18 07:00:02,086 INFO sqlalchemy.engine.Engine ROLLBACK
2026-02-19 06:00:02,484 INFO sqlalchemy.engine.Engine select pg_catalog.version()
2026-02-19 06:00:02,485 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-19 06:00:02,487 INFO sqlalchemy.engine.Engine select current_schema()
2026-02-19 06:00:02,487 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-19 06:00:02,489 INFO sqlalchemy.engine.Engine show standard_conforming_strings
2026-02-19 06:00:02,489 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-19 06:00:02,490 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-02-19 06:00:02,568 INFO sqlalchemy.engine.Engine SELECT data.process_logs.id, data.process_logs.process_name, data.process_logs.start_time, data.process_logs.end_time, data.process_logs.items_processed, data.process_logs.items_failed, data.process_logs.details, data.process_logs.created_at
FROM data.process_logs
WHERE data.process_logs.start_time >= $1::TIMESTAMP WITH TIME ZONE
2026-02-19 06:00:02,568 INFO sqlalchemy.engine.Engine [generated in 0.00015s] (datetime.datetime(2026, 2, 18, 6, 0, 2, 414161),)
📊 REGGELI ROBOT JELENTÉS - 2026-02-19
========================================
✅ Feldolgozott modellek: 0
❌ Hibás/Sikertelen: 0
🧹 AI névtisztítások száma: 0
2026-02-19 06:00:02,573 INFO sqlalchemy.engine.Engine ROLLBACK
2026-02-20 06:00:02,375 INFO sqlalchemy.engine.Engine select pg_catalog.version()
2026-02-20 06:00:02,375 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-20 06:00:02,378 INFO sqlalchemy.engine.Engine select current_schema()
2026-02-20 06:00:02,378 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-20 06:00:02,379 INFO sqlalchemy.engine.Engine show standard_conforming_strings
2026-02-20 06:00:02,379 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-02-20 06:00:02,380 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-02-20 06:00:02,456 INFO sqlalchemy.engine.Engine SELECT data.process_logs.id, data.process_logs.process_name, data.process_logs.start_time, data.process_logs.end_time, data.process_logs.items_processed, data.process_logs.items_failed, data.process_logs.details, data.process_logs.created_at
FROM data.process_logs
WHERE data.process_logs.start_time >= $1::TIMESTAMP WITH TIME ZONE
2026-02-20 06:00:02,456 INFO sqlalchemy.engine.Engine [generated in 0.00013s] (datetime.datetime(2026, 2, 19, 6, 0, 2, 329534),)
📊 REGGELI ROBOT JELENTÉS - 2026-02-20
========================================
✅ Feldolgozott modellek: 0
❌ Hibás/Sikertelen: 0
🧹 AI névtisztítások száma: 0
2026-02-20 06:00:02,462 INFO sqlalchemy.engine.Engine ROLLBACK
📊 REGGELI ROBOT JELENTÉS - 2026-02-21
========================================
✅ Feldolgozott modellek: 0
❌ Hibás/Sikertelen: 0
🧹 AI névtisztítások száma: 0
📊 REGGELI ROBOT JELENTÉS - 2026-02-22
========================================
✅ Feldolgozott modellek: 0
❌ Hibás/Sikertelen: 0
🧹 AI névtisztítások száma: 0
📊 REGGELI ROBOT JELENTÉS - 2026-02-23
========================================
✅ Feldolgozott modellek: 0
❌ Hibás/Sikertelen: 0
🧹 AI névtisztítások száma: 0

File diff suppressed because it is too large Load Diff

View File

@@ -1,576 +0,0 @@
{"__type":"$$EventMessageAudit","id":"5869b732-18e3-4469-acf6-4c1494dd6a7d","ts":"2026-02-13T21:35:59.910+00:00","eventName":"n8n.audit.user.login.failed","message":"n8n.audit.user.login.failed","payload":{"authenticationMethod":"email","userEmail":"kincses@gmail.com","reason":"wrong credentials"}}
{"__type":"$$EventMessageAudit","id":"10c34e80-a103-4fa5-817e-ef652538bafc","ts":"2026-02-13T21:36:04.754+00:00","eventName":"n8n.audit.user.login.success","message":"n8n.audit.user.login.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","authenticationMethod":"email"}}
{"__type":"$$EventMessageAudit","id":"682f2d00-6de4-4ece-9e2c-a3e74f9d410e","ts":"2026-02-13T21:47:38.976+00:00","eventName":"n8n.audit.user.credentials.created","message":"n8n.audit.user.credentials.created","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","credentialType":"postgres","credentialId":"Jm8msQnZOaEu4FpX","publicApi":false,"projectId":"rhKVh1pVPxnQvrE5","projectType":"personal","isDynamic":false}}
{"__type":"$$EventMessageAudit","id":"237cc6fb-8ee2-4dec-8a09-c8262080b060","ts":"2026-02-13T21:51:41.979+00:00","eventName":"n8n.audit.workflow.created","message":"n8n.audit.workflow.created","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageWorkflow","id":"69950600-8d87-433f-b126-3ea85db37f0c","ts":"2026-02-13T21:51:56.862+00:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"1","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","errorMessage":"The workflow has issues and cannot be executed for that reason. Please fix them first."}}
{"__type":"$$EventMessageAudit","id":"76b34802-2059-4e6c-9fc7-c6f69baa640b","ts":"2026-02-13T21:52:27.681+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"40b56efa-00bb-4b4b-bf49-be3934ebfb03","ts":"2026-02-13T21:53:16.296+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"fdacb46e-04f8-4a1b-bf90-f7dbf8122dec","ts":"2026-02-13T21:55:27.292+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"d1259331-84cf-4cca-bb0a-d61f34f0fd99","ts":"2026-02-13T21:56:35.275+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"400e57e4-2a36-4b00-8177-492a96d1e082","ts":"2026-02-13T21:56:39.888+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"b172315d-d99d-41fd-87b6-02056c65b0f9","ts":"2026-02-13T21:57:34.897+00:00","eventName":"n8n.audit.user.execution.deleted","message":"n8n.audit.user.execution.deleted","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","executionIds":["1"]}}
{"__type":"$$EventMessageAudit","id":"5265e5d8-de1c-47ba-a4e6-68cd556aaa01","ts":"2026-02-13T22:01:53.807+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"7f524dde-69b8-4a1a-ad25-49211303c9f8","ts":"2026-02-13T22:04:04.860+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageWorkflow","id":"dd399605-345a-4a36-bcf0-1b291f9ef609","ts":"2026-02-13T22:04:19.549+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"2","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"adf99f02-914b-4f47-a149-b6e408fc1d63","ts":"2026-02-13T22:04:19.550+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"2","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"647915ce-3954-4792-9db9-abd9f7994262","ts":"2026-02-13T22:04:19.551+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"2","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"5225b2de-580f-413c-b5ce-806bff919a40"}}
{"__type":"$$EventMessageNode","id":"2d0dc74f-49a9-4f87-b47e-f76d4cd35fab","ts":"2026-02-13T22:04:19.611+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"2","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"5225b2de-580f-413c-b5ce-806bff919a40"}}
{"__type":"$$EventMessageWorkflow","id":"aa888c94-8756-4912-906e-83771dfb0e64","ts":"2026-02-13T22:04:19.613+00:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"2","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","lastNodeExecuted":"Execute a SQL query","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"column \"year\" does not exist"}}
{"__type":"$$EventMessageAudit","id":"1803a054-4c0c-46b6-9da1-78659d70d19d","ts":"2026-02-13T22:05:03.907+00:00","eventName":"n8n.audit.user.credentials.created","message":"n8n.audit.user.credentials.created","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","credentialType":"postgres","credentialId":"vhQriarepfU8BmSN","publicApi":false,"projectId":"rhKVh1pVPxnQvrE5","projectType":"personal","isDynamic":false}}
{"__type":"$$EventMessageAudit","id":"ba40a7e5-3565-4609-92e5-2710791e72a6","ts":"2026-02-13T22:05:05.558+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageWorkflow","id":"fea8cdd6-505e-48aa-9a21-e8e93aaaa235","ts":"2026-02-13T22:05:36.681+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"3","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"a8478977-819e-4b1c-b426-e59c347f4cdd","ts":"2026-02-13T22:05:36.681+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"3","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"f71bf08f-3bd1-45f5-820d-ed721d0ce111","ts":"2026-02-13T22:05:36.682+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"3","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"5225b2de-580f-413c-b5ce-806bff919a40"}}
{"__type":"$$EventMessageNode","id":"85bdf6e2-b33d-4f21-acaa-62f8fcd4a16d","ts":"2026-02-13T22:05:36.719+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"3","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"5225b2de-580f-413c-b5ce-806bff919a40"}}
{"__type":"$$EventMessageWorkflow","id":"685429af-bfa7-4b8b-879a-de33d29c3dec","ts":"2026-02-13T22:05:36.726+00:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"3","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","lastNodeExecuted":"Execute a SQL query","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"column \"year\" does not exist"}}
{"__type":"$$EventMessageAudit","id":"8c6835a0-225c-4d71-96bc-244a7c2df8d2","ts":"2026-02-13T22:08:33.943+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"8739b4f0-4373-4438-9c33-58d9faa7a803","ts":"2026-02-13T22:08:35.626+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"13a53f6e-0ec3-4dd4-979a-a41d1d7bbcf9","ts":"2026-02-13T22:08:37.860+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"ce0df8e7-540d-494a-9eb1-5796470cbff8","ts":"2026-02-13T22:08:40.520+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"6dfb2d6a-085e-429c-bbb7-dc815423e994","ts":"2026-02-13T22:09:22.257+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageWorkflow","id":"2da25985-e9d0-4b2a-af88-c9e49faad80a","ts":"2026-02-13T22:09:23.241+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"4","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"f2331d63-2908-4a3a-834f-b615a32d391d","ts":"2026-02-13T22:09:23.242+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"4","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"b93bc00e-8a55-4eb4-b26c-bb076f960679","ts":"2026-02-13T22:09:23.242+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"4","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"5225b2de-580f-413c-b5ce-806bff919a40"}}
{"__type":"$$EventMessageNode","id":"3b93061f-9621-42aa-8cc5-71c875b0dc5c","ts":"2026-02-13T22:09:23.286+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow","executionId":"4","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"5225b2de-580f-413c-b5ce-806bff919a40"}}
{"__type":"$$EventMessageWorkflow","id":"d69f349c-a3bc-43b2-8add-845add0996db","ts":"2026-02-13T22:09:23.291+00:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"4","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"6d9de1d8-646b-4207-8ed8-18af56277145","ts":"2026-02-13T22:09:53.151+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"44c367a3-b576-4163-9266-7a68d2d35f2f","ts":"2026-02-13T22:10:27.483+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"09974b64-103d-4992-87b7-9f3581461c2f","ts":"2026-02-13T22:12:51.234+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"b2dc631d-7884-411d-80ac-de5e22f39240","ts":"2026-02-13T22:13:12.838+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"88f413ba-26c5-44e3-b419-784e1e47fbb6","ts":"2026-02-13T22:13:14.455+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"43539d91-c6f8-452b-9d49-cae264329dc4","ts":"2026-02-13T22:13:19.305+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"14922d8c-9688-4dd7-90f6-965951e7e470","ts":"2026-02-13T22:13:22.671+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"486789f5-c179-4531-b6b7-3d7132b76118","ts":"2026-02-14T00:16:44.585+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"3ae5edfb-0dd6-41b2-8776-3a7c02e07f09","ts":"2026-02-14T00:16:46.997+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"My workflow"}}
{"__type":"$$EventMessageAudit","id":"7fe3f161-6a60-40a9-af1b-aa6c086d396d","ts":"2026-02-14T00:17:39.089+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"e219ce8f-4f9e-4e0f-b11a-2cec1471bc25","ts":"2026-02-14T00:17:53.433+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"5","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"ac670c4b-3c36-49c3-8c73-a6272e3acdd5","ts":"2026-02-14T00:17:53.433+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"5","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"7a79c6fc-8bf5-45c1-ac67-3ed24a3d6c20","ts":"2026-02-14T00:17:53.434+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"5","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"dad25375-152e-4f0d-85d7-ef41cdd69755","ts":"2026-02-14T00:17:53.435+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"5","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageWorkflow","id":"003a46cb-c61b-4459-8f97-f8ca5ea598f2","ts":"2026-02-14T00:17:53.437+00:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"5","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"beb95055-5c82-46de-a915-0ae09004cc02","ts":"2026-02-14T00:18:30.074+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d35ae2c0-2c1b-4918-b10d-4bf8752b7f37","ts":"2026-02-14T00:18:50.955+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"8d59a3f8-6779-415e-9cfc-ac487dc90368","ts":"2026-02-14T00:18:57.945+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d7b67993-2264-4778-99e8-719803312e09","ts":"2026-02-14T00:19:24.859+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"67f16bf6-1ec5-478d-b518-512c3d6f7149","ts":"2026-02-14T00:19:30.938+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"a81b92e3-d99e-47b1-a06d-bfed49e98358","ts":"2026-02-14T00:20:13.886+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"6","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"88687f84-89ee-4d7a-8d44-a1efaa899d5c","ts":"2026-02-14T00:20:13.886+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"6","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"c26ddf9e-caea-4c99-b3a8-75dcbc5fe316","ts":"2026-02-14T00:20:13.887+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"6","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"c537e933-add5-4c58-819a-343fa00b8e76"}}
{"__type":"$$EventMessageNode","id":"66f146b1-493a-4574-9bf6-866943b3eea1","ts":"2026-02-14T00:20:14.118+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"6","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"c537e933-add5-4c58-819a-343fa00b8e76"}}
{"__type":"$$EventMessageWorkflow","id":"86ab432e-d95e-42ef-9e92-c86a63527e7b","ts":"2026-02-14T00:20:14.121+00:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"6","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"2818ee3f-a8a7-4404-8115-bffada9eeaa7","ts":"2026-02-14T00:22:14.904+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"8a825a3e-f7d1-4dcd-b962-9ca87aa18706","ts":"2026-02-14T00:22:17.614+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"7c308a6d-7963-4df6-a8e6-9c192ccc7e34","ts":"2026-02-14T00:22:39.464+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"51c22d50-95c5-4316-8346-5645d930f92e","ts":"2026-02-14T00:22:47.538+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"7","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"9608637b-5102-4ff1-8ffe-ca556e54ec58","ts":"2026-02-14T00:22:47.538+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"7","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"cff555b6-be57-46eb-b8a1-3db5026642b9","ts":"2026-02-14T00:22:47.540+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"7","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"c537e933-add5-4c58-819a-343fa00b8e76"}}
{"__type":"$$EventMessageNode","id":"2d7b0261-390c-4fe7-ba03-dbd1ce819594","ts":"2026-02-14T00:22:47.755+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"7","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"c537e933-add5-4c58-819a-343fa00b8e76"}}
{"__type":"$$EventMessageWorkflow","id":"77123a2e-2a12-420d-835d-9e414f879079","ts":"2026-02-14T00:22:47.758+00:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"7","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"1e948f13-5ed3-4cf3-9e73-4e659cde44ab","ts":"2026-02-14T00:23:56.889+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"4e31ce6f-5461-4228-a192-b16dc0b21fd4","ts":"2026-02-14T00:24:09.435+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"006d72ef-613e-45e1-ac71-a1a240066e26","ts":"2026-02-14T00:24:11.935+00:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"8","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"bc2dd643-abef-4a28-9241-03aac0f9e859","ts":"2026-02-14T00:24:11.935+00:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"8","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"c80ab445-28d3-4285-918b-a8a90c0c8266","ts":"2026-02-14T00:24:11.936+00:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"8","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"c537e933-add5-4c58-819a-343fa00b8e76"}}
{"__type":"$$EventMessageNode","id":"7b73105d-73c5-4b06-9d31-557848248141","ts":"2026-02-14T00:24:15.023+00:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"8","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"c537e933-add5-4c58-819a-343fa00b8e76"}}
{"__type":"$$EventMessageWorkflow","id":"46d8edbf-66b9-4065-8dab-5f82a00cae3a","ts":"2026-02-14T00:24:15.027+00:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"8","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"74c8e7ce-4f92-4dcd-9393-42bcea6c35e3","ts":"2026-02-14T00:28:33.362+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"df713c02-39fb-45e5-85de-0467c580f067","ts":"2026-02-14T00:31:12.798+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"54378761-3509-40c1-8cc0-c830d56728f1","ts":"2026-02-14T00:33:32.477+00:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"7fb2c566-3c58-437c-a640-113eed8bf8a7","ts":"2026-02-13T19:33:58.052-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"94ccec9e-4693-450d-abd6-cbf24f07f89b","ts":"2026-02-13T19:34:20.246-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"3b30fa0d-f0ff-45cf-872c-331c0ce2fe16","ts":"2026-02-13T19:34:30.760-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"9da5d30d-2a3b-491f-b902-5edbf999ad47","ts":"2026-02-13T19:36:03.414-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"db2ff622-6ccc-469b-995a-d199be149bf0","ts":"2026-02-13T19:36:57.362-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"e9ff3ff7-11af-4844-8854-48bb7ae55ccb","ts":"2026-02-13T19:37:04.049-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"01a1dba0-7900-42d2-8181-5c9e753c5a33","ts":"2026-02-13T19:37:08.818-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"050dedbb-ea8a-4cbe-a7e3-399e4dc5f91c","ts":"2026-02-13T19:37:17.857-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"45c49c05-37b0-4227-b46c-28e993115cfa","ts":"2026-02-13T19:38:02.616-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d0c3cfcf-0f04-4e55-b272-64cc7dfb6b8b","ts":"2026-02-13T19:38:04.354-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"58d912dc-a934-41f7-b153-9a256b7e4842","ts":"2026-02-13T19:38:10.534-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"9","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"33182e6e-199a-49b7-a841-efff1539c011","ts":"2026-02-13T19:38:10.534-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"9","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"ea5bcd9a-36dd-45e5-b124-b55e2f7122c2","ts":"2026-02-13T19:38:10.535-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"9","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"58bcaa60-1a0b-4bb7-bb91-41e52c1fb040","ts":"2026-02-13T19:38:14.589-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"9","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageWorkflow","id":"d855e6c7-7127-4dac-a424-2b12c049ad93","ts":"2026-02-13T19:38:14.595-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"9","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"34de871c-4089-4fa4-a6d2-1bd68f9736ff","ts":"2026-02-13T19:38:28.657-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"a644706f-f774-4501-98bd-f6b7ad42fe71","ts":"2026-02-13T19:38:31.263-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"a083a619-dea4-4f6b-963e-8b4f5cbc7f0b","ts":"2026-02-13T19:38:33.192-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"86c9e98b-9aaa-411d-9ad0-384eb8554250","ts":"2026-02-13T19:38:37.848-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"10","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"a6594e64-cebe-4126-a33f-3ba73c68d8d6","ts":"2026-02-13T19:38:37.849-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"10","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"18d17b7c-1e1f-44c9-8554-00bd4b99d3bd","ts":"2026-02-13T19:38:37.850-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"10","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"e9c76713-db3d-4156-a9fe-4da59fa96d0e","ts":"2026-02-13T19:38:37.852-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"10","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageWorkflow","id":"844ea0d4-2499-4689-93ff-3385147873ff","ts":"2026-02-13T19:38:37.855-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"10","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"12f57168-371e-49e6-ac4e-8cbd6bf9460b","ts":"2026-02-13T19:38:45.332-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"11","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d7561c12-c58f-4a5c-a3b9-65e2a3c1887b","ts":"2026-02-13T19:38:45.332-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"11","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"836f27e5-c87c-40da-bdba-5a888c560da0","ts":"2026-02-13T19:38:45.333-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"11","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"87059e8e-7dc8-4c94-a991-3113305365cb","ts":"2026-02-13T19:38:51.605-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"11","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageWorkflow","id":"0df55d0f-adcb-436a-abb8-cfdc28c45674","ts":"2026-02-13T19:38:51.607-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"11","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","lastNodeExecuted":"OSM Dunakeszi Query","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Gateway timed out - perhaps try again later?"}}
{"__type":"$$EventMessageAudit","id":"ef075d35-4ef9-4da4-9f47-0a57192054ff","ts":"2026-02-13T19:39:13.597-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"b288bf7a-6f12-45b3-8689-205c83a06850","ts":"2026-02-13T19:39:17.039-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"01248cb6-09cd-4736-b83b-6422ef48f880","ts":"2026-02-13T19:39:25.065-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"b024d8e8-6e5e-449a-8ee9-e380b60be489","ts":"2026-02-13T19:39:37.847-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"12","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"fc8caf38-42b9-486c-82d7-827f853d557e","ts":"2026-02-13T19:39:37.848-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"12","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"486f29c2-2407-4c59-b779-c7d72f82bf16","ts":"2026-02-13T19:39:37.849-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"12","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"1dc749a7-af74-44c4-ac62-448459eee769","ts":"2026-02-13T19:39:42.667-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"12","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageWorkflow","id":"f0cfa9b9-81a2-4871-a20d-7ecb9b72fede","ts":"2026-02-13T19:39:42.670-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"12","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","lastNodeExecuted":"OSM Dunakeszi Query","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Gateway timed out - perhaps try again later?"}}
{"__type":"$$EventMessageAudit","id":"8d126d0c-9363-40c8-93bb-47c05112e696","ts":"2026-02-14T05:18:53.575-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"ce0140d9-1d69-40d0-9cac-45892bdb75e9","ts":"2026-02-14T05:18:56.263-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"658bc4db-ffa9-48fc-88d2-d970808e2635","ts":"2026-02-14T05:19:01.218-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"c78ea193-5a9f-43fe-8006-2a0009a6eb93","ts":"2026-02-14T05:30:23.101-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"13","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"db508d17-9098-48d2-8f2a-e5dd0ede374f","ts":"2026-02-14T05:30:23.102-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"af91453e-bec7-4a27-9cee-256205b04608","ts":"2026-02-14T05:30:23.102-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"0fda3878-4fa1-4401-9c1a-4498d5f960e8","ts":"2026-02-14T05:30:23.105-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"eec0e3c5-8bdf-4626-b069-eae598a22cdd","ts":"2026-02-14T05:30:23.106-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"4c35d592-8650-4a67-abd8-460ced5256b5","ts":"2026-02-14T05:30:23.845-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"8796b30f-1d1d-433f-a0fd-23365fddf1d7","ts":"2026-02-14T05:30:23.848-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","nodeType":"n8n-nodes-base.itemLists","nodeName":"Split into Services","nodeId":"b8a98965-296a-4959-91ba-a0202bce4b64"}}
{"__type":"$$EventMessageNode","id":"477aed86-ecf7-48b7-8e17-7dcd907d1f4e","ts":"2026-02-14T05:30:23.852-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"13","nodeType":"n8n-nodes-base.itemLists","nodeName":"Split into Services","nodeId":"b8a98965-296a-4959-91ba-a0202bce4b64"}}
{"__type":"$$EventMessageWorkflow","id":"c8efc08c-d46e-46da-a2df-e221520b34f8","ts":"2026-02-14T05:30:23.856-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"13","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"8488aec1-19c9-4d5b-b4dc-0797cc85f719","ts":"2026-02-14T06:13:53.488-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"3854fbcd-68dd-407f-932e-5b75a1baffce","ts":"2026-02-14T06:14:00.423-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"f8ec573e-869b-4fba-8ea0-779d3b42f80e","ts":"2026-02-14T06:14:02.394-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"14","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"bb6009f2-cc45-4159-bd92-522157b81cc3","ts":"2026-02-14T06:14:02.399-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"14","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"90455d15-42ad-4594-a06f-96df4b89720d","ts":"2026-02-14T06:14:02.400-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"14","nodeType":"n8n-nodes-base.wait","nodeName":"Wait","nodeId":"0e27eb6c-c5fc-47e0-9fe2-f930e52741ac"}}
{"__type":"$$EventMessageNode","id":"dadd9d3c-a956-4fb0-a884-8b68c6474439","ts":"2026-02-14T06:14:03.401-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"14","nodeType":"n8n-nodes-base.wait","nodeName":"Wait","nodeId":"0e27eb6c-c5fc-47e0-9fe2-f930e52741ac"}}
{"__type":"$$EventMessageWorkflow","id":"16c84270-377d-499a-b2af-db7c282550f9","ts":"2026-02-14T06:14:03.407-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"14","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"a480ee3f-57c1-4704-817b-c0b2fffb3c17","ts":"2026-02-14T06:15:15.549-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"15","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"f5bcaada-8993-4356-80ae-99764a7eec2c","ts":"2026-02-14T06:15:15.549-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"a219400c-0fa1-4a20-b0a0-1b1be372550a","ts":"2026-02-14T06:15:15.550-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"50b0f9a7-455d-46de-9685-8df8c89f95c2","ts":"2026-02-14T06:15:15.551-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"0bcfb070-9ed6-4804-bf66-046034315019","ts":"2026-02-14T06:15:15.551-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"b1ff2a72-6986-4b14-ae8d-133b9256357c","ts":"2026-02-14T06:15:19.614-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"253f0ba5-61d4-4834-8636-c9d52a6ab0d1","ts":"2026-02-14T06:15:19.616-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.itemLists","nodeName":"Split into Services","nodeId":"b8a98965-296a-4959-91ba-a0202bce4b64"}}
{"__type":"$$EventMessageNode","id":"cf951e04-6e87-4d5d-b4f0-6b1837ec04f7","ts":"2026-02-14T06:15:19.617-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.itemLists","nodeName":"Split into Services","nodeId":"b8a98965-296a-4959-91ba-a0202bce4b64"}}
{"__type":"$$EventMessageNode","id":"4420cdcc-81d9-432c-bada-8704160c4cbb","ts":"2026-02-14T06:15:19.621-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.wait","nodeName":"Wait","nodeId":"0e27eb6c-c5fc-47e0-9fe2-f930e52741ac"}}
{"__type":"$$EventMessageNode","id":"16a28303-31f8-42d7-8262-b1dbb16ae028","ts":"2026-02-14T06:15:20.621-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"15","nodeType":"n8n-nodes-base.wait","nodeName":"Wait","nodeId":"0e27eb6c-c5fc-47e0-9fe2-f930e52741ac"}}
{"__type":"$$EventMessageWorkflow","id":"399f7316-c22b-4e71-87a3-269d3c2c700c","ts":"2026-02-14T06:15:20.626-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"15","success":true,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"8507e5ee-07b9-4d4a-ba40-56b02d56d33a","ts":"2026-02-14T06:15:36.208-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"12a4fe9f-bcbd-4755-8076-c77806fc951a","ts":"2026-02-14T06:15:37.833-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"f32ae7db-f1bb-4fe3-9c0f-dc1ea908dd57","ts":"2026-02-14T06:15:40.295-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"690c0513-3259-43a3-b74e-d8abdecc4b4b","ts":"2026-02-14T06:15:44.034-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d5ff3782-1f8a-47f1-b6ea-2afd1e739feb","ts":"2026-02-14T06:24:28.191-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"31e099cc-d845-4735-b79e-6f5772c6154b","ts":"2026-02-14T06:24:59.089-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"46ffbb65-17e6-43c4-83fc-decff61f4517","ts":"2026-02-14T06:25:09.901-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"b4a4dff6-8ee0-4468-bdae-1a8a821be1b0","ts":"2026-02-14T06:25:20.226-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"7b951c4d-745d-49fe-b090-5f68ba5862cc","ts":"2026-02-14T06:25:27.959-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"72ccb333-8c36-4561-a59f-1ee04e856b53","ts":"2026-02-14T07:49:39.514-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"16","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","errorMessage":"The workflow has issues and cannot be executed for that reason. Please fix them first."}}
{"__type":"$$EventMessageAudit","id":"84bdec4d-233d-4428-a79d-cc2958dcfea3","ts":"2026-02-14T07:49:49.340-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"5ac09daf-9252-4710-93fe-3d8bcc553832","ts":"2026-02-14T07:49:53.046-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"feb81ad9-d0d6-43ea-912f-c523c51917e9","ts":"2026-02-14T07:52:20.840-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d1fdffa3-e2e2-4f95-b6b3-0f6b55577c4a","ts":"2026-02-14T07:52:25.776-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"6674e7ba-f972-41fa-af17-d18bb78c6f85","ts":"2026-02-14T07:52:29.308-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"17","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"e546b4e0-04f7-4b49-8514-b8634a579296","ts":"2026-02-14T07:52:29.308-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"350286e7-be70-4237-a625-ec602a529364","ts":"2026-02-14T07:52:29.309-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"6e15c770-05bd-4cea-b964-eb27cefbcfcf","ts":"2026-02-14T07:52:29.311-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"1ddbe06f-01d6-44df-a3ea-8feac5b34ce3"}}
{"__type":"$$EventMessageNode","id":"79bc8219-5d46-4d5a-a455-bd09bb769b30","ts":"2026-02-14T07:52:29.311-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"d6071e75-b4b3-4774-b8c2-e4a7eee77e26","ts":"2026-02-14T07:52:29.901-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.httpRequest","nodeName":"OSM Dunakeszi Query","nodeId":"c93e1c88-e915-4d19-882a-417415ecf495"}}
{"__type":"$$EventMessageNode","id":"6f335404-6d74-416e-9691-9d127d2cda55","ts":"2026-02-14T07:52:29.904-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.itemLists","nodeName":"Split into Services","nodeId":"b8a98965-296a-4959-91ba-a0202bce4b64"}}
{"__type":"$$EventMessageNode","id":"e80841e2-3fd7-497d-9be8-8fd17f190b58","ts":"2026-02-14T07:52:29.906-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.itemLists","nodeName":"Split into Services","nodeId":"b8a98965-296a-4959-91ba-a0202bce4b64"}}
{"__type":"$$EventMessageNode","id":"5abd0988-a5e3-4751-b647-9d83734a9cf8","ts":"2026-02-14T07:52:29.907-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.wait","nodeName":"Wait","nodeId":"0e27eb6c-c5fc-47e0-9fe2-f930e52741ac"}}
{"__type":"$$EventMessageNode","id":"2fc5a840-2823-48f4-9114-18a8720b666a","ts":"2026-02-14T07:52:30.907-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.wait","nodeName":"Wait","nodeId":"0e27eb6c-c5fc-47e0-9fe2-f930e52741ac"}}
{"__type":"$$EventMessageNode","id":"f07f2541-2fc5-480f-a726-c14244b292c0","ts":"2026-02-14T07:52:30.909-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageNode","id":"1b13f2a6-0e54-43be-be0b-0ec00f1ec1f1","ts":"2026-02-14T07:52:31.006-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"17","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageWorkflow","id":"2d9f4963-22ed-4480-bb86-a4163c285bd7","ts":"2026-02-14T07:52:31.008-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"17","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","lastNodeExecuted":"Insert rows in a table","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"relation \"data.data.service_staging.\" does not exist"}}
{"__type":"$$EventMessageAudit","id":"0efd80e6-e003-4f96-8246-6015fd4eb1ee","ts":"2026-02-14T07:52:59.101-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"75052e11-8ce9-432d-b95f-46dacdd7b730","ts":"2026-02-14T07:53:25.187-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"37bf7d63-54c4-4763-9082-7bd5580c1938","ts":"2026-02-14T07:53:32.148-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"0b0aef6d-f67d-43f4-b694-9715bdee1433","ts":"2026-02-14T07:53:40.822-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"f2e3bc93-57f2-4b38-87e4-00e1249f2af1","ts":"2026-02-14T07:54:16.712-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"6829cb58-9df4-4739-b7aa-e8cfea0e73ae","ts":"2026-02-14T07:54:20.998-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"07c46290-6ffb-4096-a9ad-10b0265e7305","ts":"2026-02-14T07:54:23.086-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"efbbf536-fc52-4719-a634-2965152235a9","ts":"2026-02-14T07:54:23.876-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"18","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"d22c1357-66d7-4d10-9440-9d48d1febb30","ts":"2026-02-14T07:54:23.883-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"18","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"37e44c84-cefa-42da-b667-7bb808d19961","ts":"2026-02-14T07:54:23.883-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"18","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageNode","id":"787ef72e-b9b6-4297-9e71-3ddc3ddda3f8","ts":"2026-02-14T07:54:23.969-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"18","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageWorkflow","id":"d77a573b-24c7-4d99-a6c0-e9fdd48dd62f","ts":"2026-02-14T07:54:23.972-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"18","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","lastNodeExecuted":"Insert rows in a table","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"column \"type\" of relation \"service_staging\" does not exist"}}
{"__type":"$$EventMessageWorkflow","id":"f839f0ed-d507-43c6-9862-0c42ca7569de","ts":"2026-02-14T07:54:57.087-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"19","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"2c6f14db-14b1-457a-9e1d-4e3d7b4820ed","ts":"2026-02-14T07:54:57.092-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"19","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"5163c2d2-b1f3-484a-94c5-5824b4d4af71","ts":"2026-02-14T07:54:57.093-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"19","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageNode","id":"38ae162b-b084-4f2a-8045-ec51f5038530","ts":"2026-02-14T07:54:57.174-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"19","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageWorkflow","id":"6943aff3-8528-4b14-8f98-a09259803c14","ts":"2026-02-14T07:54:57.177-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"19","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","lastNodeExecuted":"Insert rows in a table","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"column \"type\" of relation \"service_staging\" does not exist"}}
{"__type":"$$EventMessageAudit","id":"7dd31fb2-78a2-4fd5-a15d-f229346cffbb","ts":"2026-02-14T07:56:16.439-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageWorkflow","id":"75f3ab0e-57c6-4e0d-89b4-5dcc3733d917","ts":"2026-02-14T07:56:22.091-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"20","workflowId":"eRVwBJfXw8ymc6aZ","isManual":false,"workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"47b0760c-179a-464d-b372-649f409cb9f7","ts":"2026-02-14T07:56:22.099-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"20","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"4c62e1d6-db5d-4988-88d6-ac58d1383f18","ts":"2026-02-14T07:56:22.100-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"20","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageNode","id":"50442fcb-493b-4020-b2b5-dde1e956bfaa","ts":"2026-02-14T07:56:22.180-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","executionId":"20","nodeType":"n8n-nodes-base.postgres","nodeName":"Insert rows in a table","nodeId":"1694daf7-4805-4d38-8e87-377b5cd33525"}}
{"__type":"$$EventMessageWorkflow","id":"39577d06-de03-4bcd-b07a-5835e32c7071","ts":"2026-02-14T07:56:22.183-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"20","success":false,"isManual":true,"workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter","lastNodeExecuted":"Insert rows in a table","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"permission denied for table service_staging"}}
{"__type":"$$EventMessageAudit","id":"6a3642fd-08db-492e-8453-945a7dad98a1","ts":"2026-02-14T07:59:45.227-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"54e9a006-1a04-46f5-93f3-f52836454ff8","ts":"2026-02-14T08:01:36.381-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"bfadc201-aef1-4a82-a72d-a38ce5703425","ts":"2026-02-14T08:02:19.364-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"456ca4d2-93e1-47c8-acf4-e6fa3a080d32","ts":"2026-02-14T08:03:21.657-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"17971dc5-2578-4430-add2-2ac305dea97c","ts":"2026-02-14T08:04:45.060-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"222b508d-f23e-4d29-934f-e81fc23f35fb","ts":"2026-02-14T08:04:55.136-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"3fe50cf4-5ae9-4e60-ab6a-48be49c425ff","ts":"2026-02-14T08:05:02.789-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"056728b2-6b80-445f-b9df-ab114d5cffa1","ts":"2026-02-14T08:49:56.576-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"2a34b359-b84c-446e-80bf-f2740a48ef9f","ts":"2026-02-14T08:50:10.430-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"150927be-db4c-4b8d-8a3a-d7052e38b801","ts":"2026-02-14T08:58:51.135-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"c7c0f254-2966-455d-bca3-cebfa1b2dab5","ts":"2026-02-14T08:59:22.718-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"bc2ae192-ebfa-4cce-97e9-32e725a15cc5","ts":"2026-02-14T08:59:25.438-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"5f91b541-7a73-4630-9592-bf8df5e3aa27","ts":"2026-02-14T08:59:30.313-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"aac6019c-d935-4ff9-a7fa-236ae76add07","ts":"2026-02-14T08:59:44.159-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"153c56b1-3cca-4b3b-adc6-80ba93079d1d","ts":"2026-02-14T09:00:04.356-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"36f93747-2b8b-45d4-b0fd-9857b566980a","ts":"2026-02-14T09:11:58.119-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"87e05440-992a-4166-8bcb-01ba52f374b8","ts":"2026-02-14T09:12:11.567-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"8dac283b-2861-4d3f-8307-81317bc1ce4c","ts":"2026-02-14T09:12:17.827-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"57704429-5b09-428c-984f-fe3aa7fc4aea","ts":"2026-02-14T09:12:20.895-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"0fad1c53-af81-4ec0-ac41-d7cd1992aca4","ts":"2026-02-14T09:12:45.565-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"c7f6327c-7788-4105-96b8-c3c608b3709e","ts":"2026-02-14T09:12:49.760-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"f7e6feab-0a45-4b78-b526-bd61d90421c8","ts":"2026-02-14T09:12:58.006-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"ff14b040-c2d8-4f98-8d9a-68789d96ca9f","ts":"2026-02-14T09:13:10.495-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"ad2b88bc-5aea-4a57-bc0d-f75b70237e9f","ts":"2026-02-14T09:13:22.220-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"f439a7c2-7aec-4bc5-8ef4-2891aa75072a","ts":"2026-02-14T09:13:32.643-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"eRVwBJfXw8ymc6aZ","workflowName":"01 - Dunakeszi Seed Hunter"}}
{"__type":"$$EventMessageAudit","id":"b03f51f4-38a5-4216-97fc-060228d357b3","ts":"2026-02-14T11:11:03.532-05:00","eventName":"n8n.audit.workflow.created","message":"n8n.audit.workflow.created","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"f7ee61b3-54e6-401b-aab5-81128d453f47","ts":"2026-02-14T11:15:31.558-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"f5d90ffb-677c-4278-9819-0168bf5abb56","ts":"2026-02-14T11:15:34.092-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"5f0b5c1d-2088-4f54-a925-e8760b0af90a","ts":"2026-02-14T11:15:34.851-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"21","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d4b67302-8ed3-49b4-9dde-21f37bc60cf5","ts":"2026-02-14T11:15:34.851-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"21","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"f4480e67-7b51-4c8d-9088-b5a39d18875e","ts":"2026-02-14T11:15:34.852-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"21","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"d90cfc8e-4e86-4893-ae45-e57cf3cf1a98"}}
{"__type":"$$EventMessageNode","id":"ae82527c-6e80-49a8-915c-2119ff1843f7","ts":"2026-02-14T11:15:34.853-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"21","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"d90cfc8e-4e86-4893-ae45-e57cf3cf1a98"}}
{"__type":"$$EventMessageWorkflow","id":"eb367c68-079c-4ac7-a3c5-4404fce907f1","ts":"2026-02-14T11:15:34.855-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"21","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"e1136c57-8423-49ad-90b6-d19e35021ccb","ts":"2026-02-14T11:15:35.846-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"22","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"071583ee-734a-4cd4-9f6b-29b0d77f2eeb","ts":"2026-02-14T11:15:35.847-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"22","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"51e6cf75-0cf8-44ab-814e-695c6cb21a64","ts":"2026-02-14T11:15:35.847-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"22","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"d90cfc8e-4e86-4893-ae45-e57cf3cf1a98"}}
{"__type":"$$EventMessageNode","id":"cb797990-a264-4619-b61b-e986af3afec5","ts":"2026-02-14T11:15:35.848-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"22","nodeType":"n8n-nodes-base.manualTrigger","nodeName":"When clicking Execute workflow","nodeId":"d90cfc8e-4e86-4893-ae45-e57cf3cf1a98"}}
{"__type":"$$EventMessageWorkflow","id":"22876ee3-57dc-43a8-a942-16f16f6f41c0","ts":"2026-02-14T11:15:35.849-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"22","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"9836c88e-c2de-40bf-bccc-798adb53db35","ts":"2026-02-14T11:15:44.617-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"fd4fd0c9-743f-4d97-a12b-877662ec0e05","ts":"2026-02-14T11:16:02.910-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"3c46ffad-765e-4628-b363-a6871abca2a4","ts":"2026-02-14T11:16:09.335-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"636982ce-739a-427d-b156-3e4bc2df356f","ts":"2026-02-14T11:17:14.755-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"df7edb80-d61d-46c4-b957-0c36622b085b","ts":"2026-02-14T11:17:35.752-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"4351d203-e85a-4893-8017-c3fae8d65b6d","ts":"2026-02-14T11:18:13.197-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"751c7f6c-577d-47b1-8019-9e5c0edecf4d","ts":"2026-02-14T11:18:24.037-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8d50c983-8b48-400d-99f5-7d953c11d3b7","ts":"2026-02-14T11:18:28.865-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"0f4b0ad4-afa7-4c20-b6e3-c638d5f7482a","ts":"2026-02-14T11:18:49.272-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"23","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c93aa1a9-fc76-4d43-979b-471593317b4f","ts":"2026-02-14T11:18:49.273-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"23","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"8cb84cae-cae5-4eed-af29-a2f93948d4e3","ts":"2026-02-14T11:18:49.274-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"23","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"1ba901e3-a533-4170-ba71-69b06ce97ce1"}}
{"__type":"$$EventMessageNode","id":"00c076b2-b34f-4726-b30d-07a6092da31e","ts":"2026-02-14T11:18:49.346-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"23","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"1ba901e3-a533-4170-ba71-69b06ce97ce1"}}
{"__type":"$$EventMessageWorkflow","id":"3de894e1-07c3-4826-8044-244c2e5973c0","ts":"2026-02-14T11:18:49.348-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"23","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Forbidden - perhaps check your credentials?"}}
{"__type":"$$EventMessageWorkflow","id":"cc6b9419-63db-4a1f-8d9d-ad45d3ba889c","ts":"2026-02-14T11:19:50.235-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"24","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"71c8d27c-6120-4d9e-9e9c-34a0f323c4ad","ts":"2026-02-14T11:19:50.235-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"24","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"37203cad-1a6e-490a-8059-65b2241c4611","ts":"2026-02-14T11:19:50.236-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"24","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"1ba901e3-a533-4170-ba71-69b06ce97ce1"}}
{"__type":"$$EventMessageNode","id":"8d36ece3-aeb5-4db4-bb2e-ea1f28cb371b","ts":"2026-02-14T11:19:50.278-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"24","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"1ba901e3-a533-4170-ba71-69b06ce97ce1"}}
{"__type":"$$EventMessageWorkflow","id":"e73b3650-1367-4198-aa3b-4fc3d785fc7c","ts":"2026-02-14T11:19:50.280-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"24","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Forbidden - perhaps check your credentials?"}}
{"__type":"$$EventMessageAudit","id":"09a1987b-4a1c-4985-b3ca-98a6aa50576b","ts":"2026-02-14T11:21:31.809-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c3e2d415-7933-4091-affa-9197e4ce6a6e","ts":"2026-02-14T11:21:41.829-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"84b18af9-32cd-43a7-b815-32bf0bfe72c6","ts":"2026-02-14T11:24:41.930-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"96b83cc9-4a78-43b4-8e8d-45949f3a62ec","ts":"2026-02-14T11:25:15.936-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"60d240e0-f9ef-4c05-b671-68436b2d9f81","ts":"2026-02-14T11:25:32.598-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"778d1495-3a54-490a-a126-5942f4ec03b7","ts":"2026-02-14T11:25:41.184-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"378803de-cb2c-4f30-bf13-da65d3f728f0","ts":"2026-02-14T11:25:44.477-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"6cff5779-6916-41b2-986f-7781b5410f2b","ts":"2026-02-14T11:25:49.355-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d0eadcc3-7e3d-48b9-9c89-34b873c5a6df","ts":"2026-02-14T11:25:51.566-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"0bf6fe28-8624-4539-918a-e408d7901ab5","ts":"2026-02-14T11:25:55.564-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c0caddc6-c61d-4966-b0a7-9936e414014c","ts":"2026-02-14T11:26:05.238-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d17abd81-96cd-4f17-8c5e-0a3cf7a45aba","ts":"2026-02-14T11:26:06.904-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"853d2cf4-b6e0-4dd0-8677-dbf0adc2bb04","ts":"2026-02-14T11:26:12.924-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d035437f-c1e2-4dc5-b7e2-de6d68a15e0f","ts":"2026-02-14T11:26:18.866-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"fd9be312-d7fe-4c8d-b0be-0a195406cff7","ts":"2026-02-14T11:26:20.628-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"ca9c208d-054b-4aff-ae11-14d6e7029136","ts":"2026-02-14T11:26:25.916-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"602190d4-f8d8-417c-9a17-1a72a8ba6941","ts":"2026-02-14T11:26:31.857-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"35d27eb7-44d1-4374-af6e-a35d45499000","ts":"2026-02-14T11:32:44.131-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"19b000a2-c46b-4286-809c-01f290ff0fd5","ts":"2026-02-14T11:32:49.816-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"9df0dd95-2cb1-439b-bbda-293eeb7204f6","ts":"2026-02-14T11:33:04.928-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"25","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"11bf7c33-35f8-4524-ab85-56d7f52631ef","ts":"2026-02-14T11:33:04.929-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"25","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"cad0e034-bde2-46f5-8485-34dbd6c33247","ts":"2026-02-14T11:33:04.930-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"25","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"1ba901e3-a533-4170-ba71-69b06ce97ce1"}}
{"__type":"$$EventMessageNode","id":"014c55f3-8c80-435a-b30f-d818bf6569bd","ts":"2026-02-14T11:33:09.112-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"25","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"1ba901e3-a533-4170-ba71-69b06ce97ce1"}}
{"__type":"$$EventMessageWorkflow","id":"3daf1134-6caa-4c70-af12-d99c0299c1c1","ts":"2026-02-14T11:33:09.116-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"25","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Forbidden - perhaps check your credentials?"}}
{"__type":"$$EventMessageAudit","id":"33a7ee00-3087-4727-b5bc-2275096be79d","ts":"2026-02-14T18:31:37.641-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8819563e-6c91-4fb3-b829-61ebaaf07690","ts":"2026-02-14T18:31:39.572-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"eedce65c-74f0-4be6-a94b-e1669bf1e649","ts":"2026-02-14T18:35:27.417-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"05351c7d-25c2-41da-8369-3adccd8f9b99","ts":"2026-02-14T18:37:01.972-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8f37e2cd-04ca-4197-a456-8146afe5a838","ts":"2026-02-14T18:37:17.638-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d232bf5d-3820-474d-908a-930bd42b083d","ts":"2026-02-14T18:37:32.257-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"86bc1bad-9540-4a33-9f21-10aa62d20842","ts":"2026-02-14T18:37:34.508-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"690dd642-5b9a-4350-93de-d87a946c3550","ts":"2026-02-14T18:37:51.192-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"a6111065-dc46-40d0-8f85-e9dcf3a4434b","ts":"2026-02-14T18:37:53.849-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"0ee79dcc-e74e-4bc5-9a03-5cd12f267c97","ts":"2026-02-14T18:38:00.862-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"726d01ee-b17c-4f7a-806c-589aad5d28cc","ts":"2026-02-14T18:39:12.391-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"26","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"fe9907f3-c592-47f1-b386-0466e59e7837","ts":"2026-02-14T18:39:12.392-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"26","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"8c989f96-4e0b-4682-b52f-650652fb68b4","ts":"2026-02-14T18:39:12.393-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"26","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"4c9cae69-038e-4de3-810b-25a3449182aa","ts":"2026-02-14T18:39:12.419-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"26","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"700c1cc8-24b1-4bc8-82fd-bcd3b0ebca29","ts":"2026-02-14T18:39:12.420-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"26","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"The resource you are requesting could not be found"}}
{"__type":"$$EventMessageAudit","id":"48fb2e4b-ce51-4c8c-96c2-8fe9405eedaf","ts":"2026-02-14T18:39:38.937-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"0d11f1b7-12f1-40c4-a4ce-6def83cbe274","ts":"2026-02-14T18:39:39.949-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"27","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"56cabcab-f888-4bf7-8d33-d3376a68a137","ts":"2026-02-14T18:39:39.950-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"27","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"022b2038-a405-4185-bd66-801d8a49c545","ts":"2026-02-14T18:39:39.951-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"27","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"f3ccf75b-c99b-427c-b33f-edbc888e98a8","ts":"2026-02-14T18:39:39.960-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"27","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"05dad2f5-cd61-4311-9501-bb591dd1c3da","ts":"2026-02-14T18:39:39.963-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"27","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"The service refused the connection - perhaps it is offline"}}
{"__type":"$$EventMessageAudit","id":"3d1e556e-e177-4fcb-b9b2-f4c8d38df811","ts":"2026-02-14T18:41:03.693-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"bc35223e-9e31-4c55-b0b7-32212efe9118","ts":"2026-02-14T18:41:32.089-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"ee22062b-3f3d-481e-88f7-4546bdaf752c","ts":"2026-02-14T18:42:16.619-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"28","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"dc0c5ab8-e603-497f-a7f9-809bb31f0497","ts":"2026-02-14T18:42:16.620-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"28","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"62fecc92-aa19-4ce6-a590-26ce53d3fec3","ts":"2026-02-14T18:42:16.620-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"28","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"92b72036-ca0f-4e1a-8e7f-8b17dd1cd502","ts":"2026-02-14T18:42:16.667-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"28","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"44a1ae4d-dd2c-48ed-b145-97cbad0a54c9","ts":"2026-02-14T18:42:16.670-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"28","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Bad request - please check your parameters"}}
{"__type":"$$EventMessageAudit","id":"c5e0c681-b96c-40d7-a7cd-1a7b2f89e00a","ts":"2026-02-14T18:45:25.350-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"4cb9f7a8-8bb9-438c-99b2-84dad891a158","ts":"2026-02-14T18:45:39.048-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"0345ee18-4791-4f37-9d83-1779604729e9","ts":"2026-02-14T18:45:44.576-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"29","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"a5029189-2c02-48f1-851d-f769da0420bd","ts":"2026-02-14T18:45:44.576-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"29","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"ee3bccb3-c669-4279-b6c1-036d4a79fa22","ts":"2026-02-14T18:45:44.577-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"29","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"6cedb5ca-ec3e-4503-be0b-f683cf7f18c0","ts":"2026-02-14T18:45:44.599-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"29","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"d03f1e6b-40c0-4417-adee-46a01468d44b","ts":"2026-02-14T18:45:44.602-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"29","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Bad request - please check your parameters"}}
{"__type":"$$EventMessageAudit","id":"132a094e-3e46-4648-b1e0-c2d7757d5b53","ts":"2026-02-14T18:46:36.631-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"80bf7b06-b1e4-4731-ae95-f72bca141026","ts":"2026-02-14T18:46:40.347-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"1d7a157d-8f2b-4e8d-be32-f89c0fe01bc4","ts":"2026-02-14T18:46:41.323-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"30","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c8520031-08d3-480c-8b21-dcfaacd312ef","ts":"2026-02-14T18:46:41.324-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"30","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"8e5b741b-8f14-42a7-b2a9-e9d441de62b7","ts":"2026-02-14T18:46:41.324-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"30","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"7413114e-ffd4-45c4-8b05-4deb0284d3b0","ts":"2026-02-14T18:47:12.089-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"30","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"e6ce8afe-f67a-493e-823e-181928e1dec7","ts":"2026-02-14T18:47:12.092-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"30","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Bad request - please check your parameters"}}
{"__type":"$$EventMessageAudit","id":"81ea53d3-7bde-4fae-9deb-ea824da28727","ts":"2026-02-14T19:00:08.203-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"c00c2b31-eb02-42c2-a3a8-ba3682d3d038","ts":"2026-02-14T19:00:12.442-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"31","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8f8adc88-e7d8-4dd8-bb5c-fa5ec1361ca8","ts":"2026-02-14T19:00:12.443-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"31","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"1face4d0-da30-495a-8fce-2173e016d75e","ts":"2026-02-14T19:00:12.443-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"31","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"1a586fff-2c6a-4b18-b31b-50fc4096f956","ts":"2026-02-14T19:00:12.467-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"31","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"37e758a8-9542-4c42-8b86-b51843ffb4a9","ts":"2026-02-14T19:00:12.472-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"31","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Bad request - please check your parameters"}}
{"__type":"$$EventMessageAudit","id":"57e9abc4-6b9c-475c-9554-8b96571f0bda","ts":"2026-02-14T19:00:54.321-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"3e9823e8-e4bd-499e-953d-40291aa6970d","ts":"2026-02-14T19:04:46.227-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"32","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c335fbc1-bdfc-4348-9d0b-18872ce098aa","ts":"2026-02-14T19:04:46.227-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"32","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"ec2439a8-c627-409d-908c-c3b75ca5769b","ts":"2026-02-14T19:04:46.228-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"32","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageNode","id":"5a1d7ac2-f960-4b0d-b8c2-7335e2eb26f3","ts":"2026-02-14T19:04:46.248-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"32","nodeType":"n8n-nodes-base.httpRequest","nodeName":"HTTP Request","nodeId":"19cb0235-1a2f-4905-95d8-4d37d8d8b608"}}
{"__type":"$$EventMessageWorkflow","id":"7cbf5ae3-5641-420d-883e-58d9768a3709","ts":"2026-02-14T19:04:46.250-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"32","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"HTTP Request","errorNodeType":"n8n-nodes-base.httpRequest","errorMessage":"Bad request - please check your parameters"}}
{"__type":"$$EventMessageAudit","id":"858d8f5f-c0ef-4206-be14-14eea6bf26fc","ts":"2026-02-14T19:09:16.916-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"71fee88b-630f-4ee3-9b39-ac98364d4f2c","ts":"2026-02-14T19:11:45.977-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"f41df978-9b9b-4f61-ac3c-af2c9fa9e808","ts":"2026-02-14T19:12:50.462-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"4077f1a8-8a52-4d75-a202-ffc6e0683297","ts":"2026-02-14T19:13:19.708-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"28e3e612-7f33-4cf8-9ae3-72056b85ac34","ts":"2026-02-14T19:14:17.502-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"33","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"1e8f228c-645e-41ff-8f71-0f906b0ec95f","ts":"2026-02-14T19:14:17.502-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"33","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"4c8ec78d-f736-4ee3-9b6a-6be3544f3cb2","ts":"2026-02-14T19:14:17.503-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"33","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"cf12fd03-f52d-4321-a3e6-b838c2ad41b6","ts":"2026-02-14T19:14:17.511-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"ZNqhdM67","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"33"}}
{"__type":"$$EventMessageRunner","id":"e8c5ed50-b8ef-4a47-944d-86a5db75dcc7","ts":"2026-02-14T19:14:17.603-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"ZNqhdM67","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"33"}}
{"__type":"$$EventMessageNode","id":"05840ac7-b274-4fed-a04d-a10e4b3e4a27","ts":"2026-02-14T19:14:17.605-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"33","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageWorkflow","id":"b5cf7974-f861-4daf-a7e1-3aaedeeac17e","ts":"2026-02-14T19:14:17.612-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"33","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"18ca4a3a-8b35-484e-bffe-e55c53bb3391","ts":"2026-02-14T19:15:45.719-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"14dc22a9-d0bf-452e-b158-f9142d3f8e5d","ts":"2026-02-14T19:16:54.698-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"9317dfb5-03fe-4654-9eab-1e09ddd81c48","ts":"2026-02-14T19:17:11.870-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"824bbd95-f082-4fd8-9543-64f98f707a60","ts":"2026-02-14T19:17:25.474-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d960dd2d-c06e-416b-b742-d70f19ffb929","ts":"2026-02-14T19:17:34.346-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"20240e56-8ed7-4e08-834c-13f443d8c137","ts":"2026-02-14T19:17:39.405-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"34","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"2b62dfc4-6f48-48f5-9c12-2075b2e714d0","ts":"2026-02-14T19:17:39.406-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"34","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"34007a36-dea2-4170-a6eb-3bcef2f979a5","ts":"2026-02-14T19:17:39.407-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"34","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"8217a0b8-cd7d-456c-b942-50860bb04533","ts":"2026-02-14T19:17:39.411-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"GF0uPgG1","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"34"}}
{"__type":"$$EventMessageRunner","id":"7095603d-0ad8-4414-a77e-1991b23a6d91","ts":"2026-02-14T19:17:39.469-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"GF0uPgG1","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"34"}}
{"__type":"$$EventMessageNode","id":"96f789e4-9c32-4396-b4a5-d6def55bfae2","ts":"2026-02-14T19:17:39.469-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"34","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"2c5f98a7-9bc6-426b-a870-e63d6029fe32","ts":"2026-02-14T19:17:39.475-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"34","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"f030adfc-8822-45d6-852c-d8b95ed5edfa","ts":"2026-02-14T19:17:39.549-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"34","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageWorkflow","id":"9c8b0ca9-454e-48cf-a58e-45f358d8b4c5","ts":"2026-02-14T19:17:39.550-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"34","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8ddafa75-05e8-4bc2-966b-1f6c2a48d601","ts":"2026-02-14T19:19:25.577-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c2b41941-44b2-4fd5-9b4a-242f14cd0328","ts":"2026-02-14T19:19:27.572-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"2c14d7c4-07c4-4519-89cc-347918f045a0","ts":"2026-02-14T19:20:30.609-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"6462c413-941d-4f47-894e-5e39b4e5ffc0","ts":"2026-02-14T19:20:42.613-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"d0af5296-0bdd-4001-9fca-ddd9d23b09c0","ts":"2026-02-14T19:20:56.942-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"35","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"1411cc3b-497d-4d1e-a1ac-7c0bf769873f","ts":"2026-02-14T19:20:56.943-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"35","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"fb7961de-f6c0-493f-9d9b-cb40add56730","ts":"2026-02-14T19:20:56.944-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"35","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"dd663b80-ab35-4fc1-ac97-bc5e91794176","ts":"2026-02-14T19:20:56.947-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"zOXMLRsd","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"35"}}
{"__type":"$$EventMessageRunner","id":"14af5a9b-ef65-486a-8e59-6aad16018c57","ts":"2026-02-14T19:20:57.003-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"zOXMLRsd","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"35"}}
{"__type":"$$EventMessageNode","id":"c0274d29-e2e5-43ec-93df-1716bb671133","ts":"2026-02-14T19:20:57.003-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"35","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"ffd8ab53-de72-4e04-a49d-6dc86efa6c32","ts":"2026-02-14T19:20:57.009-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"35","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"76584705-2a62-47f1-8860-26382d394eed","ts":"2026-02-14T19:20:57.064-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"35","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageWorkflow","id":"b6fe000b-ee40-4986-b081-d330ec89eea2","ts":"2026-02-14T19:20:57.066-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"35","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"02fbcd73-91af-47c4-bf24-d8ece5be0e0c","ts":"2026-02-14T19:22:00.354-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"aea81adc-3814-4e67-8ca4-fc2ca6eb1751","ts":"2026-02-14T19:22:17.335-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"c3889e7a-0098-4e81-8c6f-2d3a0430768c","ts":"2026-02-14T19:22:17.576-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"36","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"16d955d5-bd1c-4318-bfc0-ca94c081800a","ts":"2026-02-14T19:22:17.577-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"223baff6-e2ea-495b-9d8a-66806bccab30","ts":"2026-02-14T19:22:17.578-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"3ee02e49-afae-4a67-b22a-aab021b64f7a","ts":"2026-02-14T19:22:17.580-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"lKGVrE-q","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"36"}}
{"__type":"$$EventMessageRunner","id":"9d36ccfe-1b92-4264-97fe-01ffd55dd172","ts":"2026-02-14T19:22:17.632-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"lKGVrE-q","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"36"}}
{"__type":"$$EventMessageNode","id":"7e476f2d-d016-4616-96c2-9f05ae34dca7","ts":"2026-02-14T19:22:17.633-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"746f54ad-201a-4c68-8761-e186f5bf095b","ts":"2026-02-14T19:22:17.639-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"57a1bcaf-7c62-436b-896e-649df6d099c0","ts":"2026-02-14T19:22:17.682-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"75bd4a30-d1df-4f3d-b669-d9f764cc39b0","ts":"2026-02-14T19:22:17.682-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"de283afc-35db-4db5-9a17-3c81f70f9793","ts":"2026-02-14T19:22:17.684-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"36","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageWorkflow","id":"a8b51fa2-d381-4a34-95ba-b62b6ad8a5a2","ts":"2026-02-14T19:22:17.684-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"36","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c298ce44-d72b-45a2-b674-71c23c2edf6f","ts":"2026-02-14T19:22:53.435-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"0a66e6ea-b1c1-48a5-9b79-94fc9eade352","ts":"2026-02-14T19:22:59.354-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"37","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"872f9421-3665-4b6e-be23-6803f948d715","ts":"2026-02-14T19:22:59.355-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"ee66f833-9eaf-4fea-918e-d30e4b5d26d1","ts":"2026-02-14T19:22:59.356-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"ed71f99e-5a3d-4679-a6aa-352264d752c7","ts":"2026-02-14T19:22:59.359-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"dPQ6-CQE","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"37"}}
{"__type":"$$EventMessageRunner","id":"7f71a6c8-f100-4996-966a-ef400c96fef3","ts":"2026-02-14T19:22:59.406-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"dPQ6-CQE","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"37"}}
{"__type":"$$EventMessageNode","id":"d0c65143-1052-4310-8ecd-88b6b377229e","ts":"2026-02-14T19:22:59.407-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"d066fbd9-057f-40bd-92bb-bc2e8b6d9e97","ts":"2026-02-14T19:22:59.411-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"b17788e8-6197-4274-8b08-cb74b389cc83","ts":"2026-02-14T19:22:59.450-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"6a1cb3c6-21ce-4314-82f7-9eb1866783ab","ts":"2026-02-14T19:22:59.451-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"9e62d0de-1cf9-4fce-b97c-04741fc20859","ts":"2026-02-14T19:22:59.451-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"37","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageWorkflow","id":"0a01a889-d517-4346-8a04-66d69361c8d4","ts":"2026-02-14T19:22:59.452-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"37","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"93d4e103-9501-4d28-83a8-6637604a47bc","ts":"2026-02-14T19:23:08.053-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"f80dc75b-7aad-4e94-a4f8-348a879794d3","ts":"2026-02-14T19:23:08.245-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"38","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"c7d624ac-5f87-477d-8c68-77472231092f","ts":"2026-02-14T19:23:08.245-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"ddc3cc6b-5ad5-4ff4-a53b-7839eacd5c92","ts":"2026-02-14T19:23:08.246-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"78d8367c-6755-43ba-a28b-7657b0676dfe","ts":"2026-02-14T19:23:08.248-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"nukUxiSj","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"38"}}
{"__type":"$$EventMessageRunner","id":"101bd4c0-be93-4e90-bd66-5b46eadfcdfd","ts":"2026-02-14T19:23:08.307-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"nukUxiSj","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"38"}}
{"__type":"$$EventMessageNode","id":"8fa9896a-be5a-4678-840b-8497807c4a29","ts":"2026-02-14T19:23:08.307-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"51d745f0-9076-4e51-91c7-0c06032a34d7","ts":"2026-02-14T19:23:08.313-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"fe36bf49-98fa-45c0-992f-05f55e1d4394","ts":"2026-02-14T19:23:08.352-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"6441dc1f-d008-45f3-bcdc-9b1e808fd954","ts":"2026-02-14T19:23:08.353-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"295bd675-4a0d-4cb6-b2ad-0f6b0216950a","ts":"2026-02-14T19:23:08.354-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"38","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageWorkflow","id":"7aafb097-3f6a-43cf-a6a8-9d51aeb26979","ts":"2026-02-14T19:23:08.355-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"38","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"24ba491b-e346-47a6-9d95-7584a5133d26","ts":"2026-02-14T19:23:15.007-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"3f0f6cde-1c82-46a1-bcef-57827a0a764c","ts":"2026-02-14T19:23:17.758-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"cdbc3c61-ecd8-43de-8b72-8ad51da22fb3","ts":"2026-02-14T19:23:21.919-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"39","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"78440af1-c937-4f0b-bd2b-5737883a2899","ts":"2026-02-14T19:23:21.920-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"33705212-ff58-483a-b273-363c7e4bffca","ts":"2026-02-14T19:23:21.921-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"ae6e7c4b-53a8-4e6e-8825-2c50b8e848aa","ts":"2026-02-14T19:23:21.924-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"hb5XzItq","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"39"}}
{"__type":"$$EventMessageRunner","id":"4ebc8d27-2279-4264-a3c6-0059c249b7c8","ts":"2026-02-14T19:23:21.975-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"hb5XzItq","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"39"}}
{"__type":"$$EventMessageNode","id":"7174c264-5019-437d-9654-5505e01cedf8","ts":"2026-02-14T19:23:21.976-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"49a0cad3-33ac-4b3f-811c-58df24916542","ts":"2026-02-14T19:23:21.981-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"bd42d021-b453-47b6-b924-b6d8dca4d244","ts":"2026-02-14T19:23:22.019-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"a252efd0-43e5-4fb3-876d-9a30403616e5","ts":"2026-02-14T19:23:22.020-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"960b612a-cb9b-4eca-a247-74f02aa72484","ts":"2026-02-14T19:23:22.020-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"39","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageWorkflow","id":"b2e19719-fae3-4881-a8c3-3dc6f90078b0","ts":"2026-02-14T19:23:22.022-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"39","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"815de0f4-64c0-4e14-93b9-70937acec49c","ts":"2026-02-14T19:23:22.370-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"bc2956b5-b275-49f4-96ee-40443957a926","ts":"2026-02-14T19:24:00.518-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8bcaf347-33b2-427e-ab99-c6b88df882ae","ts":"2026-02-14T19:24:48.923-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"355d6391-1902-499f-9b7a-7382044a7b34","ts":"2026-02-14T19:24:50.810-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"ddab95bf-fa53-46e2-8434-7c6f92e4b86d","ts":"2026-02-14T19:25:01.433-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"a8fb95f7-7c63-4d6f-a327-a83d94fad5a1","ts":"2026-02-14T19:25:01.743-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"40","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"cbbe81bf-5796-4243-8ace-16bfbfd07e23","ts":"2026-02-14T19:25:01.744-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"40","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"517e33a7-ce32-4226-847d-79db0ba8783b","ts":"2026-02-14T19:25:01.745-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"40","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"4795ea85-c9eb-4d95-9327-793a0a5c98fd","ts":"2026-02-14T19:25:01.748-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"A0etMNPy","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"40"}}
{"__type":"$$EventMessageRunner","id":"3e9a11aa-c2af-4bf9-ab06-4a05d03e83f8","ts":"2026-02-14T19:25:01.800-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"A0etMNPy","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"40"}}
{"__type":"$$EventMessageNode","id":"f70d0374-b06d-4214-8cc1-d68ca5962d63","ts":"2026-02-14T19:25:01.801-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"40","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"492d70d1-2b77-485e-be2c-02564aefaefa","ts":"2026-02-14T19:25:01.806-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"40","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"bc845d1f-0525-4138-93f3-3107dc7ba8d5","ts":"2026-02-14T19:25:01.846-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"40","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageWorkflow","id":"86b024cf-0fd2-4eb3-9749-41064fffb394","ts":"2026-02-14T19:25:01.847-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"40","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"66d33457-7859-4035-aff5-6d75c43cfb03","ts":"2026-02-14T19:25:06.406-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"d86357d8-bc0a-49fd-bdf7-0ca0c2bd2d85","ts":"2026-02-14T19:25:08.886-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"41","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"29a9992a-4b7d-47a6-9af9-1c5c68b16f8d","ts":"2026-02-14T19:25:08.887-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"3255d0e6-f1d6-485f-99ef-85f4fb01b5cf","ts":"2026-02-14T19:25:08.888-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"e7ff66f4-5f6f-4335-8dee-c07aacbce539","ts":"2026-02-14T19:25:08.892-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"eyClpGUr","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"41"}}
{"__type":"$$EventMessageRunner","id":"e26731d5-f2b9-4640-bc63-e0974e02aa0d","ts":"2026-02-14T19:25:08.937-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"eyClpGUr","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"41"}}
{"__type":"$$EventMessageNode","id":"d0718e41-edf4-411c-a511-c64e4255c44a","ts":"2026-02-14T19:25:08.938-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"55e6a449-88fb-4978-a5be-0665091351ce","ts":"2026-02-14T19:25:08.944-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"9d25a223-38c8-49dd-85c6-191575090b70","ts":"2026-02-14T19:25:08.973-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"dc87a504-86a9-4c3a-992b-f193d9a9e38e","ts":"2026-02-14T19:25:08.974-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"4a7f02a5-7dc9-4909-8825-4b569d79ac34","ts":"2026-02-14T19:25:08.974-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"41","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageWorkflow","id":"dd28111a-ae5a-43cd-a94e-4baa5a1160af","ts":"2026-02-14T19:25:08.975-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"41","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"1c72db37-7cdc-4ed3-8aca-9cd932a99f6f","ts":"2026-02-14T19:27:05.491-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"3b591a4a-3c3d-4e01-8663-bff7cf36db57","ts":"2026-02-14T19:27:39.078-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"af76b173-b732-4ef8-9daa-7d02a275df32","ts":"2026-02-14T19:27:43.308-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"42","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"8bd55b0d-fcf0-4d2f-b977-a03e4fd1e650","ts":"2026-02-14T19:27:43.309-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"b42a4247-2b3d-4f42-93b1-b8641f0eb5e4","ts":"2026-02-14T19:27:43.310-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"ac38e18c-05f3-4a1e-918a-c5417558553b","ts":"2026-02-14T19:27:43.313-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"ptZotiS7","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"42"}}
{"__type":"$$EventMessageRunner","id":"2523767d-d19a-486c-9f6a-2f9ccd278e1f","ts":"2026-02-14T19:27:43.363-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"ptZotiS7","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"42"}}
{"__type":"$$EventMessageNode","id":"a09af45a-6cc7-4477-b576-286e0738f59a","ts":"2026-02-14T19:27:43.364-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"0b73059a-5838-4698-9683-888f8a8417e6","ts":"2026-02-14T19:27:43.370-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"20b1f59d-cf15-4706-af9f-dd80d5be1923","ts":"2026-02-14T19:27:43.405-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"0caef885-dfb0-4e22-8c5f-cbc79cb66fdd","ts":"2026-02-14T19:27:43.406-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"b76576c7-c6c7-49aa-abf1-c67c76838a55","ts":"2026-02-14T19:27:43.407-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"4ac9682b-35d1-490f-a84d-0588dbcb5deb","ts":"2026-02-14T19:27:43.407-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"f884bd2c-116d-478d-b24b-856cfae01b8e","ts":"2026-02-14T19:27:43.408-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"mjnoQD3U","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"42"}}
{"__type":"$$EventMessageNode","id":"0472b27e-f7eb-42f9-9736-07f137f9b78e","ts":"2026-02-14T19:27:43.435-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"42","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageWorkflow","id":"ea286ebe-73ae-40f0-9918-9faca88f9edb","ts":"2026-02-14T19:27:43.437-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"42","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"Code in JavaScript1","errorMessage":"item is not defined [line 2]"}}
{"__type":"$$EventMessageAudit","id":"f1bcc4e2-e43b-46a7-95fd-7ff1cfcaeda8","ts":"2026-02-14T19:29:07.457-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"4db656ec-fc9e-4836-b54d-faa872e4332d","ts":"2026-02-14T19:29:07.935-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"43","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"d16ac747-5f76-4bfd-85dd-e68fe25bf073","ts":"2026-02-14T19:29:07.935-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"d7d26429-e64a-46c7-a489-be21816fb13a","ts":"2026-02-14T19:29:07.937-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"c9100d04-1fe2-4d2b-a35b-ee37cbf65102","ts":"2026-02-14T19:29:07.939-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"u9QPBtvo","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"43"}}
{"__type":"$$EventMessageRunner","id":"0110dd92-0307-406a-b131-42b34386ff32","ts":"2026-02-14T19:29:07.995-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"u9QPBtvo","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"43"}}
{"__type":"$$EventMessageNode","id":"d4050257-81bb-41aa-8f6f-193473bad858","ts":"2026-02-14T19:29:07.995-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"68151441-eb01-4911-8efe-c1f9015d3ff1","ts":"2026-02-14T19:29:08.001-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"5391c200-4cd3-4997-a6a7-96897715f1c2","ts":"2026-02-14T19:29:08.040-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"03f20266-20fd-4601-858b-ea03a9c07ab8","ts":"2026-02-14T19:29:08.041-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"244a5637-818e-4890-8c2f-df0f52da9cb7","ts":"2026-02-14T19:29:08.042-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"6fc2d269-9365-4a9f-9de7-cb37b1493743","ts":"2026-02-14T19:29:08.043-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"ce24de19-85ab-49ff-8c43-9ede9dce7c05","ts":"2026-02-14T19:29:08.044-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"ePHzT-N3","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"43"}}
{"__type":"$$EventMessageRunner","id":"9e289a7a-e4a5-43de-86e9-5efeb195592f","ts":"2026-02-14T19:29:08.054-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"ePHzT-N3","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"43"}}
{"__type":"$$EventMessageNode","id":"bc519240-c055-4de1-9225-de3717459e35","ts":"2026-02-14T19:29:08.055-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"43","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageWorkflow","id":"d491c9e8-d7ac-4691-9db3-a84359f9add5","ts":"2026-02-14T19:29:08.056-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"43","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"947ab9ed-5655-4601-9fe0-4c61fa89e228","ts":"2026-02-14T19:30:00.692-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"44","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"ed9274ba-ce85-4fa0-8c20-39771ec404df","ts":"2026-02-14T19:30:00.692-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"88d8c2dc-99b8-43f3-b35a-a18febdba092","ts":"2026-02-14T19:30:00.693-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"478886b2-0275-4641-9561-1b72fb8280a3","ts":"2026-02-14T19:30:00.695-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"aNOkY1kM","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"44"}}
{"__type":"$$EventMessageRunner","id":"e6cd758b-db0c-4f5e-bfca-a5bec18b6bbc","ts":"2026-02-14T19:30:00.746-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"aNOkY1kM","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"44"}}
{"__type":"$$EventMessageNode","id":"70f63f0c-e91a-469a-8e8f-dd96c1ae1170","ts":"2026-02-14T19:30:00.747-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"524881cc-7296-4468-a9f6-c2353a41a324","ts":"2026-02-14T19:30:00.753-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"640bef7b-2269-40d9-a273-5966a27284fd","ts":"2026-02-14T19:30:00.786-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"a4f6cf77-9d64-4be5-ab35-c2249c57edc5","ts":"2026-02-14T19:30:00.787-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"bb5e322b-32a4-44ea-853c-fb0ba443c14d","ts":"2026-02-14T19:30:00.787-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"524c81ea-91d3-4a0f-9c74-88e36e7d951c","ts":"2026-02-14T19:30:00.788-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"e580273a-eccc-4216-a414-7d28bdf9706e","ts":"2026-02-14T19:30:00.789-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"ZU1z_J3D","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"44"}}
{"__type":"$$EventMessageRunner","id":"a84db632-bf60-4329-909a-bc4b8d933813","ts":"2026-02-14T19:30:00.806-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"ZU1z_J3D","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"44"}}
{"__type":"$$EventMessageNode","id":"f981aed6-f748-43b5-87cd-161795645db7","ts":"2026-02-14T19:30:00.806-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"44","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageWorkflow","id":"53b8d2bb-1fb2-47d7-8f24-256da88a2f03","ts":"2026-02-14T19:30:00.807-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"44","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"8f72b6cf-2577-449a-a839-273a62670c50","ts":"2026-02-14T19:31:28.463-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"45","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"da087b62-593f-4b12-bff1-20016766ec68","ts":"2026-02-14T19:31:28.464-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"b3c44f86-923b-4b3d-97f7-59f5c94355c9","ts":"2026-02-14T19:31:28.465-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"c24d54e0-7cb7-4446-ba2f-48c00edf4413","ts":"2026-02-14T19:31:28.467-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"k-76NVt4","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"45"}}
{"__type":"$$EventMessageRunner","id":"d9ec34cd-b0ff-4faf-a2eb-830ec07ff68b","ts":"2026-02-14T19:31:28.514-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"k-76NVt4","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"45"}}
{"__type":"$$EventMessageNode","id":"ae7fa026-e449-4377-ae1a-5f7c58bc428b","ts":"2026-02-14T19:31:28.515-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"a85b7b11-7b44-407c-a4ef-51f55ade6abc","ts":"2026-02-14T19:31:28.521-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"8f6a0d31-1b13-44b2-851c-c0948c7b5fd8","ts":"2026-02-14T19:31:28.557-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"68c1599a-a9f6-4700-9c01-a0123f90f922","ts":"2026-02-14T19:31:28.558-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"9a060c42-228f-4ef2-8f8d-abefc352e501","ts":"2026-02-14T19:31:28.561-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"d5787cda-0c96-426e-900a-685b7ec5ad08","ts":"2026-02-14T19:31:28.561-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"7133b1a0-1c8b-4e2b-b92e-3c0dca5ee586","ts":"2026-02-14T19:31:28.562-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"vy40OpXM","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"45"}}
{"__type":"$$EventMessageRunner","id":"6c1ff876-18df-4fdd-81b4-e7a8a6c4e08b","ts":"2026-02-14T19:31:28.578-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"vy40OpXM","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"45"}}
{"__type":"$$EventMessageNode","id":"8a50d35f-d1d0-41e1-8b23-9d14dd488218","ts":"2026-02-14T19:31:28.578-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"45","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageWorkflow","id":"665a45ac-57d4-4402-8874-bf65416cbb6c","ts":"2026-02-14T19:31:28.579-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"45","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"29071081-ba9a-4bc5-88d1-3a90a7e9334b","ts":"2026-02-14T19:31:29.212-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"646a23a2-cef3-4057-b8a4-8048575660a0","ts":"2026-02-14T19:35:08.748-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"9d179d52-c0c4-43f3-a5be-37221ae0f091","ts":"2026-02-14T19:35:08.898-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"46","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"ff977fef-b6f1-4319-8063-ede1431c90ef","ts":"2026-02-14T19:35:08.899-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"34c0ff62-c17c-49e7-8b2c-d68fcc622101","ts":"2026-02-14T19:35:08.900-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"e94f9203-fee5-4aaf-a5a0-00167cdb095e","ts":"2026-02-14T19:35:08.903-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"i4zWqeIN","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"46"}}
{"__type":"$$EventMessageRunner","id":"bb0e2e99-3498-4ed6-b949-8cd9f09575b1","ts":"2026-02-14T19:35:08.953-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"i4zWqeIN","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"46"}}
{"__type":"$$EventMessageNode","id":"1d693c3c-b973-443a-9612-e34fe2c7615f","ts":"2026-02-14T19:35:08.953-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"98000f5a-581c-4fba-8f12-1ab3fef78492","ts":"2026-02-14T19:35:08.958-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"e8d72597-307e-4857-b2f1-231489e1be5d","ts":"2026-02-14T19:35:08.995-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"78b15d82-830c-4cc6-8dae-75fec96ae7f5","ts":"2026-02-14T19:35:08.996-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"456217e2-847b-45d7-8376-988077cfef65","ts":"2026-02-14T19:35:08.996-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"5249826b-5e54-499b-87ef-99d9b44ef6f0","ts":"2026-02-14T19:35:08.997-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"73b3b670-6b34-4e63-980c-d5217c849b0e","ts":"2026-02-14T19:35:09.002-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"4qb7CrXF","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"46"}}
{"__type":"$$EventMessageRunner","id":"90e68362-6785-4de1-b9f4-b66c4fa696d3","ts":"2026-02-14T19:35:09.077-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"4qb7CrXF","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"46"}}
{"__type":"$$EventMessageNode","id":"4d21f751-b74f-415f-ae9e-78df1bc1ebac","ts":"2026-02-14T19:35:09.078-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"46","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageWorkflow","id":"b44613b4-2cfa-4a2b-b974-40b920cc03bd","ts":"2026-02-14T19:35:09.082-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"46","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"126cc823-d958-4119-bd94-d334f651f183","ts":"2026-02-14T19:36:20.863-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"671ef520-5053-40ed-9455-a13d22c952f4","ts":"2026-02-14T19:36:25.669-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"226571d0-a943-4024-8a77-bb94d66faadc","ts":"2026-02-14T19:37:08.386-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"cdb9579c-4cd0-4467-9730-7aaa67423e61","ts":"2026-02-14T19:37:14.414-05:00","eventName":"n8n.audit.workflow.updated","message":"n8n.audit.workflow.updated","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"97f284ca-74d6-41da-b193-3f30c5a64dfe","ts":"2026-02-14T19:37:48.390-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"47","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"00288d57-3c5c-4d87-91ab-91804fb858df","ts":"2026-02-14T19:37:48.390-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"cdf82349-62ea-44d5-8af7-3855c9994bcc","ts":"2026-02-14T19:37:48.391-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"be26c409-bb18-41c6-8881-766451a6ec53","ts":"2026-02-14T19:37:48.393-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"O1361oJS","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"47"}}
{"__type":"$$EventMessageRunner","id":"ac5cd0c6-9d51-4241-9e2f-c2d12bf31128","ts":"2026-02-14T19:37:48.442-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"O1361oJS","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"47"}}
{"__type":"$$EventMessageNode","id":"366c37c1-ac1d-4110-91ac-9389bee3ebe0","ts":"2026-02-14T19:37:48.442-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"1c37ed7f-0118-487f-a1f6-3ce7d6ce1ca1","ts":"2026-02-14T19:37:48.448-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"80a251da-f2f8-4879-9e51-4f392a9bc73e","ts":"2026-02-14T19:37:48.486-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"268c771f-39a6-4c2c-bbeb-20ba62b66a63","ts":"2026-02-14T19:37:48.487-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"3e2fd2a5-bb51-40bb-8c2c-4b5d82f18a31","ts":"2026-02-14T19:37:48.488-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"7acb6106-223d-4c36-8ec4-833e60dd9769","ts":"2026-02-14T19:37:48.488-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"bc2caefb-fb5c-4700-81b3-e1e7bab48c41","ts":"2026-02-14T19:37:48.489-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"G_oanKfN","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"47"}}
{"__type":"$$EventMessageRunner","id":"8c74ab0b-78af-4d35-b5cd-2282d1aa3874","ts":"2026-02-14T19:37:48.558-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"G_oanKfN","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"47"}}
{"__type":"$$EventMessageNode","id":"354bec36-df6f-4a8d-a62a-eea1f62e0e5b","ts":"2026-02-14T19:37:48.559-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageNode","id":"b87c3eda-ac7c-48a9-8247-fadc1230a458","ts":"2026-02-14T19:37:48.561-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"2d92f41f-baea-47ac-9340-54b40ad343a6"}}
{"__type":"$$EventMessageNode","id":"02b81ec5-8027-4a65-bdd4-18750dc8a1cc","ts":"2026-02-14T19:37:48.684-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"47","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"2d92f41f-baea-47ac-9340-54b40ad343a6"}}
{"__type":"$$EventMessageWorkflow","id":"990d53ac-8cc6-48b8-b2b7-5ce0da736501","ts":"2026-02-14T19:37:48.686-05:00","eventName":"n8n.workflow.failed","message":"n8n.workflow.failed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"47","success":false,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","lastNodeExecuted":"Execute a SQL query","errorNodeType":"n8n-nodes-base.postgres","errorMessage":"permission denied for table catalog_discovery"}}
{"__type":"$$EventMessageWorkflow","id":"f93ba421-f604-448a-ad1c-e79758e92b86","ts":"2026-02-14T19:40:00.381-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"48","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"3d4cb2a2-8983-4dc3-86de-5371ff056c42","ts":"2026-02-14T19:40:00.381-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"043ae25e-7d8d-4e68-8ff6-8995dd1c2671","ts":"2026-02-14T19:40:00.382-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"c656c676-2121-4cb4-bf96-eb6e5d8f79b1","ts":"2026-02-14T19:40:00.384-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"hv6uWpW3","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"48"}}
{"__type":"$$EventMessageRunner","id":"675a5d55-8a72-4b2f-9bff-2e19f96222e5","ts":"2026-02-14T19:40:00.428-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"hv6uWpW3","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"48"}}
{"__type":"$$EventMessageNode","id":"01a9cf62-4fa1-43ad-8186-91e2a962c6e8","ts":"2026-02-14T19:40:00.428-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"df9e7132-c40a-4fa9-9347-5a3c0fa09eb3","ts":"2026-02-14T19:40:00.434-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"5fce15d1-ba5e-4f2b-b3b0-09ed241372af","ts":"2026-02-14T19:40:00.470-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"75edd6f4-6424-4201-ac21-44d4512078f2","ts":"2026-02-14T19:40:00.470-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"fc5b52c4-03e1-436d-a384-fe37b3dec44b","ts":"2026-02-14T19:40:00.472-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"eda6d295-d4c4-457a-a317-1c375549433f","ts":"2026-02-14T19:40:00.473-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"d01c4667-a4af-4a29-a7c1-08ebcced3313","ts":"2026-02-14T19:40:00.474-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"xhOSvWvS","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"48"}}
{"__type":"$$EventMessageRunner","id":"a2e5c94f-caec-4f3d-abfa-6377cad3cad9","ts":"2026-02-14T19:40:00.531-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"xhOSvWvS","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"48"}}
{"__type":"$$EventMessageNode","id":"10b60953-0422-49db-b82a-c6a3ef2dd452","ts":"2026-02-14T19:40:00.532-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageNode","id":"e773e4cb-58ef-428e-b1ae-98c641726fbc","ts":"2026-02-14T19:40:00.534-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"2d92f41f-baea-47ac-9340-54b40ad343a6"}}
{"__type":"$$EventMessageNode","id":"7692b2bf-811f-4811-9a8f-7e7cd57d758f","ts":"2026-02-14T19:40:00.664-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"48","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"2d92f41f-baea-47ac-9340-54b40ad343a6"}}
{"__type":"$$EventMessageWorkflow","id":"60152d8b-2d17-4695-a31f-b1ed659afc5a","ts":"2026-02-14T19:40:00.668-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"48","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageWorkflow","id":"14339d78-0184-45ee-b954-bee60af83d19","ts":"2026-02-14T19:40:57.346-05:00","eventName":"n8n.workflow.started","message":"n8n.workflow.started","payload":{"executionId":"49","workflowId":"g3yuktNVnNC6OqUt","isManual":false,"workflowName":"Market Discovery Pipeline"}}
{"__type":"$$EventMessageAudit","id":"39ec9840-29c1-4d41-ab82-c81aea3c9c82","ts":"2026-02-14T19:40:57.346-05:00","eventName":"n8n.audit.workflow.executed","message":"n8n.audit.workflow.executed","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","_email":"kincses@gmail.com","_firstName":"Zsolt","_lastName":"Gyongyossy","globalRole":"global:owner","workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","source":"user-manual"}}
{"__type":"$$EventMessageNode","id":"2c91cee5-7733-473a-9b33-063bec8a8cf4","ts":"2026-02-14T19:40:57.347-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageRunner","id":"733dcdfb-949b-4dd5-b11b-b8760572927e","ts":"2026-02-14T19:40:57.349-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"zNrKb4XJ","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"49"}}
{"__type":"$$EventMessageRunner","id":"176fb88d-c371-4e7b-b803-39726430732e","ts":"2026-02-14T19:40:57.396-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"zNrKb4XJ","nodeId":"396b714c-a809-4623-974c-06173c445c64","workflowId":"g3yuktNVnNC6OqUt","executionId":"49"}}
{"__type":"$$EventMessageNode","id":"507318b3-d7b9-486f-becb-c572442d3544","ts":"2026-02-14T19:40:57.396-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript","nodeId":"396b714c-a809-4623-974c-06173c445c64"}}
{"__type":"$$EventMessageNode","id":"06df90a8-dc85-47e6-a2d9-7cdb5601f8db","ts":"2026-02-14T19:40:57.403-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"51f1cfae-4773-4851-91f5-22b97acc84cb","ts":"2026-02-14T19:40:57.440-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.html","nodeName":"HTML","nodeId":"12a5aa30-9500-4808-9d81-83682ba1a990"}}
{"__type":"$$EventMessageNode","id":"106ad46e-7d82-45f6-bfec-56015e099336","ts":"2026-02-14T19:40:57.441-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"9ae3bf81-ce6f-4f30-b141-84a081a6efa8","ts":"2026-02-14T19:40:57.441-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.splitOut","nodeName":"Split Out","nodeId":"8e0871e2-af94-4a6d-9b44-76e732b51a15"}}
{"__type":"$$EventMessageNode","id":"2ee88d59-be92-4297-91be-d1f32328c31f","ts":"2026-02-14T19:40:57.442-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageRunner","id":"d19926bc-9bd6-4402-a8f2-050ed54b099b","ts":"2026-02-14T19:40:57.443-05:00","eventName":"n8n.runner.task.requested","message":"n8n.runner.task.requested","payload":{"taskId":"5wxIWF7i","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"49"}}
{"__type":"$$EventMessageRunner","id":"6d21e237-e8d1-4d70-85c3-bae5f25dc585","ts":"2026-02-14T19:40:57.512-05:00","eventName":"n8n.runner.response.received","message":"n8n.runner.response.received","payload":{"taskId":"5wxIWF7i","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408","workflowId":"g3yuktNVnNC6OqUt","executionId":"49"}}
{"__type":"$$EventMessageNode","id":"f1863211-22d8-4d12-9a71-eca6fcd55804","ts":"2026-02-14T19:40:57.513-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.code","nodeName":"Code in JavaScript1","nodeId":"78726a29-919c-451d-a4a2-e9a76aa46408"}}
{"__type":"$$EventMessageNode","id":"4dca0bde-fb2a-4a40-96c3-a29dab684206","ts":"2026-02-14T19:40:57.514-05:00","eventName":"n8n.node.started","message":"n8n.node.started","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"2d92f41f-baea-47ac-9340-54b40ad343a6"}}
{"__type":"$$EventMessageNode","id":"2e82f24c-37a6-4f79-87f4-3be1a0cdb2ed","ts":"2026-02-14T19:40:57.630-05:00","eventName":"n8n.node.finished","message":"n8n.node.finished","payload":{"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline","executionId":"49","nodeType":"n8n-nodes-base.postgres","nodeName":"Execute a SQL query","nodeId":"2d92f41f-baea-47ac-9340-54b40ad343a6"}}
{"__type":"$$EventMessageWorkflow","id":"655184c6-e578-42d7-8c8a-df700ef9feca","ts":"2026-02-14T19:40:57.633-05:00","eventName":"n8n.workflow.success","message":"n8n.workflow.success","payload":{"userId":"07cbc1c9-f562-4c77-88d0-c0c8faff3e82","executionId":"49","success":true,"isManual":true,"workflowId":"g3yuktNVnNC6OqUt","workflowName":"Market Discovery Pipeline"}}

File diff suppressed because it is too large Load Diff

18
vehicle.modelfile Normal file
View File

@@ -0,0 +1,18 @@
FROM qwen2.5:7b
# Alacsony hőmérséklet = maximális precizitás
PARAMETER temperature 0.1
SYSTEM """
Te egy autóipari adat-kinyerő robot vagy.
A feladatod, hogy a megadott szövegből kinyerd a technikai adatokat.
KIZÁRÓLAG tiszta JSON-ben válaszolhatsz. Ne írj magyarázatot.
A várt formátum:
{
"ccm": szám vagy null,
"kw": szám vagy null,
"euro_class": szám vagy null,
"fuel_type": "Benzin" | "Dízel" | "Elektromos" | null
}
"""