chore: Archive legacy docs and backup files, prepare for codebase cleanup v2.0

This commit is contained in:
2026-02-18 00:11:50 +00:00
parent b11b9bce87
commit 5757754aae
94 changed files with 405 additions and 0 deletions

View File

@@ -1,18 +0,0 @@
import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
def send_verification_email(to_email: str, token: str):
message = Mail(
from_email='noreply@servicefinder.pro', # Ezt majd igazítsd a SendGrid verified senderhez
to_emails=to_email,
subject='Service Finder - Regisztráció megerősítése',
html_content=f'<h3>Üdvözöljük a Service Finderben!</h3><p>A regisztráció befejezéséhez kattintson az alábbi linkre:</p><p><a href="https://servicefinder.pro/verify?token={token}">Megerősítem a regisztrációmat</a></p>'
)
try:
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
response = sg.send(message)
return True
except Exception as e:
print(f"Email hiba: {e}")
return False

View File

@@ -1,24 +0,0 @@
# /opt/docker/dev/service_finder/backend/app/core/security.py
from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any
import bcrypt
from jose import jwt
from app.core.config import settings
def verify_password(plain_password: str, hashed_password: str) -> bool:
if not hashed_password:
return False
return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
def get_password_hash(password: str) -> str:
salt = bcrypt.gensalt()
return bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8")
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)

View File

@@ -1,18 +0,0 @@
from sqlalchemy import Column, String, JSON, Boolean, DateTime, Integer, text
from sqlalchemy.sql import func
from app.db.base_class import Base
class SystemParameter(Base):
"""
Rendszerszintű dinamikus paraméterek tárolása.
Szinkronban az admin.py és config.py elvárásaival.
"""
__tablename__ = "system_parameters"
__table_args__ = {"schema": "data"}
# Az admin.py 'key' mezőt vár, nem 'key_name'-et!
key = Column(String(50), primary_key=True, index=True)
value = Column(JSON, server_default=text("'{}'::jsonb"), nullable=False)
description = Column(String(255), nullable=True)
is_active = Column(Boolean, default=True)
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

View File

@@ -1,116 +0,0 @@
import os
import json
import logging
import asyncio
from typing import Dict, Any, Optional
from google import genai
from google.genai import types
from sqlalchemy import select
from app.db.session import SessionLocal
from app.models import SystemParameter
logger = logging.getLogger("AI-Service")
class AIService:
"""
AI Service v1.2.4 - Production Ready
- Robot 2 (Technical Enrichment) & Robot 3 (OCR)
- Fix: JSON response cleaning and array-to-dict transformation.
"""
api_key = os.getenv("GEMINI_API_KEY")
client = genai.Client(api_key=api_key) if api_key else None
PRIMARY_MODEL = "gemini-2.0-flash"
@classmethod
async def get_config_delay(cls) -> float:
"""Lekéri az adminisztrálható késleltetést az adatbázisból."""
try:
async with SessionLocal() as db:
stmt = select(SystemParameter).where(SystemParameter.key == "AI_REQUEST_DELAY")
res = await db.execute(stmt)
param = res.scalar_one_or_none()
return float(param.value) if param else 1.0
except Exception:
return 1.0
@classmethod
async def get_clean_vehicle_data(cls, make: str, raw_model: str, v_type: str) -> Optional[Dict[str, Any]]:
"""Robot 2: Gépjármű technikai adatok dúsítása."""
if not cls.client:
return None
await asyncio.sleep(await cls.get_config_delay())
prompt = f"""
Jármű: {make} {raw_model} ({v_type}).
Adj technikai adatokat JSON formátumban.
FONTOS: A 'technical_code' mező NEM lehet üres. Ha nem tudod a gyári kódot, adj 'N/A' értéket!
Várt struktúra:
{{
"marketing_name": "tiszta marketing név",
"technical_code": "gyári kód vagy N/A",
"ccm": egész szám,
"kw": egész szám,
"maintenance": {{
"oil_type": "viszkozitás",
"oil_qty": tizedes tört literben,
"spark_plug": "gyertya típus",
"coolant": "hűtőfolyadék"
}}
}}
"""
config = types.GenerateContentConfig(
system_instruction="Profi gépjárműtechnikus vagy. Kizárólag tiszta JSON-t válaszolsz.",
response_mime_type="application/json",
temperature=0.1
)
try:
response = cls.client.models.generate_content(model=cls.PRIMARY_MODEL, contents=prompt, config=config)
res_json = json.loads(response.text)
if isinstance(res_json, list) and len(res_json) > 0:
res_json = res_json[0]
return res_json if isinstance(res_json, dict) else None
except Exception as e:
logger.error(f"❌ AI hiba ({make} {raw_model}): {e}")
return None
@classmethod
async def analyze_document_image(cls, image_data: bytes, doc_type: str) -> Optional[Dict[str, Any]]:
"""Robot 3: Multimodális OCR elemzés (Képbeolvasás)."""
if not cls.client:
return None
await asyncio.sleep(await cls.get_config_delay())
prompts = {
"identity": "Személyes okmány adatok.",
"vehicle_reg": "Rendszám, alvázszám, technikai adatok.",
"invoice": "Számla adatok, összegek, dátumok.",
"odometer": "Csak a kilométeróra állása számként."
}
config = types.GenerateContentConfig(
system_instruction="Profi OCR dokumentum-elemző vagy. Csak tiszta JSON-t válaszolsz.",
response_mime_type="application/json"
)
try:
response = cls.client.models.generate_content(
model=cls.PRIMARY_MODEL,
contents=[
f"Elemezd ezt a képet ({doc_type}): {prompts.get(doc_type, '')}",
types.Part.from_bytes(data=image_data, mime_type="image/jpeg")
],
config=config
)
res_json = json.loads(response.text)
if isinstance(res_json, list) and len(res_json) > 0:
res_json = res_json[0]
return res_json if isinstance(res_json, dict) else None
except Exception as e:
logger.error(f"❌ AI OCR hiba ({doc_type}): {e}")
return None

View File

@@ -1,102 +0,0 @@
import asyncio
import httpx
import logging
import os # <--- EZ HIÁNYZOTT!
import datetime
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from app.db.session import SessionLocal
from app.models.vehicle_definitions import VehicleModelDefinition
from app.services.ai_service import AIService
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("Robot-v1.2.4-Fixed")
class TechEnricher:
API_URL = "https://opendata.rdw.nl/resource/kyri-nuah.json"
RDW_TOKEN = os.getenv("RDW_APP_TOKEN")
HEADERS = {"X-App-Token": RDW_TOKEN} if RDW_TOKEN else {}
@classmethod
async def fetch_rdw_tech_data(cls, make, model):
"""Hibatűrő RDW lekérdezés tisztított paraméterekkel."""
clean_model = str(model).strip().upper()
params = {"merk": make.upper(), "handelsbenaming": clean_model, "$limit": 1}
async with httpx.AsyncClient(headers=cls.HEADERS) as client:
try:
resp = await client.get(cls.API_URL, params=params, timeout=15)
if resp.status_code == 200 and resp.json():
return resp.json()[0]
return None
except Exception:
return None
@classmethod
async def run(cls):
logger.info("🚀 Master Enricher INDUL (Atomi mentés üzemmód)...")
# 1. Csak az ID-kat kérjük le, hogy ne tartsuk nyitva a tranzakciót feleslegesen
async with SessionLocal() as main_db:
stmt = select(VehicleModelDefinition.id).where(
VehicleModelDefinition.status == "unverified"
).limit(50)
res = await main_db.execute(stmt)
ids = res.scalars().all()
if not ids:
logger.info("😴 Nincs dúsítandó adat.")
return
# 2. Egyesével dolgozzuk fel a rekordokat saját session-ben
for m_id in ids:
async with SessionLocal() as db:
try:
master = await db.get(VehicleModelDefinition, m_id)
if not master:
continue
logger.info(f"🧪 Feldolgozás: {master.make} {master.marketing_name} (ID: {m_id})")
data_found = False
# A: RDW fázis
rdw_data = await cls.fetch_rdw_tech_data(master.make, master.marketing_name)
if rdw_data:
master.engine_capacity = int(float(rdw_data.get("cilinderinhoud", 0))) or None
master.power_kw = int(float(rdw_data.get("netto_maximum_vermogen_kw", 0))) or None
data_found = True
# B: AI fázis (ha hiányzik adat vagy pontosítani kell)
if not data_found or master.engine_capacity is None:
ai_data = await AIService.get_clean_vehicle_data(
master.make, master.marketing_name, master.vehicle_type
)
if ai_data:
master.marketing_name = ai_data.get("marketing_name", master.marketing_name)
master.technical_code = ai_data.get("technical_code") or master.technical_code or "N/A"
master.engine_capacity = ai_data.get("ccm") or master.engine_capacity
master.power_kw = ai_data.get("kw") or master.power_kw
master.specifications = ai_data.get("maintenance", {})
data_found = True
# C: Mentés és véglegesítés
if data_found:
master.status = "ai_enriched"
master.updated_at = datetime.datetime.now()
await db.commit() # AZONNALI COMMIT A LEMEZRE
logger.info(f"✅ Sikeresen mentve: {master.marketing_name} (CCM: {master.engine_capacity})")
else:
logger.warning(f"⚠️ Nem találtam adatot az ID {m_id} esetében.")
except IntegrityError:
await db.rollback()
logger.warning(f"🚫 Duplikáció vagy Constraint hiba (ID: {m_id}). Kihagyva.")
except Exception as e:
await db.rollback()
logger.error(f"❌ Váratlan hiba az ID {m_id} esetében: {e}")
finally:
await db.close()
logger.info("🏁 50-es batch feldolgozva.")
if __name__ == "__main__":
asyncio.run(TechEnricher.run())