"""Проверка какие миграции 028..054 НЕ применены.""" from __future__ import annotations import os import psycopg dsn = os.environ["DATABASE_URL"].replace("postgresql+psycopg://", "postgresql://") conn = psycopg.connect(dsn) cur = conn.cursor() # Маркеры по миграциям (table/column/index/etc) CHECKS = [ ("028 matching_tables", "table", "house_sources"), ("029 extend matching/valuation/dynamics", "column", ("listing_sources", "price_divergence_pct")), ("030 avito_imv cache_key unique", "index_unique", ("avito_imv_evaluations", "cache_key")), ("030 listings_alter_yandex", "column", ("listings", "yandex_id")), ("031 houses_alter_yandex", "column", ("houses", "yandex_id")), ("032 yandex_history", "table", "yandex_house_history"), ("040 houses_extend", "column", ("houses", "year_renovated")), ("041 house_sources noop", "noop", None), ("042 listing_sources price_divergence_idx", "index", "listing_sources_price_divergence_idx"), ("043 house_reviews_extend", "column", ("house_reviews", "review_text")), ("044 external_valuations_link", "column", ("external_valuations", "link_url")), ("045 house_placement_history_extend", "column", ("house_placement_history", "first_seen_at")), ("046 views", "view", "v_house_canonical"), ("050 search_optimization", "matview", "listings_search_mv"), ("051 scrape_runs_extend", "column", ("scrape_runs", "heartbeat_at")), ("052 scrape_schedules", "table", "scrape_schedules"), ("053 scraper_settings", "table", "scraper_settings"), ("054 scraper_settings_global", "row", ("scraper_settings", "source='global'")), ] def check(kind: str, target) -> bool: if kind == "noop": return True if kind == "table": cur.execute( "SELECT 1 FROM information_schema.tables WHERE table_schema='public' AND table_name=%s", (target,), ) elif kind == "view": cur.execute( "SELECT 1 FROM information_schema.views WHERE table_schema='public' AND table_name=%s", (target,), ) elif kind == "matview": cur.execute("SELECT 1 FROM pg_matviews WHERE matviewname=%s", (target,)) elif kind == "column": tbl, col = target cur.execute( "SELECT 1 FROM information_schema.columns WHERE table_name=%s AND column_name=%s", (tbl, col), ) elif kind == "index": cur.execute("SELECT 1 FROM pg_indexes WHERE indexname=%s", (target,)) elif kind == "index_unique": tbl, col = target cur.execute( """SELECT 1 FROM pg_indexes WHERE tablename=%s AND indexdef ~ %s""", (tbl, f"UNIQUE.*{col}"), ) elif kind == "row": tbl, where = target cur.execute(f"SELECT 1 FROM {tbl} WHERE {where}") else: return False return cur.fetchone() is not None print(f"{'Migration':<48} Status") print("-" * 60) missing = [] for name, kind, target in CHECKS: try: ok = check(kind, target) except Exception as e: # noqa: BLE001 ok = False print(f"{name:<48} ✗ ERROR: {type(e).__name__}: {str(e)[:50]}") conn.rollback() missing.append(name) continue status = "✓" if ok else "✗ MISSING" print(f"{name:<48} {status}") if not ok: missing.append(name) print(f"\nMissing: {len(missing)}/{len(CHECKS)}") for m in missing: print(f" - {m}")