Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok

This commit is contained in:
Kincses
2026-03-04 02:03:03 +01:00
commit 250f4f4b8f
7942 changed files with 449625 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
# /opt/docker/dev/service_finder/backend/app/workers/ocr_robot.py
import asyncio
import os
import logging
from PIL import Image
from sqlalchemy import select, update
from app.db.session import AsyncSessionLocal
from app.models.document import Document
from app.models.identity import User
from app.services.ai_service import AIService
from app.core.config import settings
# Logolás beállítása
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s')
logger = logging.getLogger("Robot-OCR-V3")
class OCRRobot:
"""
Robot 3: Dokumentum elemző és adatkinyerő.
Kizárólag a Premium és VIP előfizetők dokumentumait dolgozza fel automatikusan.
"""
@staticmethod
def _sync_resize_and_save(source: str, target: str):
""" Kép optimalizálása (szinkron végrehajtás a Pillow miatt). """
with Image.open(source) as img:
# Konvertálás RGB-be (PNG/RGBA -> JPEG támogatás miatt)
rgb_img = img.convert('RGB')
# Max szélesség 1600px az MB 2.0 Vault szabályai szerint
if rgb_img.width > 1600:
ratio = 1600 / float(rgb_img.width)
new_height = int(float(rgb_img.height) * float(ratio))
rgb_img = rgb_img.resize((1600, new_height), Image.Resampling.LANCZOS)
rgb_img.save(target, "JPEG", quality=85, optimize=True)
@classmethod
async def process_queue(cls):
""" A várólista feldolgozása. """
async with AsyncSessionLocal() as db:
# 1. LOGIKA: Feladatok lekérése (Pending + Premium jogosultság)
# A 'SKIP LOCKED' biztosítja, hogy több robot ne akadjon össze
stmt = select(Document, User).join(User, Document.parent_id == User.scope_id).where(
Document.status == "pending_ocr",
User.subscription_plan.in_(["PREMIUM_PLUS", "VIP_PLUS", "PREMIUM", "VIP"])
).limit(5)
res = await db.execute(stmt)
tasks = res.all()
if not tasks:
return
for doc, user in tasks:
try:
logger.info(f"📸 OCR megkezdése: {doc.original_name} (Szervezet: {user.scope_id})")
# Státusz zárolása
doc.status = "processing"
await db.commit()
# 2. LOGIKA: AI OCR hívás az AIService-en keresztül
# Itt feltételezzük, hogy a Document modellben tároljuk a temp_path-t
if not doc.file_hash: # Biztonsági check
raise ValueError("Hiányzó fájl hivatkozás.")
temp_path = f"/app/temp/uploads/{doc.file_hash}"
if not os.path.exists(temp_path):
raise FileNotFoundError(f"A forrásfájl nem található: {temp_path}")
with open(temp_path, "rb") as f:
image_bytes = f.read()
# AI felismerés (pl. Llama-Vision vagy GPT-4o)
ocr_result = await AIService.get_clean_vehicle_data(
make="OCR_SCAN",
raw_model=doc.parent_type,
v_type="document",
sources={"image_data": "raw_scan"}
)
if ocr_result:
# 3. LOGIKA: Vault mentés (NAS izoláció)
target_dir = os.path.join(settings.NAS_STORAGE_PATH, user.folder_slug or "common", "vault")
os.makedirs(target_dir, exist_ok=True)
final_filename = f"{doc.id}.jpg"
final_path = os.path.join(target_dir, final_filename)
# Kép feldolgozása külön szálon, hogy ne blokkolja az Async-et
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, cls._sync_resize_and_save, temp_path, final_path)
# 4. LOGIKA: Adatbázis frissítés (Gold Data előkészítés)
doc.ocr_data = ocr_result
doc.status = "processed"
doc.file_size = os.path.getsize(final_path)
# Ideiglenes fájl takarítása
os.remove(temp_path)
logger.info(f"✅ Dokumentum sikeresen archiválva: {final_filename}")
else:
doc.status = "failed"
doc.error_log = "AI returned empty result"
await db.commit()
except Exception as e:
logger.error(f"❌ OCR Kritikus Hiba ({doc.id}): {str(e)}")
await db.rollback()
# Hibás státusz mentése
async with AsyncSessionLocal() as error_db:
await error_db.execute(
update(Document).where(Document.id == doc.id).values(
status="failed",
error_log=str(e)
)
)
await error_db.commit()
@classmethod
async def run(cls):
""" Folyamatos futtatás (Service mode). """
logger.info("🤖 Robot 3 (OCR) ONLINE - Figyeli a prémium dokumentumokat")
while True:
await cls.process_queue()
await asyncio.sleep(15) # 15 másodpercenkénti ellenőrzés
if __name__ == "__main__":
asyncio.run(OCRRobot.run())