Skip to content

Feature/SUPeR expansion (expand other 13 DBs from NIE pseudobulk to other DBs for MVP) - #331

Merged
asherpasha merged 4 commits into
BioAnalyticResource:devfrom
VinLau:feature/superviewer-expansion
Sep 22, 2026
Merged

asherpasha merged 4 commits into
BioAnalyticResource:devfrom
VinLau:feature/superviewer-expansion

Conversation

@VinLau

@VinLau VinLau commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Registers 13 more SUPeR Viewer dataset pairs alongside arabidopsis_NIE_*, which shipped in #330. Pseudobulk databases are catalog entries only; UMAP databases move from a hand-written model file to a registry dict (umap_dynamic.py) that essentially is the same Model simply 'renamed'. First @asherpasha you'll need to upload the full DB set and then we can merge this PR in (I'll do some testing as well).

1. Summary

Four commits, each reviewable on its own.

Commit What it does
d6a605c Replaces api/models/arabidopsis_NIE_umap.py and the if/elif in UMAPUtils.get_tables with api/models/umap_dynamic.py: a UMAP_DATABASES dict mapping database → species, and a factory that builds the coords and expression models per database. Modelled on efp_dynamic.py. Only arabidopsis_NIE_umap is registered here.
6f871f1 Adds 26 fixture dumps and their CI wiring — one SQLALCHEMY_BINDS entry in config/BAR_API.cfg and one seed line in config/init.sh per database.
496ebb8 Adds 13 pseudobulk catalog entries to combined_master.json, each copying arabidopsis_NIE_pseudobulk field for field except species/gene_id_pattern (different only for rice_OW_pseudobulk). pseudobulk_std.assigned_databases 1 → 14. Updates the expected set in test_gene_expression.py to the explicit 14 names.
bafa6fe Registers the 13 new UMAP databases and adds tests/resources/test_superviewer_databases.py.

d6a605c is behaviour-preserving. tests/resources/test_umap_gene_expression.py is byte-identical to dev and still passes, so the refactor changed no route, response or error.

UMAP databases get no catalog entry: they have no sample_data table, so an entry would build a model for a table that doesn't exist. The registry dict is a code-level list instead.

Test literals in test_superviewer_databases.py were taken from the committed fixtures and are checked against fixture_manifest.json; the tests never read the manifest at runtime. Suite from an empty MySQL seeded only through init.sh: 122 passed, 91 subtests passed, 3 failed. The 3 failures are dev's existing baseline (mfinder exit 127, two Redis refusals on localhost) and this PR touches none of them. flake8 clean.

Both endpoints were also exercised directly against a local instance (47 requests): row counts and Mean_CTRL values match the fixtures for all 14 pseudobulk databases, and across all 13 UMAP databases every expression key is a coordinates key with no gap cell ever returned. That is fixture data, so it does not substitute for the staging check in the merge gate.

2. Merge checks

Do not merge on staging until both pass. Both need the 26 databases on prod first; I can run them.

  1. Schema check. Run Vin's local verify_master_against_prod.py on staging server MySQL against live information_schema. The 13 new entries copy schema_verified: true and schema_source: "prod_information_schema" from the NIE entry, but so far those claims rest on the dump files, not on prod. The script covers only the 13 pseudobulk entries — the UMAP databases aren't catalogued, so nothing verifies them this way.
  2. Staging test. For each new pseudobulk database, the fixture genes' Mean_CTRL value and value_std must equal the literals in tests/resources/test_superviewer_databases.py. For each new UMAP database, the coordinates response and each fixture gene's response must return 200.

3. Databases (reference)

26 new databases. Names are case-sensitive; note the capitalised OW.

arabidopsis_flower_lee_pseudobulk       arabidopsis_flower_lee_umap
arabidopsis_root_shahan_pseudobulk      arabidopsis_root_shahan_umap
arabidopsis_rosette_21d_lee_pseudobulk  arabidopsis_rosette_21d_lee_umap
arabidopsis_rosette_30d_lee_pseudobulk  arabidopsis_rosette_30d_lee_umap
arabidopsis_seed_0d_lee_pseudobulk      arabidopsis_seed_0d_lee_umap
arabidopsis_seed_martin_pseudobulk      arabidopsis_seed_martin_umap
arabidopsis_seedling_12d_lee_pseudobulk arabidopsis_seedling_12d_lee_umap
arabidopsis_seedling_3d_lee_pseudobulk  arabidopsis_seedling_3d_lee_umap
arabidopsis_seedling_6d_lee_pseudobulk  arabidopsis_seedling_6d_lee_umap
arabidopsis_shoot_zhang_pseudobulk      arabidopsis_shoot_zhang_umap
arabidopsis_silique_lee_pseudobulk      arabidopsis_silique_lee_umap
arabidopsis_stem_lee_pseudobulk         arabidopsis_stem_lee_umap
rice_OW_pseudobulk                      rice_OW_umap

4. Known data gaps/inconsistences, not addressed here

Counts below come from a full-dump scan (21 Sept 2026), not from anything in this PR. The committed fixtures use pattern-valid gene keys only, so none of this affects the tests.

  • shoot_zhang — 9,570 symbol-keyed genes, some containing spaces or slashes. Not reachable through the API's gene ID pattern. Separately, the UMAP route declares <string:gene_id>, which does not match slashes, so a slash-bearing key is rejected by Flask routing (404) before any handler runs. The pseudobulk route uses <path:gene_id> and has no such limit.
  • root_shahan — 21,437 lncRNA keys (AthLNC + six digits), plus 7 ATMG…-N duplicates. Real loci with no AGI, so not reachable through the Arabidopsis pattern.
  • NIE — 6,423 symbol-keyed genes; 11 AGI keys carrying 138 rows (two genes each); one nan row in the UMAP table.
  • seed_martin — some data_bot_id values end in an apostrophe, e.g. 3DAP ii1' (integument-prime names). The committed fixture has 3 such values across 9 rows. They're backslash-escaped in the dumps, so anything parsing a dump or rendering sample names must handle them — a naive single-quote parser silently drops those rows.

Reaching the symbol- and lncRNA-keyed genes needs a decision about gene ID patterns that would affect 30+ databases, so it's deliberately left out of this PR. Note that widening a pattern alone would not be sufficient for slash-bearing keys on the UMAP endpoint — its route converter would have to change too.

5. Contributor note

docker-compose.yml bind-mounts only api/, tests/ and config/databases/. combined_master.json, config/BAR_API.cfg and config/init.sh are baked into the image, and app.py runs without a reloader. After changing any of those three, rebuild:

docker compose up -d --build

Otherwise local tests run against stale copies — the usual symptom is a newly catalogued database returning "Invalid species or gene ID".

…abase

api/models/umap_dynamic.py follows efp_dynamic.py: UMAP_DATABASES maps each
database to its species, and a factory builds the coords and expression models
per database with unique class names. Only arabidopsis_NIE_umap is registered
here, so this is a pure refactor.

UMAPUtils.get_tables is now a lookup against that registry and returns the same
dict, so the routes and responses are untouched. The escaped database name is
wrapped in str() before the lookup. Proof of no behaviour change:
tests/resources/test_umap_gene_expression.py passes without a single edit.
26 fixture dumps generated from the full dumps from prod that I checked myself: 3 genes per pseudobulk
database carrying all of that database's rows per gene including Mean_CTRL,
and 8 cells per UMAP database with each gene's expression trimmed to those
cells. Every UMAP gene keeps at least one gap cell, so a server that filled in
zeros would fail the tests.

Each file keeps its source dump's header, DDL and footer verbatim; only the
data rows were cut down. Filenames follow the NIE convention: <db>_dump.sql for
pseudobulk and <db>.sql for UMAP.

Verified before wiring: every CREATE DATABASE name matches its filename, and
every rows-per-gene matches the manifest and the dataset table.

fixture_manifest.json stays uncommitted.
Each entry copies arabidopsis_NIE_pseudobulk field for field; only species and
gene_id_pattern differ, and only for rice_OW_pseudobulk. The expression
endpoint is catalog-driven, so no endpoint code changes.

pseudobulk_std.assigned_databases goes 1 -> 14. databases stays ASCII-sorted at
207 entries. UMAP databases get no catalog entry: they have no sample_data
table, so an entry would build a model for a table that does not exist.

schema_verified / schema_source are copied from NIE but so far rest on the dump
files, not prod. Confirm with verify_master_against_prod.py after IT loads the
databases, before merging.

test_gene_expression.py's expected set is now the explicit list of all 14
pseudobulk databases, written out rather than derived from the catalog so the
test still fails if the catalog gains an unintended pseudobulk_std entry.
UMAP_DATABASES gains the 13 new databases with their species. Routes, response
shapes and the existing NIE tests are untouched.

tests/resources/test_superviewer_databases.py drives both endpoints from
literal tables, with a subTest per database and gene. The literals were taken
from the committed fixtures; the tests never read the manifest.

Pseudobulk: every fixture gene returns its database's full row count with the
name/value/value_std keys and exactly one Mean_CTRL carrying its stored values.
Several databases share a row count, so the Mean_CTRL values are what catch a
bind pointing at the wrong database.

UMAP: coordinates return exactly the fixture cells, and each gene returns
exactly its non-zero cells, so a server filling in gap cells would fail.
@asherpasha
asherpasha merged commit 665c344 into BioAnalyticResource:dev Sep 22, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants