# /opt/docker/dev/service_finder/backend/discovery_bot.py import asyncio import json import httpx import os import hashlib import logging from urllib.parse import quote from sqlalchemy import select from app.database import AsyncSessionLocal from app.models.staged_data import ServiceStaging # Logolás beállítása logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s]: %(message)s') logger = logging.getLogger("OSM-Discovery") # Konfiguráció HUNGARY_BBOX = "45.7,16.1,48.6,22.9" OVERPASS_URL = "http://overpass-api.de/api/interpreter?data=" class OSMDiscoveryBot: @staticmethod def generate_fingerprint(name: str, city: str) -> str: """ Ujjlenyomat generálása a deduplikációhoz. Kicsit lazább, mint a Hunter-nél, mert az OSM címadatok néha hiányosak. """ raw = f"{str(name).lower()}|{str(city).lower()}" return hashlib.md5(raw.encode()).hexdigest() @staticmethod def get_service_type(tags: dict, name: str) -> str: """ OSM tagek leképezése belső kategóriákra. """ name = name.lower() shop = tags.get('shop', '') amenity = tags.get('amenity', '') if shop == 'tyres' or 'gumi' in name: return 'tire_shop' if amenity == 'car_wash' or 'mosó' in name: return 'car_wash' if any(x in name for x in ['villamos', 'autóvill', 'elektro']): return 'electrician' if any(x in name for x in ['fényez', 'lakatos', 'karosszéria']): return 'body_shop' return 'mechanic' async def fetch_osm_data(self, query_part: str): """ Aszinkron adatgyűjtés az Overpass API-tól. """ query = f'[out:json][timeout:120];(node{query_part}({HUNGARY_BBOX});way{query_part}({HUNGARY_BBOX}););out center;' async with httpx.AsyncClient(timeout=150) as client: try: resp = await client.get(OVERPASS_URL + quote(query)) if resp.status_code == 200: return resp.json().get('elements', []) return [] except Exception as e: logger.error(f"❌ Overpass hiba: {e}") return [] async def sync(self): logger.info("🛰️ OSM Országos szinkronizáció indítása...") # 1. Lekérdezések összeállítása queries = [ '["shop"~"car_repair|tyres"]', '["amenity"="car_wash"]' ] all_elements = [] for q in queries: elements = await self.fetch_osm_data(q) all_elements.extend(elements) logger.info(f"📊 {len(all_elements)} potenciális szervizpont érkezett.") async with AsyncSessionLocal() as db: added_count = 0 for node in all_elements: tags = node.get('tags', {}) if not tags.get('name'): continue lat = node.get('lat', node.get('center', {}).get('lat')) lon = node.get('lon', node.get('center', {}).get('lon')) name = tags.get('name', tags.get('operator', 'Ismeretlen szerviz')) city = tags.get('addr:city', 'Ismeretlen') street = tags.get('addr:street', '') housenumber = tags.get('addr:housenumber', '') f_print = self.generate_fingerprint(name, city) # Deduplikáció ellenőrzése stmt = select(ServiceStaging).where(ServiceStaging.fingerprint == f_print) existing = (await db.execute(stmt)).scalar_one_or_none() if not existing: db.add(ServiceStaging( name=name, source="osm_discovery_v2", fingerprint=f_print, city=city, full_address=f"{city}, {street} {housenumber}".strip(", "), status="pending", trust_score=20, # Az OSM adatokat alacsonyabb bizalommal kezeljük, mint a Google-t raw_data=tags )) added_count += 1 await db.commit() logger.info(f"✅ Szinkron kész. {added_count} új elem került a Staging táblába.") if __name__ == "__main__": bot = OSMDiscoveryBot() asyncio.run(bot.sync())