STABLE: Final schema sync, optimized gitignore

This commit is contained in:
Kincses
2026-02-26 08:19:25 +01:00
parent 893f39fa15
commit 505543330a
203 changed files with 11590 additions and 9542 deletions

View File

@@ -1,117 +1,137 @@
# /opt/docker/dev/service_finder/backend/app/workers/researcher_v2_1.py
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 datetime import datetime, timezone
from typing import Optional, List
from sqlalchemy import select, update, and_, func, or_, case
from app.db.session import AsyncSessionLocal
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
# DuckDuckGo search API hiba-elnyomás és import
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
# Logolás beállítása
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s')
logger = logging.getLogger("Robot-Researcher-v2.1")
class ResearcherBot:
"""
Robot 2.1: Az internet porszívója.
Technikai adatokat gyűjt (DuckDuckGo), hogy előkészítse az AI dúsítást.
Kihasználja a motorkódot és a gyártási évet a pontosabb találatokért.
"""
def __init__(self):
self.batch_size = 15
self.max_parallel_queries = 5
self.batch_size = 5 # Egyszerre 5 járművet vesz ki
self.max_parallel_queries = 3 # Párhuzamos keresések száma
async def fetch_source(self, label, query):
"""Egyedi forrás lekérése a DuckDuckGo-tól."""
async def fetch_source(self, label: str, query: str) -> str:
""" Egyedi forrás lekérése szálbiztos módon. """
try:
def search():
# Az újabb verziókban a DDGS() hívás így a legstabilabb
with DDGS() as ddgs:
# Az első 3 találat body részét gyűjtjük be kontextusnak
results = ddgs.text(query, max_results=3)
return [r['body'] for r in results] if results else []
return [f"[{r.get('title', 'No Title')}] {r.get('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"
return f"=== SOURCE: {label} | STATUS: EMPTY ===\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"
logger.error(f"❌ Keresési hiba ({label}): {str(e)}")
return f"=== SOURCE: {label} | ERROR: {str(e)} ===\n\n"
async def research_vehicle(self, vehicle_id):
async with SessionLocal() as db:
async def research_vehicle(self, vehicle_id: int):
""" Egyetlen jármű teljes körű átvilágítása. """
async with AsyncSessionLocal() 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'))
make = v.make
model = v.marketing_name
engine = v.engine_code or ""
year = f"{v.year_from}" if v.year_from else ""
# Státusz zárolása
v.status = 'research_in_progress'
await db.commit()
logger.info(f"🔎 Kutatás indul: {make} {model}")
logger.info(f"🔎 Kutatás indul: {make} {model} (Motor: {engine}, Év: {year})")
# Célzott keresési kulcsszavak (Multi-Channel stratégia)
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")
("TECH_SPECS", f"{make} {model} {engine} {year} technical specifications engine power kw torque"),
("MAINTENANCE", f"{make} {model} {engine} oil capacity coolant transmission fluid type capacity"),
("TIRES_PROD", f"{make} {model} {year} tire size load index production years status")
]
# Párhuzamos forrásgyűjtés
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()
async with AsyncSessionLocal() as db:
if len(full_context.strip()) > 200: # Ha van elegendő kontextus
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == vehicle_id)
.values(
raw_search_context=full_context,
status='awaiting_ai_synthesis', # Átadás a Robot 2.2-nek
last_research_at=func.now(),
attempts=VehicleModelDefinition.attempts + 1
)
)
)
logger.info(f"✅ Kontextus rögzítve: {make} {model}")
else:
# Sikertelen keresés, visszatesszük később
await db.execute(
update(VehicleModelDefinition)
.where(VehicleModelDefinition.id == vehicle_id)
.values(
status='unverified',
attempts=VehicleModelDefinition.attempts + 1,
last_research_at=func.now()
)
)
logger.warning(f"⚠️ Kevés adat: {make} {model} - Újrapróbálkozás később")
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")
logger.info("🚀 Robot 2.1 (Researcher) ONLINE - Cél: 407 Toyota feldolgozása")
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
async with AsyncSessionLocal() as db:
# Prioritás: unverified autók előre
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
(VehicleModelDefinition.make == 'TOYOTA', 1),
else_=2
)
stmt = select(VehicleModelDefinition.id).where(
or_(VehicleModelDefinition.status == 'unverified', VehicleModelDefinition.status == 'awaiting_research')
).order_by(priorities).limit(self.batch_size)
or_(VehicleModelDefinition.status == 'unverified',
VehicleModelDefinition.status == 'awaiting_research')
).order_by(priorities, VehicleModelDefinition.attempts.asc()).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)
await asyncio.sleep(30)
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)
# Szekvenciális feldolgozás a rate-limit miatt
for rid in ids:
await self.research_vehicle(rid)
await asyncio.sleep(5) # 5 másodperc szünet a keresések között
if __name__ == "__main__":
asyncio.run(ResearcherBot().run())