153 lines
6.6 KiB
Python
153 lines
6.6 KiB
Python
# /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()) |