Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok
This commit is contained in:
131
backend/app/workers/ocr/robot_1_ocr_processor.py
Executable file
131
backend/app/workers/ocr/robot_1_ocr_processor.py
Executable 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())
|
||||
Reference in New Issue
Block a user