From e2529e26cf3400ce9ebe3e4b1f29edf8b577ba0e Mon Sep 17 00:00:00 2001 From: asherpasha Date: Sun, 12 Dec 2021 11:27:07 -0500 Subject: [PATCH 1/5] Adding eFP Cannabis for faster tests. --- api/resources/efp_image.py | 48 ++++----------------- api/utils/bar_utils.py | 22 +++++----- api/utils/efp_utils.py | 69 +++++++++++++++++++++++++++++++ requirements.txt | 2 +- tests/resources/test_efp_image.py | 16 +++---- tests/utils/test_efp_utils.py | 49 ++++++++++++++++++++++ 6 files changed, 146 insertions(+), 60 deletions(-) create mode 100644 api/utils/efp_utils.py create mode 100644 tests/utils/test_efp_utils.py diff --git a/api/resources/efp_image.py b/api/resources/efp_image.py index 66b66abf..d918bb48 100644 --- a/api/resources/efp_image.py +++ b/api/resources/efp_image.py @@ -2,11 +2,12 @@ import re import requests import random -import redis.connection +import redis.exceptions from flask_restx import Namespace, Resource from markupsafe import escape from flask import send_from_directory from api.utils.bar_utils import BARUtils +from api.utils.efp_utils import eFPUtils efp_image = Namespace( "eFP Image", description="eFP Image generation service", path="/efp_image" @@ -18,8 +19,8 @@ class eFPImageList(Resource): def get(self): """This end point returns the list of species available""" # This are the only species available so far - # If this is updated, update get request and test as well - species = ["efp_arabidopsis"] + # If this is updated, update efp_utils.py and unit tests as well + species = ["efp_arabidopsis", "efp_cannabis"] return BARUtils.success_exit(species) @@ -29,19 +30,6 @@ def get(self): doc=False, ) class eFPImage(Resource): - @staticmethod - def is_efp_mode(efp_mode): - """This function checks if the eFP mode is valid - :param efp_mode: string eFP Mode - :return: True or False - """ - # This are case sensitive - valid_modes = ["Absolute", "Relative", "Compare"] - if efp_mode in valid_modes: - return True - else: - return False - @efp_image.param("efp", _in="path", default="efp_arabidopsis") @efp_image.param("view", _in="path", default="Developmental_Map") @efp_image.param("mode", _in="path", default="Absolute") @@ -49,10 +37,6 @@ def is_efp_mode(efp_mode): @efp_image.param("gene_2", _in="path", default="At3g27340") def get(self, efp="", view="", mode="", gene_1="", gene_2=""): """This end point returns eFP images.""" - # list of allowed eFPs - # See endpoint able - species = ["efp_arabidopsis"] - # Escape input data efp = escape(efp) view = escape(view) @@ -60,32 +44,16 @@ def get(self, efp="", view="", mode="", gene_1="", gene_2=""): gene_1 = escape(gene_1) gene_2 = escape(gene_2) - # Validate values - if efp not in species: - return BARUtils.error_exit("Invalid eFP."), 400 - - # Validate view - if not BARUtils.is_efp_view_name(view): - return BARUtils.error_exit("Invalid eFP View name."), 400 - - # Validate mode - if not self.is_efp_mode(mode): - return BARUtils.error_exit("Invalid eFP mode."), 400 - - # Validate gene ids - if not BARUtils.is_arabidopsis_gene_valid(gene_1): - return BARUtils.error_exit("Gene 1 is invalid."), 400 - - if mode == "Compare": - if not BARUtils.is_arabidopsis_gene_valid(gene_2): - return BARUtils.error_exit("Gene 2 is invalid."), 400 + validation = eFPUtils.is_efp_input_valid(efp, view, mode, gene_1, gene_2) + if validation[0] is False: + return BARUtils.error_exit(validation[1]), 400 # Check if request is cached try: r = BARUtils.connect_redis() key = "BAR_API_efp_image_" + "_".join([efp, view, mode, gene_1, gene_2]) efp_image_base64 = r.get(key) - except redis.connection.ConnectionError: + except redis.exceptions.ConnectionError: # Failed redis connection r = None key = None diff --git a/api/utils/bar_utils.py b/api/utils/bar_utils.py index c1fe521b..9144f31e 100644 --- a/api/utils/bar_utils.py +++ b/api/utils/bar_utils.py @@ -72,6 +72,17 @@ def is_tomato_gene_valid(gene, isoform_id=False): else: return False + @staticmethod + def is_cannabis_gene_valid(gene): + """This function verifies if cannabis gene is valid: AGQN03000001 + :param gene: + :return: True if valid + """ + if gene and re.search(r"^AGQN\d{0,10}$", gene, re.I): + return True + else: + return False + @staticmethod def is_integer(data): """Check if the input is at max ten figure number. @@ -91,17 +102,6 @@ def format_poplar(poplar_gene): """ return poplar_gene.translate(str.maketrans("pOTRIg", "PotriG")) - @staticmethod - def is_efp_view_name(efp_view): - """This function is used for validated eFP View names for eFP service - :param efp_view: string view name - :return: True if valid - """ - if efp_view and re.search(r"^[a-z1-9_]{1,20}$", efp_view, re.I): - return True - else: - return False - @staticmethod def connect_redis(): """This function connects to redis diff --git a/api/utils/efp_utils.py b/api/utils/efp_utils.py new file mode 100644 index 00000000..2ed1336a --- /dev/null +++ b/api/utils/efp_utils.py @@ -0,0 +1,69 @@ +import re +from api.utils.bar_utils import BARUtils + + +class eFPUtils: + @staticmethod + def is_efp_view_name(efp_view): + """This function is used for validated eFP View names for eFP service + :param efp_view: string view name + :return: True if valid + """ + if efp_view and re.search(r"^[a-z1-9_]{1,20}$", efp_view, re.I): + return True + else: + return False + + @staticmethod + def is_efp_mode(efp_mode): + """This function checks if the eFP mode is valid + :param efp_mode: string eFP Mode + :return: True or False + """ + # These are case-sensitive + valid_modes = ["Absolute", "Relative", "Compare"] + if efp_mode in valid_modes: + return True + else: + return False + + @staticmethod + def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): + """Test if eFP input is valid + :return: List with boolean and string + """ + species = ["efp_arabidopsis", "efp_cannabis"] + + # Validate values + if efp not in species: + return False, "Invalid eFP." + + # Validate view + if not eFPUtils.is_efp_view_name(view): + return False, "Invalid eFP View name." + + # Validate mode + if not eFPUtils.is_efp_mode(mode): + return False, "Invalid eFP mode." + + # Maybe this part could be imported + if efp == "efp_arabidopsis": + # Validate gene ids + if not BARUtils.is_arabidopsis_gene_valid(gene_1): + return False, "Gene 1 is invalid." + + if mode == "Compare": + if not BARUtils.is_arabidopsis_gene_valid(gene_2): + return False, "Gene 2 is invalid." + + if efp == "efp_cannabis": + # Validate gene ids + if not BARUtils.is_cannabis_gene_valid(gene_1): + return False, "Gene 1 is invalid." + + if mode == "Compare": + if not BARUtils.is_cannabis_gene_valid(gene_2): + return False, "Gene 2 is invalid." + + # Assuming all check have passed + return True, None diff --git a/requirements.txt b/requirements.txt index bf3c5a25..137859bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,7 +34,7 @@ mypy-extensions==0.4.3 mysqlclient==2.1.0 numpy==1.21.4 packaging==21.3 -pandas==1.3.4 +pandas==1.3.5 pathspec==0.9.0 pluggy==1.0.0 py==1.11.0 diff --git a/tests/resources/test_efp_image.py b/tests/resources/test_efp_image.py index 97bc30a2..e2520dfd 100644 --- a/tests/resources/test_efp_image.py +++ b/tests/resources/test_efp_image.py @@ -11,7 +11,7 @@ def test_get_efp_image_list(self): :return: """ response = self.app_client.get("/efp_image/") - expected = {"wasSuccessful": True, "data": ["efp_arabidopsis"]} + expected = {"wasSuccessful": True, "data": ["efp_arabidopsis", "efp_cannabis"]} self.assertEqual(response.json, expected) def test_get_efp_image(self): @@ -20,16 +20,16 @@ def test_get_efp_image(self): """ # Test absolute modes in the beginning # A very basic test for Arabidopsis requests - # https://bar.utoronto.ca/api/efp_image/efp_arabidopsis/Developmental_Map/Absolute/At1g01010 + # https://bar.utoronto.ca/api/efp_image/efp_cannabis/Cannabis_Atlas/Absolute/AGQN03000001 response = self.app_client.get( - "/efp_image/efp_arabidopsis/Developmental_Map/Absolute/At1g01010" + "/efp_image/efp_cannabis/Cannabis_Atlas/Absolute/AGQN03000001" ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content_type, "image/png") - # Now rerun for Cached requests. A image should be return from the cache + # Now rerun for Cached requests. An image should be return from the cache response = self.app_client.get( - "/efp_image/efp_arabidopsis/Developmental_Map/Absolute/At1g01010" + "/efp_image/efp_cannabis/Cannabis_Atlas/Absolute/AGQN03000001" ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content_type, "image/png") @@ -78,16 +78,16 @@ def test_get_efp_image(self): # Test compare modes in the end # A very basic test for Arabidopsis requests - # https://bar.utoronto.ca/api/efp_image/efp_arabidopsis/Developmental_Map/Compare/At1g01010/At1g01030 + # https://bar.utoronto.ca/api/efp_image/efp_cannabis/Cannabis_Atlas/Compare/AGQN03000001/AGQN03000012 response = self.app_client.get( - "/efp_image/efp_arabidopsis/Developmental_Map/Compare/At1g01010/At1g01030" + "/efp_image/efp_cannabis/Cannabis_Atlas/Compare/AGQN03000001/AGQN03000012" ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content_type, "image/png") # Rerun for cached request. An image should be returned response = self.app_client.get( - "/efp_image/efp_arabidopsis/Developmental_Map/Compare/At1g01010/At1g01030" + "/efp_image/efp_cannabis/Cannabis_Atlas/Compare/AGQN03000001/AGQN03000012" ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content_type, "image/png") diff --git a/tests/utils/test_efp_utils.py b/tests/utils/test_efp_utils.py new file mode 100644 index 00000000..b777a4eb --- /dev/null +++ b/tests/utils/test_efp_utils.py @@ -0,0 +1,49 @@ +from unittest import TestCase +from api.utils.efp_utils import eFPUtils + + +class UtilsUnitTest(TestCase): + def test_is_efp_input_valid(self): + """Tests for eFP input data""" + + # eFP Arabidopsis compare mode + result = eFPUtils.is_efp_input_valid( + "efp_arabidopsis", "Root", "Compare", "At1g01010", "At1g01030" + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_arabidopsis", "Root", "Compare", "At1g01010", "Abc" + ) + expected = "Gene 2 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # eFP Cannabis gene1 + result = eFPUtils.is_efp_input_valid( + "efp_cannabis", "Cannabis_Atlas", "Absolute", "AGQN03000001" + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_cannabis", "Cannabis_Atlas", "Absolute", "Abc" + ) + expected = "Gene 1 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # eFP Cannabis gene2 + result = eFPUtils.is_efp_input_valid( + "efp_cannabis", "Cannabis_Atlas", "Compare", "AGQN03000001", "AGQN03000012" + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_cannabis", "Cannabis_Atlas", "Compare", "AGQN03000001", "Abc" + ) + expected = "Gene 2 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) From 1cb58727a4df1cebef38155babf00fb5503d20b8 Mon Sep 17 00:00:00 2001 From: asherpasha Date: Sun, 12 Dec 2021 11:50:25 -0500 Subject: [PATCH 2/5] Removing eFP compare test to speed up testing. --- tests/resources/test_efp_image.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/resources/test_efp_image.py b/tests/resources/test_efp_image.py index e2520dfd..23ae3809 100644 --- a/tests/resources/test_efp_image.py +++ b/tests/resources/test_efp_image.py @@ -75,19 +75,3 @@ def test_get_efp_image(self): ) expected = {"wasSuccessful": False, "error": "Gene 2 is invalid."} self.assertEqual(response.json, expected) - - # Test compare modes in the end - # A very basic test for Arabidopsis requests - # https://bar.utoronto.ca/api/efp_image/efp_cannabis/Cannabis_Atlas/Compare/AGQN03000001/AGQN03000012 - response = self.app_client.get( - "/efp_image/efp_cannabis/Cannabis_Atlas/Compare/AGQN03000001/AGQN03000012" - ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content_type, "image/png") - - # Rerun for cached request. An image should be returned - response = self.app_client.get( - "/efp_image/efp_cannabis/Cannabis_Atlas/Compare/AGQN03000001/AGQN03000012" - ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content_type, "image/png") From 4cccd50712e48b83b950034609f9b140cc31cde5 Mon Sep 17 00:00:00 2001 From: asherpasha Date: Wed, 15 Dec 2021 11:26:09 -0500 Subject: [PATCH 3/5] eFP Arachis image service is added. --- api/resources/efp_image.py | 2 +- api/utils/bar_utils.py | 13 +++++++++ api/utils/efp_utils.py | 15 ++++++++++- requirements.txt | 2 +- tests/resources/test_efp_image.py | 7 +++-- tests/utils/test_efp_utils.py | 44 +++++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) diff --git a/api/resources/efp_image.py b/api/resources/efp_image.py index d918bb48..d395f9f1 100644 --- a/api/resources/efp_image.py +++ b/api/resources/efp_image.py @@ -20,7 +20,7 @@ def get(self): """This end point returns the list of species available""" # This are the only species available so far # If this is updated, update efp_utils.py and unit tests as well - species = ["efp_arabidopsis", "efp_cannabis"] + species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis"] return BARUtils.success_exit(species) diff --git a/api/utils/bar_utils.py b/api/utils/bar_utils.py index 9144f31e..b0396ec8 100644 --- a/api/utils/bar_utils.py +++ b/api/utils/bar_utils.py @@ -83,6 +83,19 @@ def is_cannabis_gene_valid(gene): else: return False + @staticmethod + def is_arachis_gene_valid(gene): + """This function verifies if arachis gene is valid: Adur10000_comp0_c0_seq1 + :param gene: + :return: True if valid + """ + if gene and re.search( + r"Adur\d{1,10}_comp\d{1,3}_\D{1,3}\d{1,3}_seq\d{1,5}", gene, re.I + ): + return True + else: + return False + @staticmethod def is_integer(data): """Check if the input is at max ten figure number. diff --git a/api/utils/efp_utils.py b/api/utils/efp_utils.py index 2ed1336a..6dbdb1c9 100644 --- a/api/utils/efp_utils.py +++ b/api/utils/efp_utils.py @@ -32,7 +32,7 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): """Test if eFP input is valid :return: List with boolean and string """ - species = ["efp_arabidopsis", "efp_cannabis"] + species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis"] # Validate values if efp not in species: @@ -65,5 +65,18 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): if not BARUtils.is_cannabis_gene_valid(gene_2): return False, "Gene 2 is invalid." + if efp == "efp_arachis": + # Validate gene ids + if not BARUtils.is_arachis_gene_valid(gene_1): + return False, "Gene 1 is invalid." + + if mode == "Compare": + if not BARUtils.is_arachis_gene_valid(gene_2): + return False, "Gene 2 is invalid." + + # In compare mode gene1 != gene2 + if mode == "Compare" and gene_1 == gene_2: + return False, "In compare mode, both genes should be different." + # Assuming all check have passed return True, None diff --git a/requirements.txt b/requirements.txt index 137859bd..5f96e9cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ chardet==4.0.0 charset-normalizer==2.0.9 click==8.0.3 coverage==6.2 -cryptography==36.0.0 +cryptography==36.0.1 Deprecated==1.2.13 docopt==0.6.2 flake8==4.0.1 diff --git a/tests/resources/test_efp_image.py b/tests/resources/test_efp_image.py index 23ae3809..fc36ad45 100644 --- a/tests/resources/test_efp_image.py +++ b/tests/resources/test_efp_image.py @@ -11,7 +11,10 @@ def test_get_efp_image_list(self): :return: """ response = self.app_client.get("/efp_image/") - expected = {"wasSuccessful": True, "data": ["efp_arabidopsis", "efp_cannabis"]} + expected = { + "wasSuccessful": True, + "data": ["efp_arabidopsis", "efp_cannabis", "efp_arachis"], + } self.assertEqual(response.json, expected) def test_get_efp_image(self): @@ -69,7 +72,7 @@ def test_get_efp_image(self): } self.assertEqual(response.json, expected) - # Test for gene 2 using Arabidopsis + # Test for gene 1 using Arabidopsis response = self.app_client.get( "/efp_image/efp_arabidopsis/Root/Compare/At1g01010/Abc" ) diff --git a/tests/utils/test_efp_utils.py b/tests/utils/test_efp_utils.py index b777a4eb..1ddd5b7d 100644 --- a/tests/utils/test_efp_utils.py +++ b/tests/utils/test_efp_utils.py @@ -47,3 +47,47 @@ def test_is_efp_input_valid(self): expected = "Gene 2 is invalid." self.assertFalse(result[0]) self.assertEqual(result[1], expected) + + # eFP Arachis Absolute + result = eFPUtils.is_efp_input_valid( + "efp_arachis", "Arachis_Atlas", "Absolute", "Adur10002_comp0_c0_seq1" + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_arachis", "Arachis_Atlas", "Absolute", "Abc" + ) + expected = "Gene 1 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # eFP Arachis gene 2 + result = eFPUtils.is_efp_input_valid( + "efp_arachis", + "Arachis_Atlas", + "Compare", + "Adur10002_comp0_c0_seq1", + "Adur10002_comp0_c0_seq11", + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_arachis", "Arachis_Atlas", "Compare", "Adur10002_comp0_c0_seq1", "Abc" + ) + expected = "Gene 2 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # Test if both gene are the same in eFP Comare mode + result = eFPUtils.is_efp_input_valid( + "efp_arachis", + "Arachis_Atlas", + "Compare", + "Adur10002_comp0_c0_seq1", + "Adur10002_comp0_c0_seq1", + ) + expected = "In compare mode, both genes should be different." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) From 6cc7f6952470c3d2f1967d54cc7d9feab405839f Mon Sep 17 00:00:00 2001 From: asherpasha Date: Fri, 17 Dec 2021 17:35:33 -0500 Subject: [PATCH 4/5] Added eFP Soybean. --- api/resources/efp_image.py | 2 +- api/utils/bar_utils.py | 13 +++++++++++++ api/utils/efp_utils.py | 13 +++++++++++-- requirements.txt | 4 ++-- tests/resources/test_efp_image.py | 2 +- tests/utils/test_efp_utils.py | 32 +++++++++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 6 deletions(-) diff --git a/api/resources/efp_image.py b/api/resources/efp_image.py index d395f9f1..00ea5719 100644 --- a/api/resources/efp_image.py +++ b/api/resources/efp_image.py @@ -20,7 +20,7 @@ def get(self): """This end point returns the list of species available""" # This are the only species available so far # If this is updated, update efp_utils.py and unit tests as well - species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis"] + species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis", "efp_soybean"] return BARUtils.success_exit(species) diff --git a/api/utils/bar_utils.py b/api/utils/bar_utils.py index b0396ec8..6546c8db 100644 --- a/api/utils/bar_utils.py +++ b/api/utils/bar_utils.py @@ -96,6 +96,19 @@ def is_arachis_gene_valid(gene): else: return False + @staticmethod + def is_soybean_gene_valid(gene): + """This function verifies if soybean gene is valid: Glyma06g47400 + :param gene: + :return: True if valid + """ + if gene and re.search( + r"^((Glyma\d{1,3}g\d{1,6}\.?\d?)|(Glyma\.\d{1,3}g\d{1,8}))$", gene, re.I + ): + return True + else: + return False + @staticmethod def is_integer(data): """Check if the input is at max ten figure number. diff --git a/api/utils/efp_utils.py b/api/utils/efp_utils.py index 6dbdb1c9..6ee79585 100644 --- a/api/utils/efp_utils.py +++ b/api/utils/efp_utils.py @@ -9,7 +9,7 @@ def is_efp_view_name(efp_view): :param efp_view: string view name :return: True if valid """ - if efp_view and re.search(r"^[a-z1-9_]{1,20}$", efp_view, re.I): + if efp_view and re.search(r"^[a-z1-9_]{1,50}$", efp_view, re.I): return True else: return False @@ -32,7 +32,7 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): """Test if eFP input is valid :return: List with boolean and string """ - species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis"] + species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis", "efp_soybean"] # Validate values if efp not in species: @@ -74,6 +74,15 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): if not BARUtils.is_arachis_gene_valid(gene_2): return False, "Gene 2 is invalid." + if efp == "efp_soybean": + # Validate gene ids + if not BARUtils.is_soybean_gene_valid(gene_1): + return False, "Gene 1 is invalid." + + if mode == "Compare": + if not BARUtils.is_soybean_gene_valid(gene_2): + return False, "Gene 2 is invalid." + # In compare mode gene1 != gene2 if mode == "Compare" and gene_1 == gene_2: return False, "In compare mode, both genes should be different." diff --git a/requirements.txt b/requirements.txt index 5f96e9cb..a7c8e524 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ flake8==4.0.1 Flask==2.0.2 Flask-Caching==1.10.1 Flask-Cors==3.0.10 -Flask-Limiter==2.0.2 +Flask-Limiter==2.0.3 flask-marshmallow==0.14.0 flask-restx==0.5.1 Flask-SQLAlchemy==2.5.1 @@ -24,7 +24,7 @@ idna==3.3 iniconfig==1.1.1 itsdangerous==2.0.1 Jinja2==3.0.3 -jsonschema==4.2.1 +jsonschema==4.3.1 limits==2.0.3 MarkupSafe==2.0.1 marshmallow==3.14.1 diff --git a/tests/resources/test_efp_image.py b/tests/resources/test_efp_image.py index fc36ad45..6850ad86 100644 --- a/tests/resources/test_efp_image.py +++ b/tests/resources/test_efp_image.py @@ -13,7 +13,7 @@ def test_get_efp_image_list(self): response = self.app_client.get("/efp_image/") expected = { "wasSuccessful": True, - "data": ["efp_arabidopsis", "efp_cannabis", "efp_arachis"], + "data": ["efp_arabidopsis", "efp_cannabis", "efp_arachis", "efp_soybean"], } self.assertEqual(response.json, expected) diff --git a/tests/utils/test_efp_utils.py b/tests/utils/test_efp_utils.py index 1ddd5b7d..d64161dd 100644 --- a/tests/utils/test_efp_utils.py +++ b/tests/utils/test_efp_utils.py @@ -80,6 +80,38 @@ def test_is_efp_input_valid(self): self.assertFalse(result[0]) self.assertEqual(result[1], expected) + # eFP Soybean Absolute + result = eFPUtils.is_efp_input_valid( + "efp_soybean", "soybean", "Absolute", "Glyma06g47400" + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_soybean", "soybean", "Absolute", "Abc" + ) + expected = "Gene 1 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # eFP Soybean gene 2 + result = eFPUtils.is_efp_input_valid( + "efp_soybean", + "soybean", + "Compare", + "Glyma06g47400", + "Glyma06g47390", + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_soybean", "soybean", "Compare", "Glyma06g47400", "Abc" + ) + expected = "Gene 2 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + # Test if both gene are the same in eFP Comare mode result = eFPUtils.is_efp_input_valid( "efp_arachis", From 7553f4f17569ae443a32dff7a605e15e0fd75111 Mon Sep 17 00:00:00 2001 From: asherpasha Date: Thu, 30 Dec 2021 11:10:31 -0500 Subject: [PATCH 5/5] eFP Maize is added. Output directory cleanup is also added. --- api/resources/api_manager.py | 6 ++--- api/resources/efp_image.py | 23 +++++++++++++++- api/utils/bar_utils.py | 15 +++++++++++ api/utils/efp_utils.py | 19 +++++++++++-- requirements.txt | 14 +++++----- tests/resources/test_efp_image.py | 8 +++++- tests/utils/test_efp_utils.py | 44 +++++++++++++++++++++++++++++++ 7 files changed, 115 insertions(+), 14 deletions(-) diff --git a/api/resources/api_manager.py b/api/resources/api_manager.py index 1d99c8ac..0a7327ca 100644 --- a/api/resources/api_manager.py +++ b/api/resources/api_manager.py @@ -40,14 +40,14 @@ def check_admin_pass(password): @staticmethod def validate_captcha(value): """Validates a reCaptcha value using our secret token""" - if(os.environ.get("BAR")): + if os.environ.get("BAR"): with open(CAPTCHA_KEY_FILE, "rb") as f: for line in f: key = line if key: ret = requests.post( "https://www.google.com/recaptcha/api/siteverify", - data={"secret": key, "response": value} + data={"secret": key, "response": value}, ) return ret.json()["success"] else: @@ -128,7 +128,7 @@ class ApiManagerRequest(Resource): def post(self): if request.method == "POST": captchaVal = request.headers.get("captchaVal") - if(ApiManagerUtils.validate_captcha(captchaVal)): + if ApiManagerUtils.validate_captcha(captchaVal): response_json = request.get_json() df = pandas.DataFrame.from_records([response_json]) con = db.get_engine(bind="summarization") diff --git a/api/resources/efp_image.py b/api/resources/efp_image.py index 00ea5719..f86e3286 100644 --- a/api/resources/efp_image.py +++ b/api/resources/efp_image.py @@ -2,6 +2,8 @@ import re import requests import random +import os +import time import redis.exceptions from flask_restx import Namespace, Resource from markupsafe import escape @@ -20,7 +22,13 @@ def get(self): """This end point returns the list of species available""" # This are the only species available so far # If this is updated, update efp_utils.py and unit tests as well - species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis", "efp_soybean"] + species = [ + "efp_arabidopsis", + "efp_cannabis", + "efp_arachis", + "efp_soybean", + "efp_maize", + ] return BARUtils.success_exit(species) @@ -48,6 +56,19 @@ def get(self, efp="", view="", mode="", gene_1="", gene_2=""): if validation[0] is False: return BARUtils.error_exit(validation[1]), 400 + # Data are valid. Clear directory before running + for file in os.listdir("output"): + # Full file name is required at this point + file = os.path.join("output", file) + + # Check if it is a png file and is greater than 5 minutes old + if ( + os.path.isfile(file) + and (os.path.splitext(file)[1] == ".png") + and (os.stat(file).st_mtime < (time.time() - 5 * 60)) + ): + os.remove(file) + # Check if request is cached try: r = BARUtils.connect_redis() diff --git a/api/utils/bar_utils.py b/api/utils/bar_utils.py index 6546c8db..80d02d2d 100644 --- a/api/utils/bar_utils.py +++ b/api/utils/bar_utils.py @@ -109,6 +109,21 @@ def is_soybean_gene_valid(gene): else: return False + @staticmethod + def is_maize_gene_valid(gene): + """This function verifies if maize gene is valid: Zm00001d046170 + :param gene: + :return: True if valid + """ + if gene and re.search( + r"^(AC[0-9]{6}\.[0-9]{1}_FG[0-9]{3})|(AC[0-9]{6}\.[0-9]{1}_FGT[0-9]{3})|(GRMZM(2|5)G[0-9]{6})|(GRMZM(2|5)G[0-9]{6}_T[0-9]{2})|(Zm\d+d\d+)$", + gene, + re.I, + ): + return True + else: + return False + @staticmethod def is_integer(data): """Check if the input is at max ten figure number. diff --git a/api/utils/efp_utils.py b/api/utils/efp_utils.py index 6ee79585..b78d1522 100644 --- a/api/utils/efp_utils.py +++ b/api/utils/efp_utils.py @@ -32,7 +32,13 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): """Test if eFP input is valid :return: List with boolean and string """ - species = ["efp_arabidopsis", "efp_cannabis", "efp_arachis", "efp_soybean"] + species = [ + "efp_arabidopsis", + "efp_cannabis", + "efp_arachis", + "efp_soybean", + "efp_maize", + ] # Validate values if efp not in species: @@ -46,7 +52,7 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): if not eFPUtils.is_efp_mode(mode): return False, "Invalid eFP mode." - # Maybe this part could be imported + # Maybe this part could be improved if efp == "efp_arabidopsis": # Validate gene ids if not BARUtils.is_arabidopsis_gene_valid(gene_1): @@ -83,6 +89,15 @@ def is_efp_input_valid(efp, view, mode, gene_1, gene_2=None): if not BARUtils.is_soybean_gene_valid(gene_2): return False, "Gene 2 is invalid." + if efp == "efp_maize": + # Validate gene ids + if not BARUtils.is_maize_gene_valid(gene_1): + return False, "Gene 1 is invalid." + + if mode == "Compare": + if not BARUtils.is_maize_gene_valid(gene_2): + return False, "Gene 2 is invalid." + # In compare mode gene1 != gene2 if mode == "Compare" and gene_1 == gene_2: return False, "In compare mode, both genes should be different." diff --git a/requirements.txt b/requirements.txt index a7c8e524..f6e5910e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aniso8601==9.0.1 appdirs==1.4.4 -attrs==21.2.0 +attrs==21.4.0 black==20.8b1 certifi==2021.10.8 cffi==1.15.0 @@ -15,7 +15,7 @@ flake8==4.0.1 Flask==2.0.2 Flask-Caching==1.10.1 Flask-Cors==3.0.10 -Flask-Limiter==2.0.3 +Flask-Limiter==2.0.4 flask-marshmallow==0.14.0 flask-restx==0.5.1 Flask-SQLAlchemy==2.5.1 @@ -24,15 +24,15 @@ idna==3.3 iniconfig==1.1.1 itsdangerous==2.0.1 Jinja2==3.0.3 -jsonschema==4.3.1 -limits==2.0.3 +jsonschema==4.3.2 +limits==2.1.0 MarkupSafe==2.0.1 marshmallow==3.14.1 mccabe==0.6.1 more-itertools==8.12.0 mypy-extensions==0.4.3 mysqlclient==2.1.0 -numpy==1.21.4 +numpy==1.21.5 packaging==21.3 pandas==1.3.5 pathspec==0.9.0 @@ -46,12 +46,12 @@ pyrsistent==0.18.0 pytest==6.2.5 python-dateutil==2.8.2 pytz==2021.3 -redis==4.0.2 +redis==4.1.0 regex==2021.11.10 requests==2.26.0 scour==0.38.2 six==1.16.0 -SQLAlchemy==1.4.28 +SQLAlchemy==1.4.29 toml==0.10.2 typed-ast==1.5.1 typing_extensions==4.0.1 diff --git a/tests/resources/test_efp_image.py b/tests/resources/test_efp_image.py index 6850ad86..0b77e550 100644 --- a/tests/resources/test_efp_image.py +++ b/tests/resources/test_efp_image.py @@ -13,7 +13,13 @@ def test_get_efp_image_list(self): response = self.app_client.get("/efp_image/") expected = { "wasSuccessful": True, - "data": ["efp_arabidopsis", "efp_cannabis", "efp_arachis", "efp_soybean"], + "data": [ + "efp_arabidopsis", + "efp_cannabis", + "efp_arachis", + "efp_soybean", + "efp_maize", + ], } self.assertEqual(response.json, expected) diff --git a/tests/utils/test_efp_utils.py b/tests/utils/test_efp_utils.py index d64161dd..d3bc1a7a 100644 --- a/tests/utils/test_efp_utils.py +++ b/tests/utils/test_efp_utils.py @@ -123,3 +123,47 @@ def test_is_efp_input_valid(self): expected = "In compare mode, both genes should be different." self.assertFalse(result[0]) self.assertEqual(result[1], expected) + + # eFP Maize Absolute + result = eFPUtils.is_efp_input_valid( + "efp_maize", "maize_iplant", "Absolute", "Zm00001d046170" + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_maize", "maize_iplant", "Absolute", "Abc" + ) + expected = "Gene 1 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # eFP Maize gene 2 + result = eFPUtils.is_efp_input_valid( + "efp_maize", + "maize_iplant", + "Compare", + "Zm00001d046170", + "Zm00001d014297", + ) + self.assertTrue(result[0]) + self.assertIsNone(result[1]) + + result = eFPUtils.is_efp_input_valid( + "efp_maize", "maize_iplant", "Compare", "Zm00001d046170", "Abc" + ) + expected = "Gene 2 is invalid." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected) + + # Test if both gene are the same in eFP Comare mode + result = eFPUtils.is_efp_input_valid( + "efp_arachis", + "Arachis_Atlas", + "Compare", + "Adur10002_comp0_c0_seq1", + "Adur10002_comp0_c0_seq1", + ) + expected = "In compare mode, both genes should be different." + self.assertFalse(result[0]) + self.assertEqual(result[1], expected)