create_metadata_review_tables.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env python3
  2. """
  3. Create Metadata Review tables in PostgreSQL (production by default).
  4. Tables:
  5. - public.metadata_review_records
  6. - public.metadata_version_history
  7. This script executes the SQL files under /database:
  8. - database/create_metadata_review_records_table.sql
  9. - database/create_metadata_version_history_table.sql
  10. Usage:
  11. python scripts/create_metadata_review_tables.py
  12. python scripts/create_metadata_review_tables.py --db-uri postgresql://...
  13. python scripts/create_metadata_review_tables.py --env production
  14. python scripts/create_metadata_review_tables.py --dry-run
  15. """
  16. from __future__ import annotations
  17. import argparse
  18. import logging
  19. import sys
  20. from pathlib import Path
  21. from typing import Optional
  22. logging.basicConfig(
  23. level=logging.INFO,
  24. format="%(asctime)s - %(levelname)s - %(message)s",
  25. )
  26. logger = logging.getLogger("CreateMetadataReviewTables")
  27. WORKSPACE_ROOT = Path(__file__).resolve().parent.parent
  28. SQL_REVIEW = WORKSPACE_ROOT / "database" / "create_metadata_review_records_table.sql"
  29. SQL_HISTORY = (
  30. WORKSPACE_ROOT / "database" / "create_metadata_version_history_table.sql"
  31. )
  32. def load_db_uri(env: str, override_uri: Optional[str]) -> str:
  33. if override_uri:
  34. return override_uri
  35. sys.path.insert(0, str(WORKSPACE_ROOT))
  36. from app.config.config import config # type: ignore
  37. if env not in config:
  38. raise ValueError(f"Unknown env: {env}. Must be one of: {list(config.keys())}")
  39. return config[env].SQLALCHEMY_DATABASE_URI
  40. def execute_sql_file(db_uri: str, sql_path: Path, dry_run: bool) -> None:
  41. if not sql_path.exists():
  42. raise FileNotFoundError(f"SQL file not found: {sql_path}")
  43. sql = sql_path.read_text(encoding="utf-8")
  44. logger.info(f"Applying SQL: {sql_path.relative_to(WORKSPACE_ROOT)}")
  45. if dry_run:
  46. logger.info("dry-run enabled; skipping execution.")
  47. return
  48. import psycopg2 # type: ignore
  49. conn = psycopg2.connect(db_uri)
  50. try:
  51. with conn:
  52. with conn.cursor() as cur:
  53. cur.execute(sql)
  54. logger.info("OK")
  55. finally:
  56. conn.close()
  57. def main() -> None:
  58. parser = argparse.ArgumentParser(
  59. description="Create metadata review tables in PostgreSQL",
  60. )
  61. parser.add_argument(
  62. "--env",
  63. default="production",
  64. help="Config env name in app.config.config (default: production)",
  65. )
  66. parser.add_argument(
  67. "--db-uri",
  68. default=None,
  69. help="Override database URI (postgresql://...)",
  70. )
  71. parser.add_argument(
  72. "--dry-run",
  73. action="store_true",
  74. help="Print actions without executing SQL",
  75. )
  76. args = parser.parse_args()
  77. db_uri = load_db_uri(args.env, args.db_uri)
  78. safe_target = db_uri.split("@")[-1] if "@" in db_uri else db_uri
  79. logger.info(f"Target database: {safe_target}")
  80. execute_sql_file(db_uri, SQL_REVIEW, dry_run=bool(args.dry_run))
  81. execute_sql_file(db_uri, SQL_HISTORY, dry_run=bool(args.dry_run))
  82. logger.info("All done.")
  83. if __name__ == "__main__":
  84. main()