Download database.py from larxius/larmedix: direct link, hf CLI and curl.
- Browser
- Download file 5.42 kB
-
https://huggingface.co/spaces/larxius/larmedix/resolve/main/database.py
- Command line
-
hf download hf://spaces/larxius/larmedix/database.py
-
curl -L -o database.py https://huggingface.co/spaces/larxius/larmedix/resolve/main/database.py
5.42 kB
| """ | |
| Separate database configuration for GCP Cloud SQL PostgreSQL. | |
| Instance : larmedix-db | |
| Database : larmedix | |
| Priority order for connection: | |
| 1. DATABASE_URL env var (recommended for HuggingFace Secrets / GCP) | |
| e.g. postgresql+psycopg2://USER:PASSWORD@HOST:5432/larmedix | |
| 2. Cloud SQL Python Connector (if USE_CLOUD_SQL_CONNECTOR=true) | |
| Requires: INSTANCE_CONNECTION_NAME="PROJECT:REGION:larmedix-db" | |
| 3. TCP / Unix-socket from individual env vars: | |
| DB_USER, DB_PASSWORD, DB_HOST (public IP or 127.0.0.1 via proxy), | |
| DB_PORT (default 5432), DB_NAME (default larmedix), | |
| DB_UNIX_SOCKET (e.g. /cloudsql/PROJECT:REGION:larmedix-db) | |
| 4. Local SQLite fallback (dev only): sqlite:///hospital.db | |
| On first successful connection, init_database(app) automatically | |
| creates all tables (db.create_all()) and seeds the superadmin user. | |
| We will plug real GCP credentials in later via env vars / secrets — | |
| no code change needed. | |
| """ | |
| import os | |
| # ---- GCP defaults (instance / database names are fixed) ---- | |
| GCP_INSTANCE_NAME = os.environ.get("GCP_INSTANCE_NAME", "larmedix-db") | |
| DEFAULT_DB_NAME = os.environ.get("DB_NAME", "larmedix") | |
| def get_database_uri(): | |
| """Build the SQLAlchemy database URI from environment.""" | |
| # 1. Full URL wins (HF Space Secrets -> DATABASE_URL) | |
| database_url = os.environ.get("DATABASE_URL") | |
| if database_url: | |
| # Some providers give postgres:// -> fix to postgresql:// | |
| if database_url.startswith("postgres://"): | |
| database_url = database_url.replace("postgres://", "postgresql://", 1) | |
| # Default to psycopg2 driver if no driver specified | |
| if database_url.startswith("postgresql://"): | |
| database_url = database_url.replace( | |
| "postgresql://", "postgresql+psycopg2://", 1 | |
| ) | |
| return database_url | |
| # 2. Individual parts -> build TCP URL (Cloud SQL public IP / proxy) | |
| db_user = os.environ.get("DB_USER") | |
| db_password = os.environ.get("DB_PASSWORD", "") | |
| db_host = os.environ.get("DB_HOST") # Cloud SQL public IP | |
| db_port = os.environ.get("DB_PORT", "5432") | |
| db_name = os.environ.get("DB_NAME", DEFAULT_DB_NAME) | |
| # Unix socket path (Cloud Run / GCE with cloud-sql-proxy): | |
| # e.g. /cloudsql/PROJECT:REGION:larmedix-db | |
| unix_socket = os.environ.get("DB_UNIX_SOCKET") | |
| if db_user and unix_socket: | |
| # Connect via Unix domain socket | |
| return ( | |
| f"postgresql+psycopg2://{db_user}:{db_password}@/{db_name}" | |
| f"?host={unix_socket}" | |
| ) | |
| if db_user and db_host: | |
| return ( | |
| f"postgresql+psycopg2://{db_user}:{db_password}" | |
| f"@{db_host}:{db_port}/{db_name}" | |
| ) | |
| # 3. Local fallback (dev / HF build without secrets) | |
| return "sqlite:///hospital.db" | |
| def is_postgres(uri: str) -> bool: | |
| return uri.startswith("postgresql") or uri.startswith("postgres") | |
| def get_engine_options(uri: str) -> dict: | |
| """Sensible pool defaults for Cloud SQL Postgres.""" | |
| if is_postgres(uri): | |
| return { | |
| "pool_size": int(os.environ.get("DB_POOL_SIZE", "5")), | |
| "max_overflow": int(os.environ.get("DB_MAX_OVERFLOW", "10")), | |
| "pool_timeout": 30, | |
| "pool_recycle": 1800, | |
| "pool_pre_ping": True, | |
| } | |
| # SQLite (local dev) | |
| return {"connect_args": {"timeout": 30}} | |
| def init_database(app): | |
| """ | |
| Call once inside app context after db.init_app(app). | |
| Automatically creates all tables on first connect + seeds superadmin. | |
| Safe to call on every startup (create_all is idempotent). | |
| """ | |
| # Import here to avoid circular imports (models defines db) | |
| from models import db, User | |
| from werkzeug.security import generate_password_hash | |
| with app.app_context(): | |
| db.create_all() | |
| # Auto-migrate: older Postgres DBs have user.password VARCHAR(150), | |
| # but Werkzeug scrypt hashes are ~160+ chars. Widen if needed. | |
| # Also widen filepath columns for Firebase Storage URLs. | |
| try: | |
| from sqlalchemy import text | |
| if is_postgres(str(db.engine.url)): | |
| with db.engine.begin() as conn: | |
| conn.execute(text( | |
| 'ALTER TABLE "user" ALTER COLUMN password TYPE VARCHAR(500)' | |
| )) | |
| conn.execute(text( | |
| 'ALTER TABLE report ALTER COLUMN filepath TYPE VARCHAR(1024)' | |
| )) | |
| conn.execute(text( | |
| 'ALTER TABLE patient_profile ALTER COLUMN insurance_document TYPE VARCHAR(1024)' | |
| )) | |
| except Exception as e: | |
| print(f"column migrate skipped: {e}") | |
| # Superadmin lives in the DB (User table, role=superadmin). | |
| # Seeded once with defaults; afterwards DB is source of truth. | |
| # Changing Secrets will NOT overwrite it — edit via SQL/app. | |
| sa = User.query.filter_by(role="superadmin").first() | |
| if not sa: | |
| db.session.add( | |
| User( | |
| email="superadmin@system.com", | |
| username="superadmin", | |
| password=generate_password_hash("superadmin123"), | |
| role="superadmin", | |
| hospital_id=None, | |
| ) | |
| ) | |
| db.session.commit() | |
| print("Superadmin seeded (superadmin@system.com).") | |