diff options
-rw-r--r-- | gn3/api/gemma.py | 19 | ||||
-rw-r--r-- | gn3/computations/gemma.py | 14 | ||||
-rw-r--r-- | tests/integration/test_gemma.py | 69 | ||||
-rw-r--r-- | tests/unit/computations/test_gemma.py | 1 |
4 files changed, 83 insertions, 20 deletions
diff --git a/gn3/api/gemma.py b/gn3/api/gemma.py index effae8a..7c76f2f 100644 --- a/gn3/api/gemma.py +++ b/gn3/api/gemma.py @@ -35,10 +35,12 @@ file output is returned. """ data = request.get_json() app_defaults = current_app.config.get('APP_DEFAULTS') - __hash = generate_hash_of_string("".join(data.get("values"))) + __hash = generate_hash_of_string( + f"{data.get('genofile_name')}_" + ''.join(data.get("values", ""))) gemma_kwargs = { "geno_filename": os.path.join(app_defaults.get("GENODIR"), "bimbam", - f"{data.get('genofile_name')}.txt"), + f"{data.get('geno_filename')}"), "trait_filename": generate_pheno_txt_file( tmpdir=app_defaults.get("TMPDIR"), values=data.get("values"), @@ -46,18 +48,21 @@ file output is returned. trait_filename=(f"{data.get('dataset_groupname')}_" f"{data.get('trait_name')}_" f"{__hash}.txt"))} + gemma_wrapper_kwargs = {} + if data.get("loco"): + gemma_wrapper_kwargs["loco"] = f"--input {data.get('loco')}" k_computation_cmd = generate_gemma_computation_cmd( - gemma_cmd=app_defaults.get("GEMMA_WRAPPER_CMD"), + gemma_cmd=app_defaults.get("GEMMA_WRAPPER_CMD") + "_haha", + gemma_wrapper_kwargs={"loco": f"--input {data.get('loco')}"}, gemma_kwargs=gemma_kwargs, output_file=(f"{app_defaults.get('TMPDIR')}/gn2/" f"{data.get('dataset_name')}_K_" f"{__hash}.json")) - if data.get("covariates"): - gemma_kwargs["c"] = os.path.join(app_defaults.get("GENODIR"), - "bimbam", - data.get("covariates")) gemma_kwargs["lmm"] = data.get("lmm", 9) + gemma_wrapper_kwargs["input"] = (f"{data.get('dataset_name')}_K_" + f"{__hash}.json") gwa_cmd = generate_gemma_computation_cmd( + gemma_wrapper_kwargs=gemma_wrapper_kwargs, gemma_cmd=app_defaults.get("GEMMA_WRAPPER_CMD"), gemma_kwargs=gemma_kwargs, output_file=(f"{data.get('dataset_name')}_GWA_" diff --git a/gn3/computations/gemma.py b/gn3/computations/gemma.py index 5741692..528f888 100644 --- a/gn3/computations/gemma.py +++ b/gn3/computations/gemma.py @@ -55,10 +55,12 @@ def do_paths_exist(paths: ValuesView) -> bool: return True -def generate_gemma_computation_cmd(gemma_cmd: str, - gemma_kwargs: Dict[str, str], - output_file: str) -> Optional[str]: - """Create a higher order function that generates a command""" +def generate_gemma_computation_cmd( + gemma_cmd: str, + gemma_kwargs: Dict[str, str], + output_file: str, + gemma_wrapper_kwargs: Dict[str, str]) -> Optional[str]: + """Create a computation cmd""" geno_filename = gemma_kwargs.get("geno_filename", "") trait_filename = gemma_kwargs.get("trait_filename") ext, snps_filename = geno_filename.partition(".")[-1], "" @@ -66,13 +68,13 @@ def generate_gemma_computation_cmd(gemma_cmd: str, snps_filename = geno_filename.replace(f".{ext}", "") snps_filename += f"_snps.{ext}" _kwargs = {"g": geno_filename, "p": trait_filename} - if gemma_kwargs.get("covar_filename"): - _kwargs["a"] = gemma_kwargs.get("covar_filename") + _kwargs["a"] = snps_filename if not do_paths_exist(_kwargs.values()): # Prevents injection! return None if _kwargs.get("lmm"): _kwargs["lmm"] = gemma_kwargs.get("lmm") return compose_gemma_cmd(gemma_wrapper_cmd=gemma_cmd, + gemma_wrapper_kwargs=gemma_wrapper_kwargs, gemma_kwargs=_kwargs, gemma_args=["-gk", ">", output_file]) diff --git a/tests/integration/test_gemma.py b/tests/integration/test_gemma.py index 598c91f..1f59530 100644 --- a/tests/integration/test_gemma.py +++ b/tests/integration/test_gemma.py @@ -1,4 +1,5 @@ """Integration tests for gemma API endpoints""" +import os import unittest from dataclasses import dataclass @@ -17,7 +18,14 @@ class MockRedis: class GemmaAPITest(unittest.TestCase): """Test cases for the Gemma API""" def setUp(self): - self.app = create_app().test_client() + self.app = create_app({ + "APP_DEFAULTS": { + "GENODIR": os.path.abspath( + os.path.join(os.path.dirname(__file__), + "test_data/")), + "TMPDIR": "/tmp", + "REDIS_JOB_QUEUE": "GN3::job-queue", + "GEMMA_WRAPPER_CMD": "gemma-wrapper"}}).test_client() @mock.patch("gn3.api.gemma.run_cmd") def test_get_version(self, mock_run_cmd): @@ -31,9 +39,9 @@ class GemmaAPITest(unittest.TestCase): @mock.patch("gn3.api.gemma.redis.Redis") @mock.patch("gn3.api.gemma.queue_cmd") @mock.patch("gn3.api.gemma.generate_gemma_computation_cmd") - def test_run_gemma(self, mock_gemma_computation_cmd, - mock_queue_cmd, mock_redis): - """Test that gemma composes the command correctly""" + def test_run_gemma_no_loco(self, mock_gemma_computation_cmd, + mock_queue_cmd, mock_redis): + """Test that gemma composes the command correctly without loco""" _redis_conn = MockRedis(redis=mock.MagicMock(), hget=mock.MagicMock()) mock_redis.return_value = _redis_conn mock_gemma_computation_cmd.side_effect = [ @@ -42,7 +50,9 @@ class GemmaAPITest(unittest.TestCase): "test.txt -a genofile_snps.txt " "-gk > /tmp/gn2/" "bxd_K_gUFhGu4rLG7k+CXLPk1OUg.txt"), - ("gemma-wrapper --json -- " + ("gemma-wrapper --json --input /tmp/gn2/" + "bxd_K_gUFhGu4rLG7k+CXLPk1OUg.txt -- " + "-a genofile_snps.txt -lmm 9 " "-g genofile.txt -p " "test.txt -a genofile_snps.txt " "-gk > /tmp/gn2/" @@ -66,7 +76,9 @@ class GemmaAPITest(unittest.TestCase): "genofile.txt -p test.txt " "-a genofile_snps.txt -gk > " "/tmp/gn2/bxd_K_gUFhGu4rLG7k+CXLPk1OUg.txt " - "&& gemma-wrapper --json -- -g " + "&& gemma-wrapper --json --input " + "/tmp/gn2/bxd_K_gUFhGu4rLG7k+CXLPk1OUg.txt" + " -- -a genofile_snps.txt -lmm 9 -g " "genofile.txt -p test.txt " "-a genofile_snps.txt " "-gk > " @@ -78,7 +90,50 @@ class GemmaAPITest(unittest.TestCase): response.get_json(), {"unique_id": 'my-unique-id', "status": "queued", - "output_file": "BXD_GWA_9lo8zwOOXbfB73EcyXxAYQ.txt"}) + "output_file": "BXD_GWA_WzxVcfhKAn4fJnSWpsBq0g.txt"}) + + @mock.patch("gn3.api.gemma.redis.Redis") + @mock.patch("gn3.api.gemma.queue_cmd") + def test_run_gemma_with_loco(self, + mock_queue_cmd, mock_redis): + """Test that gemma composes the command correctly with loco""" + _redis_conn = MockRedis(redis=mock.MagicMock(), hget=mock.MagicMock()) + mock_redis.return_value = _redis_conn + mock_queue_cmd.return_value = "my-unique-id" + response = self.app.post("/gemma/k-gwa-computation", json={ + "trait_filename": os.path.abspath(os.path.join( + os.path.dirname(__file__), + "test_data/" + )), + "geno_filename": "BXD_geno.txt.gz", + "values": ["X", "N/A", "X"], + "dataset_groupname": "BXD", + "trait_name": "Height", + "email": "me@me.com", + "dataset_name": "BXD", + "loco": "1,2,3,4,5,6" + }) + mock_queue_cmd.called_with( + conn=_redis_conn, + email="me@me.com", + job_queue="GN3::job-queue", + cmd=("gemma-wrapper --json -- -g " + "genofile.txt -p test.txt " + "-a genofile_snps.txt -gk > " + "/tmp/gn2/bxd_K_gUFhGu4rLG7k+CXLPk1OUg.txt " + "&& gemma-wrapper --json --input " + "/tmp/gn2/bxd_K_gUFhGu4rLG7k+CXLPk1OUg.txt" + " -- -a genofile_snps.txt -lmm 9 -g " + "genofile.txt -p test.txt " + "-a genofile_snps.txt " + "-gk > " + "/tmp/gn2/" + "bxd_GWA_gUFhGu4rLG7k+CXLPk1OUg.txt")) + self.assertEqual( + response.get_json(), + {"unique_id": 'my-unique-id', + "status": "queued", + "output_file": "BXD_GWA_WzxVcfhKAn4fJnSWpsBq0g.txt"}) @mock.patch("gn3.api.gemma.redis.Redis") def test_check_cmd_status(self, mock_redis): diff --git a/tests/unit/computations/test_gemma.py b/tests/unit/computations/test_gemma.py index a7884ea..b5ffbfd 100644 --- a/tests/unit/computations/test_gemma.py +++ b/tests/unit/computations/test_gemma.py @@ -38,6 +38,7 @@ class TestGemma(unittest.TestCase): self.assertEqual( generate_gemma_computation_cmd( gemma_cmd="gemma-wrapper", + gemma_wrapper_kwargs=None, gemma_kwargs={ "geno_filename": "genofile.txt", "trait_filename": "test.txt", |