Files
service-finder/backend/app/scripts/sync_engine.py
2026-03-22 11:02:05 +00:00

153 lines
6.6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# /opt/docker/dev/service_finder/backend/app/scripts/sync_engine.py
#!/usr/bin/env python3
# docker exec -it sf_api python -m app.scripts.sync_engine
import asyncio
import importlib
import sys
from pathlib import Path
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy import inspect, text
from sqlalchemy.schema import CreateTable
# Path beállítása
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from app.database import Base
from app.core.config import settings
def dynamic_import_models():
"""Modellek betöltése a Metadata feltöltéséhez."""
models_dir = Path(__file__).parent.parent / "models"
# Rekurzív bejárás az alkönyvtárakkal együtt
for py_file in models_dir.rglob("*.py"):
if py_file.name == "__init__.py": continue
# Számítsuk ki a modulnevet a models könyvtárhoz képest
relative_path = py_file.relative_to(models_dir)
# Konvertáljuk path-t modulná: pl. identity/identity.py -> identity.identity
module_stem = str(relative_path).replace('/', '.').replace('\\', '.')[:-3] # eltávolítjuk a .py-t
module_name = f"app.models.{module_stem}"
try:
importlib.import_module(module_name)
except Exception as e:
# Csak debug célra
print(f"Failed to import {module_name}: {e}")
pass
async def perform_detailed_audit():
engine = create_async_engine(str(settings.SQLALCHEMY_DATABASE_URI))
# Audit számlálók
stats = {"ok": 0, "fixed": 0, "extra": 0, "missing": 0}
def audit_logic(connection):
inspector = inspect(connection)
metadata = Base.metadata
db_schemas = inspector.get_schema_names()
model_schemas = sorted({t.schema for t in metadata.sorted_tables if t.schema})
print("\n" + "="*80)
print(f"{'🔍 RÉSZLETES SCHEMA AUDIT JELENTÉS':^80}")
print("="*80)
# --- A IRÁNY: KÓD -> ADATBÁZIS (Minden ellenőrzése) ---
print(f"\n[A IRÁNY: Kód (SQLAlchemy) -> Adatbázis (PostgreSQL)]")
print("-" * 50)
for schema in model_schemas:
# 1. Séma ellenőrzése
if schema not in db_schemas:
print(f"❌ HIÁNYZIK: Séma [{schema}] -> Létrehozás...")
connection.execute(text(f'CREATE SCHEMA IF NOT EXISTS "{schema}"'))
stats["fixed"] += 1
else:
print(f"✅ RENDBEN: Séma [{schema}] létezik.")
stats["ok"] += 1
db_tables = inspector.get_table_names(schema=schema)
model_tables = [t for t in metadata.sorted_tables if t.schema == schema]
for table in model_tables:
full_name = f"{schema}.{table.name}"
# 2. Tábla ellenőrzése
if table.name not in db_tables:
print(f" ❌ HIÁNYZIK: Tábla [{full_name}] -> Létrehozás...")
connection.execute(CreateTable(table))
stats["fixed"] += 1
continue
else:
print(f" ✅ RENDBEN: Tábla [{full_name}] létezik.")
stats["ok"] += 1
# 3. Oszlopok ellenőrzése
db_cols = {c['name']: c for c in inspector.get_columns(table.name, schema=schema)}
for col in table.columns:
col_path = f"{full_name}.{col.name}"
if col.name not in db_cols:
print(f" ❌ HIÁNYZIK: Oszlop [{col_path}] -> Hozzáadás...")
col_type = col.type.compile(dialect=connection.dialect)
default_sql = ""
if col.server_default is not None:
arg = col.server_default.arg
val = arg.text if hasattr(arg, 'text') else str(arg)
default_sql = f" DEFAULT {val}"
null_sql = " NOT NULL" if not col.nullable else ""
connection.execute(text(f'ALTER TABLE "{schema}"."{table.name}" ADD COLUMN "{col.name}" {col_type}{default_sql}{null_sql}'))
stats["fixed"] += 1
else:
print(f" ✅ RENDBEN: Oszlop [{col_path}]")
stats["ok"] += 1
# --- B IRÁNY: ADATBÁZIS -> KÓD (Árnyék adatok keresése) ---
print(f"\n[B IRÁNY: Adatbázis -> Kód (Extra elemek keresése)]")
print("-" * 50)
for schema in model_schemas:
if schema not in db_schemas: continue
db_tables = inspector.get_table_names(schema=schema)
model_table_names = {t.name for t in metadata.sorted_tables if t.schema == schema}
for db_table in db_tables:
# Ignore deprecated tables (ending with _deprecated)
if db_table.endswith("_deprecated"):
continue
full_db_name = f"{schema}.{db_table}"
if db_table not in model_table_names:
print(f" ⚠️ EXTRA TÁBLA: [{full_db_name}] (Nincs a kódban!)")
stats["extra"] += 1
else:
# Extra oszlopok a táblán belül
db_cols = inspector.get_columns(db_table, schema=schema)
model_col_names = {c.name for c in metadata.tables[full_db_name].columns}
for db_col in db_cols:
col_name = db_col['name']
if col_name not in model_col_names:
print(f" ⚠️ EXTRA OSZLOP: [{full_db_name}.{col_name}]")
stats["extra"] += 1
# --- ÖSSZESÍTŐ ---
print("\n" + "="*80)
print(f"{'📊 AUDIT ÖSSZESÍTŐ':^80}")
print("="*80)
print(f" ✅ Megfelelt (OK): {stats['ok']:>4} elem")
print(f" ❌ Javítva/Pótolva (Fixed): {stats['fixed']:>4} elem")
print(f" ⚠️ Extra (Shadow Data): {stats['extra']:>4} elem")
print("-" * 80)
if stats["fixed"] == 0 and stats["extra"] == 0:
print(f"{'✨ A RENDSZER TÖKÉLETESEN SZINKRONBAN VAN!':^80}")
else:
print(f"{' A rendszer üzemkész, de nézd át az extra (Shadow) elemeket!':^80}")
print("="*80 + "\n")
async with engine.begin() as conn:
await conn.run_sync(audit_logic)
await engine.dispose()
async def main():
dynamic_import_models()
await perform_detailed_audit()
if __name__ == "__main__":
asyncio.run(main())