STABLE: Final schema sync, optimized gitignore
This commit is contained in:
@@ -1,83 +1,123 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/workers/robot0_priority_setter.py
|
||||
import asyncio
|
||||
import httpx
|
||||
import logging
|
||||
import os
|
||||
from sqlalchemy import text
|
||||
from app.db.session import SessionLocal
|
||||
from app.db.session import AsyncSessionLocal
|
||||
|
||||
# Logolás beállítása a Sentinel rendszerhez
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s]: %(message)s')
|
||||
logger = logging.getLogger("Robot-0-Strategist")
|
||||
|
||||
class Robot0Strategist:
|
||||
"""
|
||||
Robot 0: A Stratéga.
|
||||
Meghatározza a feldolgozási prioritásokat a valós piaci darabszámok alapján.
|
||||
"""
|
||||
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
|
||||
# Holland típusok leképezése belső kategóriákra (MB 2.0 prioritás)
|
||||
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')"]}
|
||||
# JAVÍTVA: Bedrijfsauto hozzáadva, Bedrijfswagen törölve
|
||||
{"name": "truck", "rdw_types": ["'Bedrijfsauto'", "'Vrachtwagen'", "'Opleggertrekker'"]},
|
||||
{"name": "other", "rdw_types": ["NOT IN ('Personenauto', 'Motorfiets', 'Bedrijfsauto', '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
|
||||
async def get_popular_makes(self, vehicle_class: str, rdw_types: list):
|
||||
""" Lekéri az adott kategória 500 legnépszerűbb márkáját. """
|
||||
|
||||
# SoQL filter összeállítása
|
||||
if "NOT IN" in rdw_types[0]:
|
||||
type_filter = f"voertuigsoort {rdw_types[0]}"
|
||||
else:
|
||||
type_filter = " OR ".join([f"voertuigsoort = {t}" for t in rdw_types])
|
||||
|
||||
params = {
|
||||
"$select": "merk, count(*)",
|
||||
"$select": "merk, count(*) AS darabszam", # Itt adjuk meg az aliast
|
||||
"$where": type_filter,
|
||||
"$group": "merk",
|
||||
"$order": "count DESC",
|
||||
"$limit": 500 # Kategóriánként az 500 legfontosabb márka bőven elég
|
||||
"$order": "darabszam DESC", # Itt hivatkozunk rá
|
||||
"$limit": 500
|
||||
}
|
||||
|
||||
async with httpx.AsyncClient(timeout=30) as client:
|
||||
async with httpx.AsyncClient(timeout=45.0) as client:
|
||||
try:
|
||||
resp = await client.get(self.RDW_API, params=params, headers=self.HEADERS)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
logger.error(f"⚠️ API Hiba ({vehicle_class}): {resp.status_code}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Hiba a {vehicle_class} lekérdezésekor: {e}")
|
||||
logger.error(f"❌ Kapcsolati hiba ({vehicle_class}): {e}")
|
||||
return []
|
||||
|
||||
async def run(self):
|
||||
logger.info("🚀 Robot 0 (Strategist) INDUL - Piaci alapú sorrend felállítása...")
|
||||
""" A stratégiai prioritás-beállítás futtatása. """
|
||||
logger.info("🚀 Robot 0 (Strategist) INDUL - Piaci prioritások elemzése...")
|
||||
|
||||
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
|
||||
|
||||
# --- ÖNGYÓGYÍTÓ ADATBÁZIS JAVÍTÁS ---
|
||||
# Garantáljuk, hogy a priority_score oszlop létezik a táblában
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
await db.execute(text("ALTER TABLE data.catalog_discovery ADD COLUMN IF NOT EXISTS priority_score INTEGER DEFAULT 0;"))
|
||||
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("✅ Adatbázis séma ellenőrizve: priority_score oszlop aktív.")
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"⚠️ Nem sikerült ellenőrizni az oszlopot: {e}")
|
||||
# ------------------------------------
|
||||
|
||||
# Nem nyitunk itt globális db-t, hanem a cikluson belül kezeljük
|
||||
for category in self.CATEGORIES:
|
||||
v_class = category["name"]
|
||||
logger.info(f"📊 {v_class.upper()} elemzés és sorbarendezés...")
|
||||
|
||||
makes = await self.get_popular_makes(v_class, category["rdw_types"])
|
||||
|
||||
if not makes:
|
||||
logger.warning(f"⚠️ {v_class.upper()}: Nincs visszaadott adat az RDW-től!")
|
||||
continue
|
||||
|
||||
logger.info("🏁 Robot 0 végzett. A Discovery tábla készen áll a Robot 1 (Hunter) számára!")
|
||||
added_count = 0
|
||||
for item in makes:
|
||||
make_name = str(item.get("merk", "")).upper().strip()
|
||||
if not make_name:
|
||||
continue
|
||||
|
||||
count = int(item.get("darabszam", 0))
|
||||
|
||||
# DEBUG: ellenőrizzük az 'item'-et
|
||||
if added_count == 0:
|
||||
logger.info(f"🧬 Elem felépítése: {item} -> Kinyert márka: {make_name}, Prioritás: {count}")
|
||||
|
||||
# Minden egyes márkához saját session-t nyitunk
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
# JAVÍTÁS: beletettük az attempts (0) és a priority_score (:score) oszlopokat!
|
||||
query = text("""
|
||||
INSERT INTO data.catalog_discovery (make, model, vehicle_class, status, source, attempts, priority_score)
|
||||
VALUES (:make, 'ALL_VARIANTS', :class, 'pending', 'STRATEGIST-POPULARITY-V2', 0, :score)
|
||||
ON CONFLICT (make, model, vehicle_class)
|
||||
DO UPDATE SET status = 'pending', priority_score = :score
|
||||
WHERE catalog_discovery.status NOT IN ('processed', 'in_progress');
|
||||
""")
|
||||
|
||||
# Átadjuk a query-nek a 'score' paramétert is
|
||||
await db.execute(query, {"make": make_name, "class": v_class, "score": count})
|
||||
await db.commit()
|
||||
added_count += 1
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.warning(f"❌ Sikertelen rögzítés ({make_name}): {e}")
|
||||
|
||||
logger.info(f"✅ {v_class.upper()} kész: {added_count} márka prioritizálva.")
|
||||
|
||||
logger.info("🏁 Robot 0 végzett. A terep előkészítve a Hunterek számára.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(Robot0Strategist().run())
|
||||
Reference in New Issue
Block a user