Initial commit: Robot ökoszisztéma v2.0 - Stabilizált jármű és szerviz robotok
This commit is contained in:
25
backend/app/tests_internal/README.md
Executable file
25
backend/app/tests_internal/README.md
Executable file
@@ -0,0 +1,25 @@
|
||||
# 🛠️ Internal Diagnostic Tools
|
||||
|
||||
Ez a mappa a rendszer stabilitását ellenőrző szkripteket tartalmazza.
|
||||
Futtatásuk a konténeren belül javasolt.
|
||||
|
||||
### 1. Schema Sync (`compare_schema.py`)
|
||||
**Mikor használd:** Ha új oszlopot adtál a modellhez, vagy nem indul el a rendszer DB hiba miatt.
|
||||
**Futtatás:** `docker compose exec api python -m app.tests_internal.compare_schema`
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.compare_schema
|
||||
|
||||
### 2. API Health (`check_api.py`)
|
||||
**Mikor használd:** Refaktorálás után. Ellenőrzi, hogy az összes API "kapu" nyitva van-e.
|
||||
**Futtatás:** `docker compose exec api python -m app.tests_internal.check_api`
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.check_api
|
||||
|
||||
### 3. Geo Search Test (`test_postgis.py`)
|
||||
**Mikor használd:** Ha a szervizkereső nem ad vissza eredményt, vagy SQL hibát dob.
|
||||
**Futtatás:** `docker compose exec api python -m app.tests_internal.test_postgis`
|
||||
|
||||
### 3. Rendszerdiagnosztika (`diagnose_system.py`)
|
||||
- **Cél:** Mély ellenőrzés: DB kapcsolat, i18n szótárak, Master Data mezők és konfigurációk.
|
||||
- **Mikor használd:** Telepítés után, vagy ha a rendszer "furcsán" viselkedik (pl. angolul beszél magyar helyett).
|
||||
- **Indítás:**
|
||||
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.diagnose_system
|
||||
0
backend/app/tests_internal/__init__.py
Executable file
0
backend/app/tests_internal/__init__.py
Executable file
0
backend/app/tests_internal/diagnostics/__init__.py
Executable file
0
backend/app/tests_internal/diagnostics/__init__.py
Executable file
53
backend/app/tests_internal/diagnostics/check_api.py
Executable file
53
backend/app/tests_internal/diagnostics/check_api.py
Executable file
@@ -0,0 +1,53 @@
|
||||
# /app/tests_internal/diagnostics/check_api.py
|
||||
import requests
|
||||
import json
|
||||
|
||||
# THOUGHT PROCESS:
|
||||
# 1. Az API konténeren belül futva a localhost:8000 a célpont.
|
||||
# 2. Csak olyan végpontokat tesztelünk, amik szerepelnek a v1/api.py-ben.
|
||||
# 3. A 401/405 kódokat elfogadjuk 'OK'-nak, mert azt jelentik, hogy a szerver
|
||||
# látja az útvonalat, csak hitelesítést vagy más HTTP metódust vár.
|
||||
# 4. A 404 azt jelenti, hogy a router nincs jól regisztrálva.
|
||||
# 5. A 500 azt jelenti, hogy a kód elszállt (pl. AttributeError a main.py-ban).
|
||||
|
||||
base_url = 'http://localhost:8000'
|
||||
|
||||
# A valós, api.py-ben definiált útvonalak:
|
||||
tests = [
|
||||
('Health Check', '/health', 'GET'),
|
||||
('Auth Login (Entry)', '/api/v1/auth/login', 'POST'), # Létezik
|
||||
('Catalog Makes', '/api/v1/catalog/makes', 'GET'), # Létezik
|
||||
('Service Hunt', '/api/v1/services/hunt', 'POST'), # Létezik
|
||||
('Admin Health', '/api/v1/admin/health-monitor', 'GET'), # Létezik
|
||||
('My Organizations', '/api/v1/organizations/my', 'GET') # Létezik
|
||||
]
|
||||
|
||||
print('\n--- 🧪 API VÉGPONT DIAGNOSZTIKA ---')
|
||||
print(f"{'Végpont neve':20} | {'Útvonal':25} | {'Állapot'}")
|
||||
print("-" * 65)
|
||||
|
||||
for name, endpoint, method in tests:
|
||||
try:
|
||||
url = f"{base_url}{endpoint}"
|
||||
# A POST hívásokhoz üres adatot küldünk, hogy ne 422-t kapjunk a hiányzó body miatt
|
||||
resp = requests.request(method, url, timeout=5, json={})
|
||||
|
||||
# Logika:
|
||||
# 200: Tökéletes
|
||||
# 401: Él, de login kell (JÓ)
|
||||
# 405: Él, de pl. GET helyett POST kell (JÓ - az útvonal létezik)
|
||||
# 422: Él, de hiányoznak a küldött adatok (JÓ - a validáció működik)
|
||||
|
||||
if resp.status_code in [200, 401, 405, 422]:
|
||||
status_msg = f"✅ OK ({resp.status_code})"
|
||||
elif resp.status_code == 404:
|
||||
status_msg = f"❌ HIÁNYZIK (404)"
|
||||
else:
|
||||
status_msg = f"🔥 SZERVER HIBA ({resp.status_code})"
|
||||
|
||||
print(f"{name:20} | {endpoint:25} | {status_msg}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{name:20} | {endpoint:25} | 🔌 ELÉRHETETLEN")
|
||||
|
||||
print("\n💡 Megjegyzés: Ha a Health Check továbbra is 500, az a main.py-ban lévő elírás miatt van.")
|
||||
66
backend/app/tests_internal/diagnostics/compare_schema.py
Executable file
66
backend/app/tests_internal/diagnostics/compare_schema.py
Executable file
@@ -0,0 +1,66 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/diagnostics/compare_schema.py
|
||||
import asyncio
|
||||
import sys
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import inspect
|
||||
from app.database import Base
|
||||
from app.core.config import settings
|
||||
|
||||
try:
|
||||
import app.models
|
||||
except ImportError as e:
|
||||
print(f"❌ KRITIKUS IMPORT HIBA: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
async def compare():
|
||||
""" Diagnosztika minden sémára: identity, data, system. """
|
||||
print(f"🔗 Kapcsolódás az adatbázishoz...")
|
||||
engine = create_async_engine(str(settings.SQLALCHEMY_DATABASE_URI))
|
||||
|
||||
def get_diff(connection):
|
||||
inspector = inspect(connection)
|
||||
# Ezeket a sémákat ellenőrizzük
|
||||
schemas = ["identity", "data", "system"]
|
||||
all_db_schemas = inspector.get_schema_names()
|
||||
|
||||
mismatches = 0
|
||||
for sc in schemas:
|
||||
if sc not in all_db_schemas:
|
||||
print(f"❌ HIBA: A(z) '{sc}' séma nem létezik!")
|
||||
continue
|
||||
|
||||
db_tables = inspector.get_table_names(schema=sc)
|
||||
# Begyűjtjük a modelleket, amik ehhez a sémához tartoznak
|
||||
model_tables = [t.name for t in Base.metadata.sorted_tables if t.schema == sc]
|
||||
|
||||
print(f"\n--- 🔍 DIAGNOSZTIKA: '{sc}' séma ({len(db_tables)} tábla a DB-ben) ---")
|
||||
|
||||
for mt in model_tables:
|
||||
if mt not in db_tables:
|
||||
print(f"❌ HIÁNYZÓ TÁBLA: {sc}.{mt}")
|
||||
mismatches += 1
|
||||
else:
|
||||
db_cols = {c['name']: c for c in inspector.get_columns(mt, schema=sc)}
|
||||
# Kikeressük a modellt a metadata-ból
|
||||
table_key = f"{sc}.{mt}"
|
||||
model_cols = Base.metadata.tables[table_key].columns
|
||||
|
||||
missing_cols = [m.name for m in model_cols if m.name not in db_cols]
|
||||
if missing_cols:
|
||||
print(f"⚠️ {mt:25} | HIÁNYZÓ OSZLOPOK: {missing_cols}")
|
||||
mismatches += 1
|
||||
else:
|
||||
print(f"✅ {mt:25} | Rendben.")
|
||||
return mismatches
|
||||
|
||||
try:
|
||||
async with engine.connect() as conn:
|
||||
err_count = await conn.run_sync(get_diff)
|
||||
print(f"\n--- Összegzés: {err_count} eltérés található. ---\n")
|
||||
except Exception as e:
|
||||
print(f"❌ HIBA: {e}")
|
||||
finally:
|
||||
await engine.dispose()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(compare())
|
||||
157
backend/app/tests_internal/diagnostics/diagnose_system.py
Executable file
157
backend/app/tests_internal/diagnostics/diagnose_system.py
Executable file
@@ -0,0 +1,157 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/diagnostics/diagnose_system.py
|
||||
"""
|
||||
🛰️ SENTINEL SYSTEM DIAGNOSTICS - MB2.0 (2026)
|
||||
---------------------------------------------
|
||||
CÉL: A rendszer mélyszintű integritásának és működőképességének auditálása.
|
||||
|
||||
FŐBB TESZTEK:
|
||||
1. Adatbázis Kapcsolat: PostgreSQL aszinkron elérés ellenőrzése.
|
||||
2. Séma Integritás: Kritikus Master Data táblák és mezők meglétének vizsgálata.
|
||||
3. Rendszer Paraméterek: A Sentinel központi konfigurációs táblájának ellenőrzése.
|
||||
4. i18n Motor: Nyelvi gyorsítótár (Cache) és fordítási mechanizmus tesztje.
|
||||
5. Robot Pipeline: Staging (Hunter) és Gold (Catalog) rekordok számlálása.
|
||||
|
||||
FUTTATÁS:
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.diagnose_system
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from sqlalchemy import text, select, func
|
||||
|
||||
# 🛠️ KRITIKUS IMPORT KÖRNYEZET ELLENŐRZÉSE
|
||||
try:
|
||||
from app.core.config import settings
|
||||
# Megjegyzés: A projekt struktúrájától függően AsyncSessionLocal az app.database-ben van
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.services.translation_service import translation_service
|
||||
from app.models.system import SystemParameter
|
||||
from app.models.identity import User
|
||||
from app.models.organization import Organization
|
||||
from app.models.asset import AssetCatalog
|
||||
from app.models.vehicle_definitions import VehicleModelDefinition
|
||||
except ImportError as e:
|
||||
print(f"\n❌ [KRITIKUS HIBA] Az importálás nem sikerült: {e}")
|
||||
print("💡 Javaslat: Ellenőrizd a PYTHONPATH-t és a __init__.py fájlok meglétét!")
|
||||
sys.exit(1)
|
||||
|
||||
# SQL logolás némítása a tiszta kimenet érdekében
|
||||
logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING)
|
||||
|
||||
async def diagnose():
|
||||
start_time = datetime.now()
|
||||
print("\n" + "═"*70)
|
||||
print(f"🛰️ SENTINEL SYSTEM DIAGNOSTICS - MB2.0 | {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("═"*70 + "\n")
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
|
||||
# --- 1. CSATLAKOZÁS ÉS ADATBÁZIS PING ---
|
||||
print("1️⃣ Kapcsolódási teszt...")
|
||||
try:
|
||||
await session.execute(text("SELECT 1"))
|
||||
print(" [✅ OK] PostgreSQL aszinkron kapcsolat aktív.")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Nem sikerült kapcsolódni az adatbázishoz: {e}")
|
||||
return
|
||||
|
||||
# --- 2. SÉMA INTEGRITÁS (Master Data Audit) ---
|
||||
print("\n2️⃣ Séma integritás ellenőrzése (Kritikus mezők)...")
|
||||
# Tábla neve (sémával) | Elvárt oszlopok listája
|
||||
tables_to_check = [
|
||||
("identity.users", ["preferred_language", "scope_id", "is_active"]),
|
||||
("data.organizations", ["org_type", "folder_slug", "is_active"]),
|
||||
("data.assets", ["owner_org_id", "catalog_id", "vin"]),
|
||||
# "asset_catalog" helyett "vehicle_catalog"
|
||||
("data.vehicle_catalog", ["make", "model", "factory_data"]),
|
||||
("data.vehicle_model_definitions", ["status", "raw_search_context"])
|
||||
]
|
||||
|
||||
for table, columns in tables_to_check:
|
||||
try:
|
||||
schema_name, table_name = table.split('.')
|
||||
query = text(f"""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_schema = '{schema_name}' AND table_name = '{table_name}';
|
||||
""")
|
||||
res = await session.execute(query)
|
||||
existing_cols = [row[0] for row in res.fetchall()]
|
||||
|
||||
if not existing_cols:
|
||||
print(f" [❌ HIBA] A tábla NEM létezik: {table}")
|
||||
continue
|
||||
|
||||
missing = [c for c in columns if c not in existing_cols]
|
||||
if not missing:
|
||||
print(f" [✅ OK] {table:35} (Minden mező rendben)")
|
||||
else:
|
||||
print(f" [⚠️ HIÁNY] {table:35} | Hiányzó mezők: {', '.join(missing)}")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Hiba a(z) {table} ellenőrzésekor: {e}")
|
||||
|
||||
# --- 3. RENDSZER PARAMÉTEREK (Sentinel Config) ---
|
||||
print("\n3️⃣ System Parameters (Sentinel Config) ellenőrzése...")
|
||||
try:
|
||||
res = await session.execute(select(SystemParameter))
|
||||
params = res.scalars().all()
|
||||
if params:
|
||||
print(f" [✅ OK] Talált paraméterek: {len(params)} db")
|
||||
# Kritikus kulcsok, amiknek illik lenniük
|
||||
critical_keys = ["SECURITY_MAX_RECORDS_PER_HOUR", "VEHICLE_LIMIT"]
|
||||
existing_keys = [p.key for p in params]
|
||||
for ck in critical_keys:
|
||||
status = "✔️" if ck in existing_keys else "❌ (Hiányzik)"
|
||||
print(f" {status} {ck}")
|
||||
else:
|
||||
print(" [⚠️ FIGYELEM] A system_parameters tábla üres! Futtasd a seedert.")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] SystemParameter lekérdezési hiba: {e}")
|
||||
|
||||
# --- 4. i18n ÉS CACHE MOTOR ---
|
||||
print("\n4️⃣ Nyelvi motor és i18n Cache ellenőrzése...")
|
||||
try:
|
||||
# Gyorsítótár frissítése az adatbázisból
|
||||
await translation_service.load_cache(session)
|
||||
|
||||
test_key = "COMMON.SAVE"
|
||||
test_val = translation_service.get_text(test_key, "hu")
|
||||
|
||||
# Ha a visszakapott érték nem egyezik a kulccsal, akkor van fordítás
|
||||
if test_val and test_val != f"[{test_key}]":
|
||||
print(f" [✅ OK] Fordítás sikeres (HU): '{test_key}' -> '{test_val}'")
|
||||
else:
|
||||
print(f" [❌ HIBA] Fordítás nem működik. Nincs betöltött adat vagy hibás a cache.")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Nyelvi motor hiba: {e}")
|
||||
|
||||
# --- 5. ROBOT ELŐKÉSZÜLETEK (Pipeline MDM) ---
|
||||
print("\n5️⃣ Robot Pipeline (MDM Staging) állapot...")
|
||||
try:
|
||||
# Hunter robot eredményei (nyers adatok)
|
||||
res_hunter = await session.execute(
|
||||
select(func.count(VehicleModelDefinition.id)).where(VehicleModelDefinition.status == 'unverified')
|
||||
)
|
||||
unverified_count = res_hunter.scalar() or 0
|
||||
|
||||
# Catalog robot eredményei (tisztított adatok)
|
||||
res_gold = await session.execute(select(func.count(AssetCatalog.id)))
|
||||
gold_count = res_gold.scalar() or 0
|
||||
|
||||
print(f" [📊 STAT] Feldolgozatlan rekordok (Staging): {unverified_count} db")
|
||||
print(f" [📊 STAT] Validált arany rekordok (Catalog): {gold_count} db")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Robot-statisztika hiba: {e}")
|
||||
|
||||
duration = (datetime.now() - start_time).total_seconds()
|
||||
print("\n" + "═"*70)
|
||||
print(f"🏁 DIAGNOSZTIKA BEFEJEZŐDÖTT | Időtartam: {duration:.2f} mp")
|
||||
print("═"*70 + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(diagnose())
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 Diagnosztika megszakítva a felhasználó által.")
|
||||
sys.exit(0)
|
||||
0
backend/app/tests_internal/fixes/__init__.py
Executable file
0
backend/app/tests_internal/fixes/__init__.py
Executable file
82
backend/app/tests_internal/fixes/final_admin_fix.py
Executable file
82
backend/app/tests_internal/fixes/final_admin_fix.py
Executable file
@@ -0,0 +1,82 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/final_admin_fix.py
|
||||
import asyncio
|
||||
import uuid
|
||||
from sqlalchemy import text, select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.core.security import get_password_hash
|
||||
|
||||
async def run_fix():
|
||||
print("\n" + "═"*50)
|
||||
print("🛠️ ADMIN RENDSZERJAVÍTÁS ÉS INICIALIZÁLÁS (MB2.0)")
|
||||
print("═"*50)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. LOGIKA: Séma ellenőrzése az 'identity' névtérben
|
||||
# Az MB2.0-ban a felhasználók már nem a 'data', hanem az 'identity' sémában vannak.
|
||||
check_query = text("""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_schema = 'identity' AND table_name = 'users'
|
||||
""")
|
||||
res = await db.execute(check_query)
|
||||
cols = [r[0] for r in res.fetchall()]
|
||||
|
||||
if not cols:
|
||||
print("❌ HIBA: Az 'identity.users' tábla nem található. Futtasd az Alembic migrációt!")
|
||||
return
|
||||
|
||||
if "hashed_password" not in cols:
|
||||
print("❌ HIBA: A 'hashed_password' oszlop hiányzik. Az adatbázis sémája elavult.")
|
||||
return
|
||||
|
||||
# 2. LOGIKA: Admin keresése
|
||||
admin_email = "admin@profibot.hu"
|
||||
stmt = select(User).where(User.email == admin_email)
|
||||
existing_res = await db.execute(stmt)
|
||||
existing_admin = existing_res.scalar_one_or_none()
|
||||
|
||||
if existing_admin:
|
||||
print(f"⚠️ Információ: A(z) {admin_email} felhasználó már létezik.")
|
||||
# Opcionális: Jelszó kényszerített frissítése, ha elfelejtetted
|
||||
# existing_admin.hashed_password = get_password_hash("Admin123!")
|
||||
# await db.commit()
|
||||
else:
|
||||
try:
|
||||
# 3. LOGIKA: Person és User létrehozása (MB2.0 Standard)
|
||||
# Előbb létrehozzuk a fizikai személyt
|
||||
new_person = Person(
|
||||
id_uuid=uuid.uuid4(),
|
||||
first_name="Rendszer",
|
||||
last_name="Adminisztrátor",
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush() # ID lekérése a mentés előtt
|
||||
|
||||
# Létrehozzuk a felhasználói fiókot az Admin role-al
|
||||
new_admin = User(
|
||||
email=admin_email,
|
||||
hashed_password=get_password_hash("Admin123!"),
|
||||
person_id=new_person.id,
|
||||
role=UserRole.superadmin, # MB2.0 enum érték
|
||||
is_active=True,
|
||||
is_deleted=False,
|
||||
preferred_language="hu"
|
||||
)
|
||||
db.add(new_admin)
|
||||
|
||||
await db.commit()
|
||||
print(f"✅ SIKER: Superadmin létrehozva!")
|
||||
print(f" 📧 Email: {admin_email}")
|
||||
print(f" 🔑 Jelszó: Admin123!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ HIBA a mentés során: {e}")
|
||||
await db.rollback()
|
||||
|
||||
print("\n" + "═"*50)
|
||||
print("🏁 JAVÍTÁSI FOLYAMAT BEFEJEZŐDÖTT")
|
||||
print("═"*50 + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_fix())
|
||||
0
backend/app/tests_internal/seeds/__init__.py
Executable file
0
backend/app/tests_internal/seeds/__init__.py
Executable file
98
backend/app/tests_internal/seeds/seed_catalog.py
Executable file
98
backend/app/tests_internal/seeds/seed_catalog.py
Executable file
@@ -0,0 +1,98 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/seeds/seed_catalog.py
|
||||
"""
|
||||
🌱 MB2.0 KATALÓGUS ÉS ROBOT SEEDER
|
||||
----------------------------------
|
||||
CÉL: A járműkatalógus alapadatainak és a robotok munkalistájának feltöltése.
|
||||
|
||||
FUNKCIÓK:
|
||||
1. DiscoveryParameter: Azon városok rögzítése, ahol a Scout robot keresni fog.
|
||||
2. CatalogDiscovery: Azon márkák rögzítése, amiket a Robot 0/1 fel fog dolgozni.
|
||||
3. AssetCatalog: 'Arany' (már validált) technikai rekordok beszúrása.
|
||||
|
||||
JAVÍTÁSOK:
|
||||
- 'last_error' oszlop eltávolítva (mivel az adatbázisban nem létezik).
|
||||
- 'attempts' mező kényszerített 0 értékkel (NOT NULL kényszer miatt).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.asset import AssetCatalog, CatalogDiscovery
|
||||
from app.models.staged_data import DiscoveryParameter
|
||||
|
||||
# Logolás beállítása
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Seed: %(message)s')
|
||||
logger = logging.getLogger("Seed-Catalog")
|
||||
|
||||
async def quick_seed():
|
||||
""" Katalógus és Discovery adatok inicializálása. """
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 Katalógus alapozás indítása...")
|
||||
|
||||
try:
|
||||
# 1. Felderítendő Városok (DiscoveryParameter)
|
||||
# A Scout robot ezekben a városokban kezdi meg a szervizek kutatását.
|
||||
cities = [
|
||||
("BUDAPEST", "HU"),
|
||||
("DEBRECEN", "HU"),
|
||||
("GYŐR", "HU"),
|
||||
("SZEGED", "HU")
|
||||
]
|
||||
|
||||
for city_name, country in cities:
|
||||
db.add(DiscoveryParameter(
|
||||
city=city_name,
|
||||
country_code=country,
|
||||
is_active=True
|
||||
))
|
||||
|
||||
# 2. Felderítendő Márkák és Típusok (CatalogDiscovery)
|
||||
# Ezeket a rekordokat fogja a Robot 0 és Robot 1 feldolgozni a háttérben.
|
||||
discovery_queue = [
|
||||
("SUZUKI", "ALL"),
|
||||
("TOYOTA", "ALL"),
|
||||
("HONDA", "ALL"),
|
||||
("SKODA", "ALL"),
|
||||
("YAMAHA", "ALL")
|
||||
]
|
||||
|
||||
for m, mod in discovery_queue:
|
||||
# Az attempts=0 kötelező a DB kényszer miatt
|
||||
db.add(CatalogDiscovery(
|
||||
make=m,
|
||||
model=mod,
|
||||
status="pending",
|
||||
attempts=0
|
||||
))
|
||||
|
||||
# 3. Arany rekordok (AssetCatalog / vehicle_catalog tábla)
|
||||
# Példa adatok, amik már átmentek a validációs folyamaton.
|
||||
gold_data = [
|
||||
AssetCatalog(
|
||||
make="SUZUKI",
|
||||
model="VITARA",
|
||||
generation="LY (2015-)",
|
||||
fuel_type="petrol",
|
||||
factory_data={"segment": "SUV", "origin": "Hungary"}
|
||||
),
|
||||
AssetCatalog(
|
||||
make="SKODA",
|
||||
model="OCTAVIA",
|
||||
generation="IV (2020-)",
|
||||
fuel_type="diesel",
|
||||
factory_data={"segment": "Sedan/Combi"}
|
||||
)
|
||||
]
|
||||
db.add_all(gold_data)
|
||||
|
||||
# Mentés végrehajtása
|
||||
await db.commit()
|
||||
logger.info("✨ Katalógus és Discovery paraméterek sikeresen rögzítve!")
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"❌ HIBA a katalógus feltöltésekor: {e}")
|
||||
raise e
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(quick_seed())
|
||||
106
backend/app/tests_internal/seeds/seed_data.py
Executable file
106
backend/app/tests_internal/seeds/seed_data.py
Executable file
@@ -0,0 +1,106 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/seed_data.py
|
||||
import asyncio
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import text, select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models.social import ServiceProvider, Vote, ModerationStatus, Competition
|
||||
from app.services.social_service import SocialService
|
||||
from app.core.security import get_password_hash
|
||||
|
||||
async def run_simulation():
|
||||
async with AsyncSessionLocal() as db:
|
||||
print("--- 1. TAKARÍTÁS (MB2.0 Séma-tisztítás) ---")
|
||||
# Szigorú sorrend a kényszerek miatt (Cascade)
|
||||
await db.execute(text("TRUNCATE identity.users, identity.persons, data.service_providers, data.votes, data.competitions RESTART IDENTITY CASCADE"))
|
||||
await db.commit()
|
||||
|
||||
print("\n--- 2. SZEREPLŐK LÉTREHOZÁSA (Person + User) ---")
|
||||
users_to_create = [
|
||||
("admin@test.com", "Adminisztrátor", UserRole.superadmin),
|
||||
("good@test.com", "Rendes Srác", UserRole.user),
|
||||
("bad@test.com", "Spammer Aladár", UserRole.user),
|
||||
("voter@test.com", "Szavazó Gép", UserRole.user)
|
||||
]
|
||||
|
||||
created_users = {}
|
||||
for email, name, role in users_to_create:
|
||||
p = Person(id_uuid=uuid.uuid4(), first_name=name.split()[0], last_name=name.split()[1], is_active=True)
|
||||
db.add(p)
|
||||
await db.flush()
|
||||
|
||||
u = User(
|
||||
email=email,
|
||||
hashed_password=get_password_hash("test1234"),
|
||||
person_id=p.id,
|
||||
role=role,
|
||||
is_active=True,
|
||||
reputation_score=5 if "good" in email else (-8 if "bad" in email else 0)
|
||||
)
|
||||
db.add(u)
|
||||
await db.flush()
|
||||
created_users[email] = u
|
||||
|
||||
await db.commit()
|
||||
|
||||
print("\n--- 3. VERSENY INDÍTÁSA ---")
|
||||
race = Competition(
|
||||
name="Téli Szervizvadászat",
|
||||
start_date=datetime.now(timezone.utc) - timedelta(days=1),
|
||||
end_date=datetime.now(timezone.utc) + timedelta(days=30),
|
||||
is_active=True
|
||||
)
|
||||
db.add(race)
|
||||
await db.commit()
|
||||
|
||||
# Szereplők kiemelése a szimulációhoz
|
||||
good_user = created_users["good@test.com"]
|
||||
bad_user = created_users["bad@test.com"]
|
||||
voter = created_users["voter@test.com"]
|
||||
|
||||
print("\n--- 4. SZCENÁRIÓ A: POZITÍV VALIDÁCIÓ ---")
|
||||
# Rendes srác beküld egy szervizt
|
||||
shop = ServiceProvider(
|
||||
name="Profi Gumis",
|
||||
address="Budapest, Váci út 10.",
|
||||
added_by_user_id=good_user.id,
|
||||
status=ModerationStatus.pending
|
||||
)
|
||||
db.add(shop)
|
||||
await db.flush()
|
||||
|
||||
# Szavazatok szimulálása (SocialService használatával a pontszámítás miatt)
|
||||
print(f"Szavazás a '{shop.name}'-re...")
|
||||
# Szimulálunk 5 pozitív szavazatot különböző "virtuális" szavazóktól
|
||||
for _ in range(5):
|
||||
await SocialService.vote_for_provider(db, voter.id, shop.id, 1)
|
||||
|
||||
await db.refresh(good_user)
|
||||
print(f"Jó felhasználó hírneve: {good_user.reputation_score}")
|
||||
|
||||
print("\n--- 5. SZCENÁRIÓ B: AUTO-BAN (SPAM SZŰRÉS) ---")
|
||||
fake_shop = ServiceProvider(
|
||||
name="KAMU SZERVIZ",
|
||||
address="Nincs ilyen utca 0.",
|
||||
added_by_user_id=bad_user.id,
|
||||
status=ModerationStatus.pending
|
||||
)
|
||||
db.add(fake_shop)
|
||||
await db.flush()
|
||||
|
||||
# Leszavazás (Kell -3 a bukáshoz)
|
||||
print("Spam jelentése...")
|
||||
await SocialService.vote_for_provider(db, voter.id, fake_shop.id, -1)
|
||||
await SocialService.vote_for_provider(db, voter.id, fake_shop.id, -1)
|
||||
await SocialService.vote_for_provider(db, voter.id, fake_shop.id, -1)
|
||||
|
||||
await db.refresh(bad_user)
|
||||
print(f"Rossz felhasználó hírneve: {bad_user.reputation_score}")
|
||||
print(f"Fiók státusza: {'KITILTVA' if not bad_user.is_active else 'AKTÍV'}")
|
||||
|
||||
if not bad_user.is_active:
|
||||
print("✅ SIKER: A Sentinel automatikusan leállította a spammert!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_simulation())
|
||||
65
backend/app/tests_internal/seeds/seed_expertises.py
Executable file
65
backend/app/tests_internal/seeds/seed_expertises.py
Executable file
@@ -0,0 +1,65 @@
|
||||
import asyncio
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.service import ExpertiseTag
|
||||
from sqlalchemy import text
|
||||
|
||||
async def seed_expertises():
|
||||
tags = [
|
||||
# --- ALAPSZOLGÁLTATÁSOK (MECHANICS) ---
|
||||
('OIL_SERVICE', 'Időszakos szerviz / Olajcsere', 'MECHANICS'),
|
||||
('BRAKE_REPAIR', 'Fékrendszer javítás', 'MECHANICS'),
|
||||
('SUSPENSION', 'Futómű javítás és beállítás', 'MECHANICS'),
|
||||
('EXHAUST', 'Kipufogó szerviz', 'MECHANICS'),
|
||||
('CLUTCH', 'Kuplung és kettőstömegű csere', 'MECHANICS'),
|
||||
|
||||
# --- MOTOR ÉS VÁLTÓ (ENGINE_DRIVETRAIN) ---
|
||||
('ENGINE_REBUILD', 'Motorfelújítás', 'ENGINE_DRIVETRAIN'),
|
||||
('TIMING_BELT', 'Vezérlés csere', 'ENGINE_DRIVETRAIN'),
|
||||
('AUTO_GEARBOX', 'Automata váltó javítás/olajcsere', 'ENGINE_DRIVETRAIN'),
|
||||
('TURBO_REPAIR', 'Turbófeltöltő felújítás', 'ENGINE_DRIVETRAIN'),
|
||||
('INJECTOR', 'Dízel injektor / Adagoló javítás', 'ENGINE_DRIVETRAIN'),
|
||||
('DPF_CLEAN', 'DPF / Részecskeszűrő tisztítás', 'ENGINE_DRIVETRAIN'),
|
||||
|
||||
# --- ELEKTRONIKA (ELECTRICAL) ---
|
||||
('DIAGNOSTICS', 'Számítógépes diagnosztika', 'ELECTRICAL'),
|
||||
('AC_REPAIR', 'Klíma javítás és töltés', 'ELECTRICAL'),
|
||||
('BATTERY', 'Akkumulátor szerviz', 'ELECTRICAL'),
|
||||
('HYBRID_EV', 'Hibrid és Elektromos autó szerviz', 'ELECTRICAL'),
|
||||
('CHIP_TUNING', 'Szoftveres optimalizálás / Tuning', 'ELECTRICAL'),
|
||||
('ADAS', 'Vezetéstámogató rendszerek kalibrálása', 'ELECTRICAL'),
|
||||
|
||||
# --- GUMI ÉS KERÉK (TYRES) ---
|
||||
('TYRE_CHANGE', 'Gumiszerelés és centírozás', 'TYRES'),
|
||||
('WHEEL_REPAIR', 'Alufelni javítás / Görgőzés', 'TYRES'),
|
||||
|
||||
# --- KAROSSZÉRIA (BODY) ---
|
||||
('BODY_REPAIR', 'Karosszéria lakatolás', 'BODY'),
|
||||
('PAINTING', 'Autófényezés', 'BODY'),
|
||||
('GLASS_REPAIR', 'Szélvédő javítás és csere', 'BODY'),
|
||||
('PDR', 'Jégkár és horpadásjavítás (PDR)', 'BODY'),
|
||||
|
||||
# --- SEGÉLY ÉS SZÁLLÍTÁS (EMERGENCY) ---
|
||||
('TOWING', 'Autómentés / Vontatás', 'EMERGENCY'),
|
||||
('ROADSIDE_ASSIST', 'Segélyszolgálat / Helyszíni javítás', 'EMERGENCY'),
|
||||
('LOCKSMITH', 'Autózár szerviz / Kulcsmásolás', 'EMERGENCY'),
|
||||
|
||||
# --- EGYÉB JÁRMŰVEK (VEHICLE_TYPES) ---
|
||||
('MOTO_SERVICE', 'Motorkerékpár szerviz', 'VEHICLE_TYPES'),
|
||||
('TRUCK_SERVICE', 'Tehergépjármű szerviz', 'VEHICLE_TYPES'),
|
||||
('AGRI_SERVICE', 'Mezőgazdasági gép szerviz', 'VEHICLE_TYPES'),
|
||||
]
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
print("🌱 Szakmai címkék feltöltése...")
|
||||
for key, name, cat in tags:
|
||||
stmt = text("""
|
||||
INSERT INTO data.expertise_tags (key, name_hu, category, is_official)
|
||||
VALUES (:k, :n, :c, true)
|
||||
ON CONFLICT (key) DO UPDATE SET name_hu = EXCLUDED.name_hu, category = EXCLUDED.category
|
||||
""")
|
||||
await db.execute(stmt, {"k": key, "n": name, "c": cat})
|
||||
await db.commit()
|
||||
print(f"✅ {len(tags)} címke rögzítve.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_expertises())
|
||||
74
backend/app/tests_internal/seeds/seed_honda.py
Executable file
74
backend/app/tests_internal/seeds/seed_honda.py
Executable file
@@ -0,0 +1,74 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/seed_honda.py
|
||||
import asyncio
|
||||
import logging
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.asset import AssetCatalog
|
||||
from app.models.staged_data import DiscoveryParameter
|
||||
|
||||
# Logolás beállítása
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Seed: %(message)s')
|
||||
logger = logging.getLogger("Honda-Seeder")
|
||||
|
||||
async def seed_honda():
|
||||
"""
|
||||
Honda specifikus alapozás az MB2.0 MDM (Master Data Management) szerint.
|
||||
Létrehozza a katalógus-vázat és a robot-feladatokat.
|
||||
"""
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 Honda márka-ökoszisztéma inicializálása...")
|
||||
|
||||
# 1. LOGIKA: Robot Discovery feladatok rögzítése
|
||||
# Ezzel mondjuk meg a Hunter robotnak, hogy keressen rá minden Honda variánsra
|
||||
discovery_tasks = [
|
||||
DiscoveryParameter(make="HONDA", vehicle_class="car", city="BUDAPEST", keyword="repair", is_active=True),
|
||||
DiscoveryParameter(make="HONDA", vehicle_class="motorcycle", city="BUDAPEST", keyword="service", is_active=True)
|
||||
]
|
||||
|
||||
for task in discovery_tasks:
|
||||
# Megnézzük, van-e már ilyen feladat
|
||||
stmt = select(DiscoveryParameter).where(
|
||||
DiscoveryParameter.make == task.make,
|
||||
DiscoveryParameter.vehicle_class == task.vehicle_class
|
||||
)
|
||||
exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
if not exists:
|
||||
db.add(task)
|
||||
|
||||
# 2. LOGIKA: Népszerű modellek (Arany Rekordok) betöltése
|
||||
# Ezek a "Starter" adatok, amik azonnal elérhetők a felhasználóknak
|
||||
honda_models = [
|
||||
# Személyautók
|
||||
{"model": "CIVIC", "gen": "X (2015-2021)", "class": "car"},
|
||||
{"model": "ACCORD", "gen": "X (2017-)", "class": "car"},
|
||||
{"model": "CR-V", "gen": "V (2016-)", "class": "car"},
|
||||
{"model": "JAZZ", "gen": "IV (2020-)", "class": "car"},
|
||||
# Motorkerékpárok
|
||||
{"model": "CB500X", "gen": "PC64 (2019-)", "class": "motorcycle"},
|
||||
{"model": "AFRICA TWIN", "gen": "CRF1100L", "class": "motorcycle"},
|
||||
{"model": "NC750X", "gen": "RH09 (2021-)", "class": "motorcycle"}
|
||||
]
|
||||
|
||||
for m in honda_models:
|
||||
# Ellenőrizzük az AssetCatalog-ban (MDM tábla)
|
||||
stmt = select(AssetCatalog).where(
|
||||
AssetCatalog.make == "HONDA",
|
||||
AssetCatalog.model == m["model"],
|
||||
AssetCatalog.generation == m["gen"]
|
||||
)
|
||||
exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
if not exists:
|
||||
db.add(AssetCatalog(
|
||||
make="HONDA",
|
||||
model=m["model"],
|
||||
generation=m["gen"],
|
||||
vehicle_class=m["class"],
|
||||
factory_data={"source": "manual_priority_seed"} # MDM metaadat
|
||||
))
|
||||
|
||||
await db.commit()
|
||||
logger.info("✅ Honda (Autó & Motor) katalógus váz sikeresen felépítve!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_honda())
|
||||
79
backend/app/tests_internal/seeds/seed_system.py
Executable file
79
backend/app/tests_internal/seeds/seed_system.py
Executable file
@@ -0,0 +1,79 @@
|
||||
# /app/tests_internal/seeds/seed_system.py
|
||||
import asyncio
|
||||
import logging
|
||||
import uuid
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models.system import SystemParameter
|
||||
# JAVÍTOTT IMPORTOK: A grep alapján szétválasztva
|
||||
from app.models.gamification import PointRule, LevelConfig, UserStats
|
||||
from app.models.core_logic import SubscriptionTier
|
||||
from app.core.security import get_password_hash
|
||||
from app.core.config import settings
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Seed: %(message)s')
|
||||
logger = logging.getLogger("System-Seeder")
|
||||
|
||||
async def seed_data():
|
||||
""" Rendszer alapadatok inicializálása a megfelelő modellekből. """
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 Rendszer-alapozás indítása (MB2.0 Standard)...")
|
||||
|
||||
admin_email = settings.INITIAL_ADMIN_EMAIL
|
||||
admin_password = settings.INITIAL_ADMIN_PASSWORD
|
||||
|
||||
if not admin_email or not admin_password:
|
||||
logger.error("❌ HIBA: Admin hitelesítési adatok hiányoznak!")
|
||||
return
|
||||
|
||||
# 1. Superadmin és Person kapcsolat
|
||||
stmt = select(User).where(User.email == admin_email)
|
||||
admin_exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
if not admin_exists:
|
||||
new_person = Person(
|
||||
first_name="Rendszer",
|
||||
last_name="Adminisztrátor",
|
||||
id_uuid=uuid.uuid4(),
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
|
||||
new_admin = User(
|
||||
email=admin_email,
|
||||
hashed_password=get_password_hash(admin_password),
|
||||
role=UserRole.superadmin,
|
||||
is_active=True,
|
||||
person_id=new_person.id
|
||||
)
|
||||
db.add(new_admin)
|
||||
await db.flush()
|
||||
|
||||
# Statisztikai rekord (Gamification)
|
||||
db.add(UserStats(user_id=new_admin.id, total_xp=0, current_level=1))
|
||||
logger.info(f"✅ Superadmin létrehozva: {admin_email}")
|
||||
|
||||
# 2. Rendszerparaméterek (JSONB értékekkel)
|
||||
params = [
|
||||
("SECURITY_MAX_RECORDS_PER_HOUR", {"limit": 50}, "Biztonsági limit"),
|
||||
("VEHICLE_LIMIT", {"default": 5}, "Alapértelmezett jármű limit")
|
||||
]
|
||||
for key, val, desc in params:
|
||||
stmt_p = select(SystemParameter).where(SystemParameter.key == key)
|
||||
if not (await db.execute(stmt_p)).scalar_one_or_none():
|
||||
db.add(SystemParameter(key=key, value=val, description=desc))
|
||||
|
||||
# 3. Gamification Szabályok (gamification.py-ból)
|
||||
rules = [("ASSET_REGISTER", 100), ("ASSET_REVIEW", 75)]
|
||||
for key, pts in rules:
|
||||
stmt_r = select(PointRule).where(PointRule.action_key == key)
|
||||
if not (await db.execute(stmt_r)).scalar_one_or_none():
|
||||
db.add(PointRule(action_key=key, points=pts))
|
||||
|
||||
await db.commit()
|
||||
logger.info("✨ Rendszer alapadatok rögzítve.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_data())
|
||||
120
backend/app/tests_internal/seeds/seed_test_scenario.py
Executable file
120
backend/app/tests_internal/seeds/seed_test_scenario.py
Executable file
@@ -0,0 +1,120 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/seed_test_scenario.py
|
||||
import asyncio
|
||||
import uuid
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.organization import Organization, OrganizationMember, OrgType
|
||||
from app.models.asset import (
|
||||
Asset, AssetCatalog, AssetTelemetry,
|
||||
AssetFinancials, AssetCost
|
||||
)
|
||||
|
||||
# Sentinel naplózás
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Scenario: %(message)s')
|
||||
logger = logging.getLogger("Test-Scenario")
|
||||
|
||||
async def seed_test_scenario():
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 MB2.0 Teszt ökoszisztéma felépítése indul...")
|
||||
|
||||
# 1. LOGIKA: Admin (Superuser) lekérése az identity sémából
|
||||
res = await db.execute(select(User).where(User.is_active == True))
|
||||
admin = res.scalars().first()
|
||||
|
||||
if not admin:
|
||||
logger.error("❌ Hiba: Nincs aktív felhasználó a rendszerben. Futtasd a seed_system.py-t!")
|
||||
return
|
||||
|
||||
# 2. LOGIKA: Szervezeti struktúra felállítása
|
||||
# Privát garázs
|
||||
private_org = Organization(
|
||||
name="Kincses Privát",
|
||||
full_name="Kincses Magánflotta és Garázs",
|
||||
org_type=OrgType.individual,
|
||||
owner_id=admin.id,
|
||||
folder_slug="kincses-privat-vault"
|
||||
)
|
||||
# Üzleti flotta
|
||||
company_org = Organization(
|
||||
name="ProfiBot Fleet",
|
||||
full_name="ProfiBot Software Solutions Kft.",
|
||||
org_type=OrgType.business,
|
||||
owner_id=admin.id,
|
||||
folder_slug="profibot-fleet-vault"
|
||||
)
|
||||
# Szolgáltatók (Szerviz és Üzemanyag)
|
||||
service_org = Organization(
|
||||
name="Mester Szerviz",
|
||||
org_type=OrgType.service,
|
||||
owner_id=admin.id,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
db.add_all([private_org, company_org, service_org])
|
||||
await db.flush()
|
||||
|
||||
# Tagsági viszonyok rögzítése
|
||||
db.add(OrganizationMember(user_id=admin.id, organization_id=company_org.id, role="owner"))
|
||||
|
||||
# 3. LOGIKA: Tesla Model 3 - Digitális Iker (Digital Twin)
|
||||
# Előbb a katalógus (Gold Data)
|
||||
catalog = AssetCatalog(
|
||||
make="TESLA", model="MODEL 3", generation="Long Range (2021-)",
|
||||
fuel_type="electric",
|
||||
factory_data={
|
||||
"battery": "75 kWh", "power_kw": 366,
|
||||
"tire_size": "235/45 R18", "ac_charge": "11kW"
|
||||
}
|
||||
)
|
||||
db.add(catalog)
|
||||
await db.flush()
|
||||
|
||||
# Majd a konkrét jármű (Asset)
|
||||
vehicle = Asset(
|
||||
vin=f"5YJ3E1EB8LF{uuid.uuid4().hex[:6].upper()}",
|
||||
license_plate="TES-777-EV",
|
||||
name="Céges Tesla",
|
||||
year_of_manufacture=2021,
|
||||
catalog_id=catalog.id,
|
||||
owner_org_id=company_org.id,
|
||||
status="active"
|
||||
)
|
||||
db.add(vehicle)
|
||||
await db.flush()
|
||||
|
||||
# Telemetria és Pénzügyi alapok
|
||||
db.add(AssetTelemetry(asset_id=vehicle.id, current_mileage=45200, vqi_score=100.0))
|
||||
db.add(AssetFinancials(asset_id=vehicle.id, acquisition_price=18500000, currency="HUF"))
|
||||
|
||||
# 4. LOGIKA: A 9 költségtípus szimulálása
|
||||
costs_data = [
|
||||
("FUEL", 12500, "Supercharger töltés"),
|
||||
("MAINTENANCE", 85000, "Pollenszűrő és átvizsgálás"),
|
||||
("TIRES", 280000, "Téli gumi szett"),
|
||||
("INSURANCE", 32000, "Havi CASCO"),
|
||||
("TAX", 15000, "Cégautóadó (szimulált)"),
|
||||
("TOLL", 6500, "Éves matrica"),
|
||||
("CLEANING", 4500, "Külső-belső takarítás"),
|
||||
("PARKING", 1200, "Belvárosi zóna"),
|
||||
("OTHER", 2500, "Szélvédőmosó folyadék")
|
||||
]
|
||||
|
||||
for c_type, amount, desc in costs_data:
|
||||
db.add(AssetCost(
|
||||
asset_id=vehicle.id,
|
||||
organization_id=company_org.id,
|
||||
cost_type=c_type,
|
||||
amount=amount,
|
||||
currency="HUF",
|
||||
date=datetime.now(timezone.utc) - timedelta(days=2),
|
||||
specifications={"description": desc}
|
||||
))
|
||||
|
||||
await db.commit()
|
||||
logger.info("✅ Siker! A teljes flotta-ökoszisztéma üzemkész.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_test_scenario())
|
||||
31
backend/app/tests_internal/test_functional.py
Executable file
31
backend/app/tests_internal/test_functional.py
Executable file
@@ -0,0 +1,31 @@
|
||||
# /app/tests_internal/test_functional.py
|
||||
"""
|
||||
CÉL: Éles funkcionális teszt a bejelentkezési folyamathoz.
|
||||
"""
|
||||
import asyncio
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.services.auth_service import AuthService
|
||||
|
||||
async def test_login_flow():
|
||||
print("\n--- 🔑 FUNKCIONÁLIS LOGIN TESZT ---")
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. Keressünk egy teszt felhasználót
|
||||
result = await db.execute(select(User).limit(1))
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
print("❌ HIBA: Nincs felhasználó az adatbázisban. Futtass egy seeder-t!")
|
||||
return
|
||||
|
||||
print(f"👤 Tesztelés felhasználóval: {user.email}")
|
||||
|
||||
# 2. Próbáljunk meg egy 'hitelesítést' (jelszó ellenőrzés nélkül a DB szinten)
|
||||
if user.is_active:
|
||||
print(f"✅ SIKER: A(z) {user.email} fiók aktív és elérhető.")
|
||||
else:
|
||||
print(f"⚠️ FIGYELEM: A felhasználó létezik, de inaktív.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_login_flow())
|
||||
91
backend/app/tests_internal/test_gamification_flow.py
Executable file
91
backend/app/tests_internal/test_gamification_flow.py
Executable file
@@ -0,0 +1,91 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/test_gamification_flow.py
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from sqlalchemy import select
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Környezeti változók betöltése
|
||||
load_dotenv()
|
||||
|
||||
# MB2.0 Importok
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.system import UserStats, PointsLedger
|
||||
from app.services.social_service import SocialService
|
||||
from app.schemas.social import ServiceProviderCreate
|
||||
|
||||
# Naplózás beállítása
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Test: %(message)s')
|
||||
logger = logging.getLogger("Gamification-Test")
|
||||
|
||||
async def run_test():
|
||||
logger.info("🚀 Gamifikációs integrációs folyamat tesztelése...")
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
# 1. LOGIKA: Teszt felhasználó lekérése az identity sémából
|
||||
result = await db.execute(select(User).limit(1))
|
||||
user = result.scalars().first()
|
||||
|
||||
if not user:
|
||||
logger.error("❌ Hiba: Nincs felhasználó az adatbázisban. Futtasd a seed_system.py-t!")
|
||||
return
|
||||
|
||||
logger.info(f"👤 Aktív teszt alany: {user.email}")
|
||||
|
||||
# 2. LOGIKA: Új szolgáltató rögzítése (Trigger az XP szerzéshez)
|
||||
# A SocialService.create_service_provider automatikusan hívja a GamificationService-t
|
||||
unique_id = os.urandom(2).hex()
|
||||
test_provider = ServiceProviderCreate(
|
||||
name=f"Robot Szerviz {unique_id}",
|
||||
address="Alchemist utca 12.",
|
||||
category="service"
|
||||
)
|
||||
|
||||
logger.info(f"🛠️ Esemény kiváltása: '{test_provider.name}' rögzítése...")
|
||||
new_provider = await SocialService.create_service_provider(db, test_provider, user.id)
|
||||
|
||||
# Commit kényszerítése, hogy a háttérfolyamatok rögzüljenek
|
||||
await db.commit()
|
||||
logger.info(f"✅ Szolgáltató elfogadva (ID: {new_provider.id})")
|
||||
|
||||
# 3. LOGIKA: Eredmények ellenőrzése a Ledgerben (Főkönyv)
|
||||
# Újra lekérjük a statisztikákat a commit után
|
||||
stats_res = await db.execute(select(UserStats).where(UserStats.user_id == user.id))
|
||||
stats = stats_res.scalar_one_or_none()
|
||||
|
||||
ledger_res = await db.execute(
|
||||
select(PointsLedger)
|
||||
.where(PointsLedger.user_id == user.id)
|
||||
.order_by(PointsLedger.created_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
last_entry = ledger_res.scalars().first()
|
||||
|
||||
print("\n" + "═"*40)
|
||||
print("📊 INTEGRÁCIÓS JELENTÉS:")
|
||||
if stats:
|
||||
print(f"🏆 Aktuális XP: {stats.total_xp}")
|
||||
print(f"📈 Szint: {stats.current_level}")
|
||||
else:
|
||||
print("⚠️ UserStats rekord nem található!")
|
||||
|
||||
if last_entry:
|
||||
print(f"📝 Tranzakció oka: {last_entry.reason}")
|
||||
print(f"💰 XP változás: +{last_entry.points_change}")
|
||||
print("═"*40 + "\n")
|
||||
|
||||
if stats and stats.total_xp > 0:
|
||||
logger.info("✅ SIKER: A gamifikációs lánc éles és működik!")
|
||||
else:
|
||||
logger.warning("❌ HIBA: A pontszámítás nem történt meg.")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"💥 Kritikus hiba a teszt közben: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_test())
|
||||
33
backend/app/tests_internal/test_postgis.py
Executable file
33
backend/app/tests_internal/test_postgis.py
Executable file
@@ -0,0 +1,33 @@
|
||||
# app/tests_internal/test_postgis.py
|
||||
import asyncio
|
||||
from sqlalchemy import text
|
||||
from app.db.session import AsyncSessionLocal
|
||||
|
||||
async def test_geo_logic():
|
||||
"""
|
||||
THOUGHT PROCESS:
|
||||
Ellenőrizni kell, hogy a PostgreSQL-ben a 'data.branches' tábla 'location' oszlopa
|
||||
valóban GEOGRAPHY típusú-e, és az ST_Distance függvény működik-e.
|
||||
Ha ez elbukik, a 'search.py' nem fog eredményt adni.
|
||||
"""
|
||||
print("🌍 PostGIS távolságszámítás tesztelése...")
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
# Egy teszt pont (Budapest központ) és egy körzet lekérdezése
|
||||
query = text("""
|
||||
SELECT id, name,
|
||||
ST_Distance(location, ST_SetSRID(ST_MakePoint(19.0402, 47.4979), 4326)::geography) / 1000 as distance_km
|
||||
FROM data.branches
|
||||
LIMIT 1
|
||||
""")
|
||||
result = await db.execute(query)
|
||||
row = result.fetchone()
|
||||
if row:
|
||||
print(f"✅ SIKER: Találtunk egy ágat ({row.name}) {row.distance_km:.2f} km távolságra.")
|
||||
else:
|
||||
print("⚠️ FIGYELEM: A lekérdezés lefutott, de nincsenek adatok a data.branches táblában.")
|
||||
except Exception as e:
|
||||
print(f"❌ HIBA: A PostGIS lekérdezés elbukott. Oka: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_geo_logic())
|
||||
Reference in New Issue
Block a user