From 6a13f59c2541386f165ab304d32fa547e79e216c Mon Sep 17 00:00:00 2001 From: Kyle Bittinger Date: Sat, 28 Mar 2026 09:33:07 -0400 Subject: [PATCH 1/5] Use in-memory database when SQLALCHEMY_DATABASE_URI is missing --- sample_registry/__init__.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/sample_registry/__init__.py b/sample_registry/__init__.py index c19644e..5ba7f1b 100644 --- a/sample_registry/__init__.py +++ b/sample_registry/__init__.py @@ -30,26 +30,13 @@ def sample_registry_version(): sys.stderr.write(__version__) - -try: - SQLALCHEMY_DATABASE_URI = os.environ["SAMPLE_REGISTRY_DB_URI"] -except KeyError: +SQLALCHEMY_DATABASE_URI = os.environ.get("SAMPLE_REGISTRY_DB_URI") +if SQLALCHEMY_DATABASE_URI is None: sys.stdout.write( - "Missing database connection information in environment, using test SQLite database\n" - ) - SQLALCHEMY_DATABASE_URI = ( - f"sqlite:///{Path(__file__).parent.parent.resolve()}/sample_registry.sqlite" - ) - - -if "PYTEST_VERSION" in os.environ: - # Set SQLALCHEMY_DATABASE_URI to an in-memory SQLite database for testing + "SAMPLE_REGISTRY_DB_URI not defined in environment, " + "using in-memory SQLite database\n") SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" - -sys.stderr.write(f"Connecting to database at {SQLALCHEMY_DATABASE_URI}\n") engine = create_engine(SQLALCHEMY_DATABASE_URI) - -# Create database session Session = sessionmaker(bind=engine) session = Session() From bb6ad0a40c1d92816794474709a9d5e65d31454f Mon Sep 17 00:00:00 2001 From: Kyle Bittinger Date: Sat, 28 Mar 2026 15:02:24 -0400 Subject: [PATCH 2/5] Create tables in database if no URI sepcified --- sample_registry/__init__.py | 3 ++- sample_registry/app.py | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sample_registry/__init__.py b/sample_registry/__init__.py index 5ba7f1b..bbe528a 100644 --- a/sample_registry/__init__.py +++ b/sample_registry/__init__.py @@ -31,7 +31,8 @@ def sample_registry_version(): sys.stderr.write(__version__) SQLALCHEMY_DATABASE_URI = os.environ.get("SAMPLE_REGISTRY_DB_URI") -if SQLALCHEMY_DATABASE_URI is None: +DATABASE_URI_MISSING = SQLALCHEMY_DATABASE_URI is None +if DATABASE_URI_MISSING: sys.stdout.write( "SAMPLE_REGISTRY_DB_URI not defined in environment, " "using in-memory SQLite database\n") diff --git a/sample_registry/app.py b/sample_registry/app.py index b9323f2..8e14a83 100644 --- a/sample_registry/app.py +++ b/sample_registry/app.py @@ -18,7 +18,10 @@ from contextlib import contextmanager from io import StringIO from pathlib import Path -from sample_registry import ARCHIVE_ROOT, SQLALCHEMY_DATABASE_URI +import sys +from sample_registry import ( + ARCHIVE_ROOT, SQLALCHEMY_DATABASE_URI, DATABASE_URI_MISSING, + ) from sample_registry.mapping import SampleTable from sample_registry.models import Base, Annotation, Run, Sample from sample_registry.db import run_to_dataframe, query_tag_stats, STANDARD_TAGS @@ -48,6 +51,11 @@ write_engine = create_engine(SQLALCHEMY_WRITE_URI, echo=False) WriteSession = sessionmaker(bind=write_engine) +if DATABASE_URI_MISSING: + sys.stderr.write("Creating tables...\n") + with app.app_context(): + db.create_all() + @contextmanager def api_registry(): From 1f83cb2dfc91ef7508aaabe24fc23d36a171adca Mon Sep 17 00:00:00 2001 From: Kyle Bittinger Date: Sat, 28 Mar 2026 16:24:57 -0400 Subject: [PATCH 3/5] Move stuff out of __init__, load test data if needed --- sample_registry/__init__.py | 33 --------------------------------- sample_registry/app.py | 35 ++++++++++++++++++++++++++++++----- sample_registry/db.py | 21 ++++++++++++++++++--- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/sample_registry/__init__.py b/sample_registry/__init__.py index bbe528a..58de6a2 100644 --- a/sample_registry/__init__.py +++ b/sample_registry/__init__.py @@ -8,36 +8,3 @@ __version__ = "1.4.1" -# Define archive root path -ARCHIVE_ROOT = Path( - os.environ.get("SAMPLE_REGISTRY_ARCHIVE_ROOT", "/mnt/isilon/microbiome/") -) -# Doesn't include "NA" because that's what we fill in for missing values -NULL_VALUES: list[Optional[str]] = [ - None, - "", - "null", - "NULL", - "None", - "none", - "NONE", - "N/A", - "n/a", - "na", -] - - -def sample_registry_version(): - sys.stderr.write(__version__) - -SQLALCHEMY_DATABASE_URI = os.environ.get("SAMPLE_REGISTRY_DB_URI") -DATABASE_URI_MISSING = SQLALCHEMY_DATABASE_URI is None -if DATABASE_URI_MISSING: - sys.stdout.write( - "SAMPLE_REGISTRY_DB_URI not defined in environment, " - "using in-memory SQLite database\n") - SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" - -engine = create_engine(SQLALCHEMY_DATABASE_URI) -Session = sessionmaker(bind=engine) -session = Session() diff --git a/sample_registry/app.py b/sample_registry/app.py index 8e14a83..71c0d92 100644 --- a/sample_registry/app.py +++ b/sample_registry/app.py @@ -19,12 +19,12 @@ from io import StringIO from pathlib import Path import sys -from sample_registry import ( - ARCHIVE_ROOT, SQLALCHEMY_DATABASE_URI, DATABASE_URI_MISSING, - ) from sample_registry.mapping import SampleTable from sample_registry.models import Base, Annotation, Run, Sample -from sample_registry.db import run_to_dataframe, query_tag_stats, STANDARD_TAGS +from sample_registry.db import ( + run_to_dataframe, query_tag_stats, STANDARD_TAGS, + load_test_data, + ) from sample_registry.registrar import SampleRegistry from sample_registry.standards import STANDARD_HOST_SPECIES, STANDARD_SAMPLE_TYPES from typing import Optional @@ -32,6 +32,30 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker +# Define archive root path +ARCHIVE_ROOT = Path( + os.environ.get("SAMPLE_REGISTRY_ARCHIVE_ROOT", "/mnt/isilon/microbiome/") +) + +SQLALCHEMY_DATABASE_URI = os.environ.get("SAMPLE_REGISTRY_DB_URI") +DATABASE_URI_MISSING = SQLALCHEMY_DATABASE_URI is None +if DATABASE_URI_MISSING: + if os.environ.get('PYTEST_VERSION'): + SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" + else: + dev_db_fp = ( + Path(__file__).parent.parent.resolve() / + "sample_registry.sqlite" + ) + sys.stdout.write( + f"SAMPLE_REGISTRY_DB_URI not defined in environment, " + f"using SQLite database at {dev_db_fp}\n") + SQLALCHEMY_DATABASE_URI = f"sqlite:///{dev_db_fp}" + +engine = create_engine(SQLALCHEMY_DATABASE_URI) +Session = sessionmaker(bind=engine) +session = Session() + app = Flask(__name__) app.secret_key = os.urandom(12) @@ -55,7 +79,8 @@ sys.stderr.write("Creating tables...\n") with app.app_context(): db.create_all() - + with WriteSession() as session: + load_test_data(session) @contextmanager def api_registry(): diff --git a/sample_registry/db.py b/sample_registry/db.py index 206f57d..92f0f77 100644 --- a/sample_registry/db.py +++ b/sample_registry/db.py @@ -2,7 +2,6 @@ from typing import Optional from flask_sqlalchemy import SQLAlchemy from sqlalchemy.orm import sessionmaker -from sample_registry import NULL_VALUES from sample_registry.models import ( Base, Run, @@ -16,6 +15,20 @@ "HostSpecies": "host_species", } +# Doesn't include "NA" because that's what we fill in for missing values +NULL_VALUES: list[Optional[str]] = [ + None, + "", + "null", + "NULL", + "None", + "none", + "NONE", + "N/A", + "n/a", + "na", +] + def create_test_db(session: Optional[sessionmaker] = None): if not session: @@ -24,12 +37,14 @@ def create_test_db(session: Optional[sessionmaker] = None): session = imported_session Base.metadata.create_all(engine) + load_test_data(session) +def load_test_data(session): if session.query(Run).count() > 0: sys.stderr.write( - "Database already contains data, please delete any existing test database before running this command" + "Database already contains data, skipping data load.\n" ) - sys.exit(1) + return run1 = Run( run_accession=1, From 0fa81a3b3f3b1a7cd36decaa265b9b0edeb0c37c Mon Sep 17 00:00:00 2001 From: Kyle Bittinger Date: Sat, 28 Mar 2026 17:21:12 -0400 Subject: [PATCH 4/5] Remove create_test_db --- sample_registry/db.py | 9 --------- tests/test_api.py | 4 ++-- tests/test_register.py | 4 ++-- tests/test_registrar.py | 4 ++-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/sample_registry/db.py b/sample_registry/db.py index 92f0f77..aeaa5ae 100644 --- a/sample_registry/db.py +++ b/sample_registry/db.py @@ -30,15 +30,6 @@ ] -def create_test_db(session: Optional[sessionmaker] = None): - if not session: - from sample_registry import engine - from sample_registry import session as imported_session - - session = imported_session - Base.metadata.create_all(engine) - load_test_data(session) - def load_test_data(session): if session.query(Run).count() > 0: sys.stderr.write( diff --git a/tests/test_api.py b/tests/test_api.py index 9616251..c1dc52f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -51,8 +51,8 @@ def api_client(tmp_path, monkeypatch): Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() - create_test_db = importlib.import_module("sample_registry.db").create_test_db - create_test_db(session) + load_test_data = importlib.import_module("sample_registry.db").load_test_data + load_test_data(session) session.close() sample_registry = importlib.import_module("sample_registry") diff --git a/tests/test_register.py b/tests/test_register.py index 8ca3835..93e68ae 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -6,7 +6,7 @@ from sqlalchemy import and_, create_engine, select from sqlalchemy.orm import Session, sessionmaker from typing import Generator -from sample_registry.db import create_test_db +from sample_registry.db import load_test_data from sample_registry.mapping import SampleTable from sample_registry.models import ( Annotation, @@ -69,7 +69,7 @@ def db() -> Generator[Session, None, None]: Session = sessionmaker(bind=engine) session = Session() - create_test_db(session) + load_test_data(session) yield session diff --git a/tests/test_registrar.py b/tests/test_registrar.py index a2cb9fe..51f99bf 100644 --- a/tests/test_registrar.py +++ b/tests/test_registrar.py @@ -2,7 +2,7 @@ import pytest from sqlalchemy import create_engine, func, insert, select from sqlalchemy.orm import Session, sessionmaker -from sample_registry.db import create_test_db +from sample_registry.db import load_test_data from sample_registry.mapping import SampleTable from sample_registry.models import ( Annotation, @@ -38,7 +38,7 @@ def db() -> Generator[Session, None, None]: Session = sessionmaker(bind=engine) session = Session() - create_test_db(session) + load_test_data(session) yield session From 4a34b2dc1c6bb64253dec8f54e5b4b8c3735c2f5 Mon Sep 17 00:00:00 2001 From: Kyle Bittinger Date: Mon, 30 Mar 2026 13:25:07 -0400 Subject: [PATCH 5/5] Reformat with black --- sample_registry/__init__.py | 2 -- sample_registry/app.py | 17 +++++++++-------- sample_registry/db.py | 4 +--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/sample_registry/__init__.py b/sample_registry/__init__.py index 58de6a2..c83e03f 100644 --- a/sample_registry/__init__.py +++ b/sample_registry/__init__.py @@ -6,5 +6,3 @@ from typing import Optional __version__ = "1.4.1" - - diff --git a/sample_registry/app.py b/sample_registry/app.py index 71c0d92..bc84f1e 100644 --- a/sample_registry/app.py +++ b/sample_registry/app.py @@ -22,9 +22,11 @@ from sample_registry.mapping import SampleTable from sample_registry.models import Base, Annotation, Run, Sample from sample_registry.db import ( - run_to_dataframe, query_tag_stats, STANDARD_TAGS, + run_to_dataframe, + query_tag_stats, + STANDARD_TAGS, load_test_data, - ) +) from sample_registry.registrar import SampleRegistry from sample_registry.standards import STANDARD_HOST_SPECIES, STANDARD_SAMPLE_TYPES from typing import Optional @@ -40,16 +42,14 @@ SQLALCHEMY_DATABASE_URI = os.environ.get("SAMPLE_REGISTRY_DB_URI") DATABASE_URI_MISSING = SQLALCHEMY_DATABASE_URI is None if DATABASE_URI_MISSING: - if os.environ.get('PYTEST_VERSION'): + if os.environ.get("PYTEST_VERSION"): SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" else: - dev_db_fp = ( - Path(__file__).parent.parent.resolve() / - "sample_registry.sqlite" - ) + dev_db_fp = Path(__file__).parent.parent.resolve() / "sample_registry.sqlite" sys.stdout.write( f"SAMPLE_REGISTRY_DB_URI not defined in environment, " - f"using SQLite database at {dev_db_fp}\n") + f"using SQLite database at {dev_db_fp}\n" + ) SQLALCHEMY_DATABASE_URI = f"sqlite:///{dev_db_fp}" engine = create_engine(SQLALCHEMY_DATABASE_URI) @@ -82,6 +82,7 @@ with WriteSession() as session: load_test_data(session) + @contextmanager def api_registry(): session = WriteSession() diff --git a/sample_registry/db.py b/sample_registry/db.py index aeaa5ae..998bc6b 100644 --- a/sample_registry/db.py +++ b/sample_registry/db.py @@ -32,9 +32,7 @@ def load_test_data(session): if session.query(Run).count() > 0: - sys.stderr.write( - "Database already contains data, skipping data load.\n" - ) + sys.stderr.write("Database already contains data, skipping data load.\n") return run1 = Run(