Repository navigation
[389] Support ingest from postgresql with testing - #477
dbashford-NOAA wants to merge 50 commits into
Conversation
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
|
@leewujung Let me know if there is anything here I should change. |
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.6 → v0.15.7](astral-sh/ruff-pre-commit@v0.15.6...v0.15.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.7 → v0.15.8](astral-sh/ruff-pre-commit@v0.15.7...v0.15.8) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.8 → v0.15.9](astral-sh/ruff-pre-commit@v0.15.8...v0.15.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
…#485) Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](pypa/gh-action-pypi-publish@v1.13.0...v1.14.0) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.9 → v0.15.10](astral-sh/ruff-pre-commit@v0.15.9...v0.15.10) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
|
@dbashford-NOAA : Agreed! I just closed #415. |
leewujung
left a comment
There was a problem hiding this comment.
Thanks @dbashford-NOAA ! I think the code/tests look good on my side. Just requested @aliciabillings-noaa to review. :)
* Fix number proportions normalization * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Quick addendum to `hake_2019.py` with associated workflow and bug-fixes. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.10 → v0.15.11](astral-sh/ruff-pre-commit@v0.15.10...v0.15.11) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.11 → v0.15.12](astral-sh/ruff-pre-commit@v0.15.11...v0.15.12) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.12 → v0.15.13](astral-sh/ruff-pre-commit@v0.15.12...v0.15.13) - [github.com/psf/black-pre-commit-mirror: 26.3.1 → 26.5.0](psf/black-pre-commit-mirror@26.3.1...26.5.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
…project-metadata changing the org name path
* just changing the org name! * Migrate dependency management to pyproject.toml - Move dev/docs deps to pyproject extras - Update CI and RTD install workflows - Remove legacy requirements/config files - Refresh installation documentation * Update pyproject.toml Co-authored-by: Brandyn Lucca <45861253+brandynlucca@users.noreply.github.com> * Re-adding missing comma --------- Co-authored-by: Brandyn Lucca <45861253+brandynlucca@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.13 → v0.15.14](astral-sh/ruff-pre-commit@v0.15.13...v0.15.14) - [github.com/psf/black-pre-commit-mirror: 26.5.0 → 26.5.1](psf/black-pre-commit-mirror@26.5.0...26.5.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.14 → v0.15.15](astral-sh/ruff-pre-commit@v0.15.14...v0.15.15) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.15 → v0.15.16](astral-sh/ruff-pre-commit@v0.15.15...v0.15.16) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.16 → v0.15.18](astral-sh/ruff-pre-commit@v0.15.16...v0.15.18) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.18 → v0.15.20](astral-sh/ruff-pre-commit@v0.15.18...v0.15.20) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v6...v7) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.14.0 to 1.14.1. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](pypa/gh-action-pypi-publish@v1.14.0...v1.14.1) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.20 → v0.16.0](astral-sh/ruff-pre-commit@v0.15.20...v0.16.0) - [github.com/codespell-project/codespell: v2.4.2 → v2.4.3](codespell-project/codespell@v2.4.2...v2.4.3)
…-update-config [pre-commit.ci] pre-commit autoupdate
…thub_actions/actions/setup-python-7 Bump actions/setup-python from 6 to 7
…thub_actions/pypa/gh-action-pypi-publish-1.14.1 Bump pypa/gh-action-pypi-publish from 1.14.0 to 1.14.1
…#511) Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.14.1 to 1.14.2. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](pypa/gh-action-pypi-publish@v1.14.1...v1.14.2) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.16.0 → v0.16.1](astral-sh/ruff-pre-commit@v0.16.0...v0.16.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.16.1 → v0.16.2](astral-sh/ruff-pre-commit@v0.16.1...v0.16.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.16.2 → v0.16.3](astral-sh/ruff-pre-commit@v0.16.2...v0.16.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.16.3 → v0.16.4](astral-sh/ruff-pre-commit@v0.16.3...v0.16.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
updates: - [github.com/astral-sh/ruff-pre-commit: v0.16.4 → v0.16.5](astral-sh/ruff-pre-commit@v0.16.4...v0.16.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
# Conflicts: # echopop/ingest/biological.py # requirements-dev.txt # requirements.txt
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
…io_ingest # Conflicts: # requirements-dev.txt # requirements.txt
There was a problem hiding this comment.
@dbashford-NOAA I suggested adding requires-python/python-version fields in pr.yaml and project.toml. That should allow the tests to pass for 3.14; effecting those changes allowed my local tox tests to pass.
There was one exception: I was not able to run the acutal PostgreSQL database tests since I can't run Docker locally. Could we make the database tests runnable locally without Docker? Currently, the fixture skips all three when Docker or testcontainers is unavailable, so a passing local run can leave database ingestion untested. That makes it harder for contributors to catch regressions before submitting changes.
I suggest using an automatically managed local PostgreSQL instance while retaining Docker coverage in CI. Both paths could reuse the existing tests and seed data, allowing contributors to verify the same behavior without manually configuring a database. Setup failures should also produce errors rather than skips, so broken infrastructure cannot silently reduce coverage.
I tested this approach locally and the three previously skipped database tests passed.
| @@ -18,19 +18,29 @@ jobs: | |||
| python-version: ["3.12", "3.13"] | |||
There was a problem hiding this comment.
| python-version: ["3.12", "3.13"] | |
| python-version: ["3.12", "3.13", "3.14"] |
| "Programming Language :: Python :: 3.12", | ||
| "Topic :: Scientific/Engineering" | ||
| ] | ||
| requires-python = ">=3.12, <3.14" |
There was a problem hiding this comment.
| requires-python = ">=3.12, <3.15" |
| pip install -e . | ||
| python -m pip install -e ".[dev]" | ||
| - name: Test with pytest | ||
| run: | |
There was a problem hiding this comment.
| run: | | |
| # Fixtures also exercise Docker automatically on Ubuntu runners. | |
| run: | |
| - name: Setup PostgreSQL Binaries | ||
| # GitHub action to set up postgreSQL for all 3 platforms | ||
| uses: ikalnytskyi/action-setup-postgres@v8 | ||
| with: | ||
| username: test_user | ||
| password: postgres | ||
| database: test | ||
| port: 5432 | ||
| postgres-version: '14' | ||
| id: postgres |
There was a problem hiding this comment.
| - name: Setup PostgreSQL Binaries | |
| # GitHub action to set up postgreSQL for all 3 platforms | |
| uses: ikalnytskyi/action-setup-postgres@v8 | |
| with: | |
| username: test_user | |
| password: postgres | |
| database: test | |
| port: 5432 | |
| postgres-version: '14' | |
| id: postgres |
See my overall comment concerning this.
| dev = [ | ||
| "black", | ||
| "coverage", | ||
| "isort", |
There was a problem hiding this comment.
| "isort", | |
| "isort", | |
| "pgembed==0.2.0", |
| @@ -1,5 +1,92 @@ | |||
| import os | |||
There was a problem hiding this comment.
| import os | |
| import os | |
| import sys | |
| from importlib.resources import files |
| from pathlib import Path | ||
|
|
||
| import pandas as pd | ||
| import pytest |
There was a problem hiding this comment.
| import pytest | |
| import pytest | |
| from pytest_postgresql import factories |
| TEST_DATA_ROOT = HERE.parent / "test_data" | ||
| TEST_SQL_FILE = TEST_DATA_ROOT / "ingest" / "test_bio_data.sql" | ||
|
|
||
|
|
There was a problem hiding this comment.
| # Bundled binaries let plain pytest run locally without Docker or server setup. | |
| native_postgres_process = factories.postgresql_proc( | |
| executable=str( | |
| files("pgembed") / "pginstall" / "bin" / ("pg_ctl.exe" if os.name == "nt" else "pg_ctl") | |
| ), | |
| host="127.0.0.1", | |
| port=None, | |
| user="test_user", | |
| password="postgres", | |
| dbname="test", | |
| ) | |
| native_postgres = factories.postgresql("native_postgres_process") | |
| """ | ||
| Session-scoped fixture to get database connection. | ||
|
|
||
| - In GitHub Actions: Uses the postgres service from the workflow | ||
| - Locally: Uses Testcontainers if Docker is available, skips if not | ||
| """ | ||
| is_github_action = os.environ.get("GITHUB_ACTIONS") | ||
|
|
||
| if is_github_action: | ||
| # In GitHub Actions use the postgres service from workflow | ||
| yield type( | ||
| "obj", | ||
| (object,), | ||
| { | ||
| "get_connection_url": lambda self: "postgresql+psycopg://test_user:postgres@localhost:5432/test", | ||
| "get_container_host_ip": lambda self: "localhost", | ||
| "get_exposed_port": lambda self, port: 5432, | ||
| }, | ||
| )() | ||
| else: | ||
| # Local development | ||
| try: | ||
| from testcontainers.postgres import PostgresContainer | ||
|
|
||
| container = PostgresContainer( | ||
| image="postgres:16", username="test_user", password="postgres", dbname="test" | ||
| ) | ||
| container.start() | ||
| yield container | ||
| container.stop() | ||
| except Exception as e: | ||
| # Docker not available - skip integration tests | ||
| pytest.skip(f"Docker must be running for Testcontainers: {e}") |
There was a problem hiding this comment.
| """ | |
| Session-scoped fixture to get database connection. | |
| - In GitHub Actions: Uses the postgres service from the workflow | |
| - Locally: Uses Testcontainers if Docker is available, skips if not | |
| """ | |
| is_github_action = os.environ.get("GITHUB_ACTIONS") | |
| if is_github_action: | |
| # In GitHub Actions use the postgres service from workflow | |
| yield type( | |
| "obj", | |
| (object,), | |
| { | |
| "get_connection_url": lambda self: "postgresql+psycopg://test_user:postgres@localhost:5432/test", | |
| "get_container_host_ip": lambda self: "localhost", | |
| "get_exposed_port": lambda self, port: 5432, | |
| }, | |
| )() | |
| else: | |
| # Local development | |
| try: | |
| from testcontainers.postgres import PostgresContainer | |
| container = PostgresContainer( | |
| image="postgres:16", username="test_user", password="postgres", dbname="test" | |
| ) | |
| container.start() | |
| yield container | |
| container.stop() | |
| except Exception as e: | |
| # Docker not available - skip integration tests | |
| pytest.skip(f"Docker must be running for Testcontainers: {e}") | |
| """Exercise the existing PostgreSQL container on Linux CI.""" | |
| from testcontainers.community.postgres import PostgresContainer | |
| with PostgresContainer( | |
| image="postgres:16", | |
| username="test_user", | |
| password="postgres", | |
| dbname="test", | |
| driver="psycopg", | |
| ) as container: | |
| yield container |
| @pytest.fixture(scope="session") | ||
| def database_credentials(postgres_container): | ||
| """ | ||
| Session-scoped fixture to: | ||
| 1. Connect to the PostgreSQL database (CI or local). | ||
| 2. Load 'test_bio_data.sql' into it. | ||
| 3. Yield the credentials dictionary in the format expected by load_biodata_db_views. | ||
|
|
||
| Returns dict with keys: host, port, dbname, user, password, schema | ||
| """ | ||
|
|
||
| host = postgres_container.get_container_host_ip() | ||
| port = postgres_container.get_exposed_port(5432) | ||
|
|
||
| creds = { | ||
| "host": host, | ||
| "port": port, | ||
| "dbname": "test", | ||
| "user": "test_user", | ||
| "password": "postgres", | ||
| "schema": "public", | ||
| } | ||
|
|
||
| db_url = ( | ||
| f"postgresql+psycopg://" | ||
| f"{creds['user']}:{creds['password']}@" | ||
| f"{creds['host']}:{creds['port']}/" | ||
| f"{creds['dbname']}" | ||
| ) | ||
|
|
||
| try: | ||
| engine = create_engine(db_url) | ||
| with engine.begin() as connection: | ||
| with open(TEST_SQL_FILE) as f: | ||
| sql_script = f.read() | ||
| connection.execute(text(sql_script)) | ||
| except Exception as e: | ||
| pytest.fail(f"Failed to load {TEST_SQL_FILE}: {e}") | ||
|
|
||
| yield creds |
There was a problem hiding this comment.
| @pytest.fixture(scope="session") | |
| def database_credentials(postgres_container): | |
| """ | |
| Session-scoped fixture to: | |
| 1. Connect to the PostgreSQL database (CI or local). | |
| 2. Load 'test_bio_data.sql' into it. | |
| 3. Yield the credentials dictionary in the format expected by load_biodata_db_views. | |
| Returns dict with keys: host, port, dbname, user, password, schema | |
| """ | |
| host = postgres_container.get_container_host_ip() | |
| port = postgres_container.get_exposed_port(5432) | |
| creds = { | |
| "host": host, | |
| "port": port, | |
| "dbname": "test", | |
| "user": "test_user", | |
| "password": "postgres", | |
| "schema": "public", | |
| } | |
| db_url = ( | |
| f"postgresql+psycopg://" | |
| f"{creds['user']}:{creds['password']}@" | |
| f"{creds['host']}:{creds['port']}/" | |
| f"{creds['dbname']}" | |
| ) | |
| try: | |
| engine = create_engine(db_url) | |
| with engine.begin() as connection: | |
| with open(TEST_SQL_FILE) as f: | |
| sql_script = f.read() | |
| connection.execute(text(sql_script)) | |
| except Exception as e: | |
| pytest.fail(f"Failed to load {TEST_SQL_FILE}: {e}") | |
| yield creds | |
| @pytest.fixture( | |
| params=( | |
| ["native", "docker"] | |
| if os.environ.get("GITHUB_ACTIONS") == "true" and sys.platform == "linux" | |
| else ["native"] | |
| ) | |
| ) | |
| def database_credentials(request): | |
| """ | |
| Fixture to: | |
| 1. Start native PostgreSQL locally and on CI; also test Docker on Linux CI. | |
| 2. Load 'test_bio_data.sql' into it. | |
| 3. Return the credentials dictionary expected by load_biodata_db_views. | |
| Returns dict with keys: host, port, dbname, user, password, schema | |
| """ | |
| backend = request.param | |
| if backend == "native": | |
| connection = request.getfixturevalue("native_postgres") | |
| host, port = connection.info.host, connection.info.port | |
| dbname = connection.info.dbname | |
| else: | |
| container = request.getfixturevalue("postgres_container") | |
| host, port = container.get_container_host_ip(), container.get_exposed_port(5432) | |
| dbname = "test" | |
| creds = { | |
| "host": host, | |
| "port": port, | |
| "dbname": dbname, | |
| "user": "test_user", | |
| "password": "postgres", | |
| "schema": "public", | |
| } | |
| db_url = ( | |
| f"postgresql+psycopg://" | |
| f"{creds['user']}:{creds['password']}@" | |
| f"{creds['host']}:{creds['port']}/" | |
| f"{creds['dbname']}" | |
| ) | |
| engine = create_engine(db_url) | |
| try: | |
| with engine.begin() as connection: | |
| with open(TEST_SQL_FILE, encoding="utf-8") as f: | |
| sql_script = f.read() | |
| connection.execute(text(sql_script)) | |
| except Exception as e: | |
| pytest.fail(f"Failed to load {TEST_SQL_FILE}: {e}") | |
| finally: | |
| engine.dispose() | |
| return creds |
Resolves #389
Added a function to load bio data from a postgres db as an alternative to loading from a CSV
Cleaned version of PR #415