Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ python sample_registry/app.py

How you want to deploy this will depend on your needs, facilities, and ability. We have it deployed by a Kubernetes cluster but you could also 1) just run it in development mode from a lab computer or 2) setup Nginx/Apache on a dedicated server or 3) run it serverlessly in the cloud (e.g. with [Zappa](https://github.com/zappa/Zappa) on AWS) or 4) do something else. There are lots of well documented examples of deploying Flask sites out there, look around and find what works best for you.

When running, it will default to using a SQLite3 database located in the root of this repository (automatically created if it doesn't already exist). You can change to use a different backend by setting the `SAMPLE_REGISTRY_DB_URI` environment variable before running the app. For example, another sqlite database could be specified with a URI like this: `export SAMPLE_REGISTRY_DB_URI=sqlite:////path/to/db.sqlite`.
When running, it will default to using a SQLite3 database located in the root of this repository (automatically created if it doesn't already exist). You can change to use a different backend by setting the `SAMPLE_REGISTRY_DB_URI` environment variable before running the app. For example, another sqlite database could be specified with a URI like this: `export SAMPLE_REGISTRY_DB_URI=sqlite:////path/to/db.sqlite`.

If you're deploying behind a reverse proxy at a URL prefix, set `SAMPLE_REGISTRY_URL_PREFIX` (for example, `/sample_registry`). The WSGI entrypoint `sample_registry.app:application` will mount the Flask app at that prefix while leaving local development (`python sample_registry/app.py`) available at `/`.

## Using the library

Expand All @@ -36,4 +38,4 @@ The `sample_registry` library can be installed and run anywhere by following the

If you want to iterate over a feature you can only test on the K8s deployment, you can manually build the Docker image instead of relying on the release workflow. Use `docker build -t ctbushman/sample_registry:latest -f Dockerfile .` to build the image and then `docker push ctbushman/sample_registry:latest` to push it to DockerHub. You can then trigger the K8s deployment to grab the new image.

N.B. You might want to use a different tag than `latest` if you're testing something volatile so that if someone else is trying to use the image as you're developing, they won't pull your wonky changes.
N.B. You might want to use a different tag than `latest` if you're testing something volatile so that if someone else is trying to use the image as you're developing, they won't pull your wonky changes.
48 changes: 32 additions & 16 deletions sample_registry/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,45 @@
from pathlib import Path
from sample_registry import ARCHIVE_ROOT, SQLALCHEMY_DATABASE_URI
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.standards import STANDARD_HOST_SPECIES, STANDARD_SAMPLE_TYPES
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.secret_key = os.urandom(12)
from sample_registry.db import run_to_dataframe, query_tag_stats, STANDARD_TAGS
from sample_registry.standards import STANDARD_HOST_SPECIES, STANDARD_SAMPLE_TYPES
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.secret_key = os.urandom(12)

# This line is only used in production mode on a nginx server, follow instructions to setup forwarding for
# whatever production server you are using instead. It's ok to leave this in when running the dev server.
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)

# Sanitize and RO db connection
SQLALCHEMY_DATABASE_URI = f"{SQLALCHEMY_DATABASE_URI.split('?')[0]}?mode=ro"
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
print(SQLALCHEMY_DATABASE_URI)
# Sanitize and RO db connection
SQLALCHEMY_DATABASE_URI = f"{SQLALCHEMY_DATABASE_URI.split('?')[0]}?mode=ro"
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
print(SQLALCHEMY_DATABASE_URI)
# Ensure SQLite explicitly opens in read-only mode
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"connect_args": {"uri": True}}
db = SQLAlchemy(model_class=Base)
db.init_app(app)


@app.route("/favicon.ico")
def favicon():
db = SQLAlchemy(model_class=Base)
db.init_app(app)

def _normalize_url_prefix(raw_prefix: str) -> str:
prefix = (raw_prefix or "").strip()
if not prefix or prefix == "/":
return ""
if not prefix.startswith("/"):
prefix = f"/{prefix}"
return prefix.rstrip("/")
Comment on lines +43 to +49
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_normalize_url_prefix is annotated to accept str, but it is immediately used with os.getenv("SAMPLE_REGISTRY_URL_PREFIX"), which can return None; this mismatched type annotation can cause static type checkers (e.g., mypy) to flag an error and makes the intended contract less clear. Consider updating the parameter type to Optional[str] (and importing it if needed) so the signature accurately reflects the possible inputs.

Copilot uses AI. Check for mistakes.


URL_PREFIX = _normalize_url_prefix(os.getenv("SAMPLE_REGISTRY_URL_PREFIX"))
if URL_PREFIX:
application = DispatcherMiddleware(Flask("dummy_root"), {URL_PREFIX: app})
else:
application = app


@app.route("/favicon.ico")
def favicon():
return send_from_directory(
Path(app.root_path) / "static",
"favicon.ico",
Expand Down
Loading