From 2d4801edc6eb45a4c9de57ab5d50bd52511f75cf Mon Sep 17 00:00:00 2001 From: francastell Date: Mon, 22 Apr 2024 09:44:39 -0400 Subject: [PATCH] G3-230 geneset gene values endpoint --- pyproject.toml | 2 +- src/geneweaver/api/controller/genesets.py | 24 +++++++++++++++ src/geneweaver/api/schemas/apimodels.py | 7 +++++ src/geneweaver/api/services/geneset.py | 29 ++++++++++++++++++ tests/controllers/test_genesets.py | 30 ++++++++++++++++++ tests/data/__init__.py | 7 ++++- tests/data/geneset.json | 18 ++++++++++- tests/services/test_genset.py | 37 ++++++++++++++++++++--- 8 files changed, 147 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 24ee67e..6e7bfb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "geneweaver-api" -version = "0.4.0a13" +version = "0.4.0a14" description = "The Geneweaver API" authors = [ "Alexander Berger ", diff --git a/src/geneweaver/api/controller/genesets.py b/src/geneweaver/api/controller/genesets.py index 1a15715..558e998 100644 --- a/src/geneweaver/api/controller/genesets.py +++ b/src/geneweaver/api/controller/genesets.py @@ -9,6 +9,7 @@ from fastapi import APIRouter, Depends, HTTPException, Path, Query, Security from fastapi.responses import FileResponse from geneweaver.api import dependencies as deps +from geneweaver.api.schemas.apimodels import GeneValueReturn from geneweaver.api.schemas.auth import UserInternal from geneweaver.api.services import geneset as genset_service from geneweaver.api.services import publications as publication_service @@ -140,6 +141,29 @@ def get_geneset( return response +@router.get("/{geneset_id}/values") +def get_geneset_values( + geneset_id: Annotated[ + int, Path(format="int64", minimum=0, maxiumum=9223372036854775807) + ], + user: UserInternal = Security(deps.full_user), + cursor: Optional[deps.Cursor] = Depends(deps.cursor), +) -> GeneValueReturn: + """Get geneset gene values by geneset ID.""" + response = genset_service.get_geneset_gene_values(cursor, geneset_id, user) + + if "error" in response: + if response.get("message") == api_message.ACCESS_FORBIDDEN: + raise HTTPException(status_code=403, detail=api_message.ACCESS_FORBIDDEN) + else: + raise HTTPException(status_code=500, detail=api_message.UNEXPECTED_ERROR) + + if response.get("data") is None: + raise HTTPException(status_code=404, detail=api_message.RECORD_NOT_FOUND_ERROR) + + return response + + @router.get("/{geneset_id}/file", response_class=FileResponse) def get_export_geneset_by_id_type( geneset_id: Annotated[ diff --git a/src/geneweaver/api/schemas/apimodels.py b/src/geneweaver/api/schemas/apimodels.py index 4e754e3..90ab9dd 100644 --- a/src/geneweaver/api/schemas/apimodels.py +++ b/src/geneweaver/api/schemas/apimodels.py @@ -4,6 +4,7 @@ from geneweaver.core.enum import GeneIdentifier, Species from geneweaver.core.schema.gene import Gene as GeneSchema +from geneweaver.core.schema.geneset import GeneValue as GeneValueSchema from geneweaver.core.schema.species import Species as SpeciesSchema from pydantic import AnyUrl, BaseModel @@ -75,3 +76,9 @@ class SpeciesReturn(CollectionResponse): """Model for Species endpoint return.""" data: List[SpeciesSchema] + + +class GeneValueReturn(BaseModel): + """Model for geneset values endpoint return.""" + + data: List[GeneValueSchema] diff --git a/src/geneweaver/api/services/geneset.py b/src/geneweaver/api/services/geneset.py index 48c9cda..f4775c1 100644 --- a/src/geneweaver/api/services/geneset.py +++ b/src/geneweaver/api/services/geneset.py @@ -126,6 +126,7 @@ def get_geneset(cursor: Cursor, geneset_id: int, user: User) -> dict: ) geneset = results[0] geneset_values = db_geneset_value.by_geneset_id(cursor, geneset_id) + return {"geneset": geneset, "geneset_values": geneset_values} except Exception as err: @@ -133,6 +134,34 @@ def get_geneset(cursor: Cursor, geneset_id: int, user: User) -> dict: raise err +def get_geneset_gene_values(cursor: Cursor, geneset_id: int, user: User) -> dict: + """Get a gene values for a given geneset ID. + + @param cursor: DB cursor + @param geneset_id: geneset identifier + @param user: GW user + @return: dictionary response (geneset and genset values). + """ + try: + if user is None or user.id is None: + return {"error": True, "message": message.ACCESS_FORBIDDEN} + + geneset_values = db_geneset_value.by_geneset_id(cursor, geneset_id) + if geneset_values is None or len(geneset_values) <= 0: + return {"data": None} + + genes_data = [] + for gsv in geneset_values: + gene_value = {"symbol": gsv["ode_ref_id"], "value": float(gsv["gsv_value"])} + genes_data.append(gene_value) + + return {"data": genes_data} + + except Exception as err: + logger.error(err) + raise err + + def get_geneset_w_gene_id_type( cursor: Cursor, geneset_id: int, user: User, gene_id_type: GeneIdentifier ) -> dict: diff --git a/tests/controllers/test_genesets.py b/tests/controllers/test_genesets.py index 973cac1..d65af59 100644 --- a/tests/controllers/test_genesets.py +++ b/tests/controllers/test_genesets.py @@ -10,6 +10,7 @@ geneset_w_gene_id_type_resp = test_geneset_data.get("geneset_w_gene_id_type_resp") geneset_metadata_w_pub_info = test_geneset_data.get("geneset_metadata_w_pub_info") publication_by_id_resp = test_publication_data.get("publication_by_id") +geneset_genes_values_resp = test_geneset_data.get("geneset_genes_values_resp_1") @patch("geneweaver.api.services.geneset.get_geneset") @@ -198,3 +199,32 @@ def test_get_visible_geneset_errors(mock_get_visible_genesets, client): response = client.get("/api/genesets?gs_id=1234") assert response.status_code == 500 + + +@patch("geneweaver.api.services.geneset.get_geneset_gene_values") +def test_get_geneset_gene_values_url_response(mock_get_geneset_gene_values, client): + """Test get geneset gene values data response.""" + mock_get_geneset_gene_values.return_value = geneset_genes_values_resp + + response = client.get("/api/genesets/1234/values") + assert response.status_code == 200 + assert response.json() == geneset_genes_values_resp + + +@patch("geneweaver.api.services.geneset.get_geneset_gene_values") +def test_get_geneset_gene_values_errors(mock_get_geneset_gene_values, client): + """Test get geneset gene values error responses.""" + mock_get_geneset_gene_values.return_value = { + "error": True, + "message": message.ACCESS_FORBIDDEN, + } + response = client.get("/api/genesets/1234/values") + assert response.status_code == 403 + + mock_get_geneset_gene_values.return_value = {"error": True, "message": "other"} + response = client.get("/api/genesets/1234/values") + assert response.status_code == 500 + + mock_get_geneset_gene_values.return_value = {"data": None} + response = client.get("/api/genesets/1234/values") + assert response.status_code == 404 diff --git a/tests/data/__init__.py b/tests/data/__init__.py index 9f7861c..d3b2c46 100644 --- a/tests/data/__init__.py +++ b/tests/data/__init__.py @@ -41,7 +41,12 @@ "geneset_metadata_w_pub_info": json.loads(geneset_response_json).get( "geneset_with_publication_info" ), - "geneset_list_resp": json.loads(geneset_list_response_json), + "geneset_list_resp": json.loads(geneset_list_response_json).get( + "geneset_resp_1_list_10" + ), + "geneset_genes_values_resp_1": json.loads(geneset_list_response_json).get( + "geneset_genes_values_resp_1" + ), } # Gene homolog ids test data diff --git a/tests/data/geneset.json b/tests/data/geneset.json index 8aba928..c42ff67 100644 --- a/tests/data/geneset.json +++ b/tests/data/geneset.json @@ -280,5 +280,21 @@ "publication_year": null, "publication_pubmed_id": null } - ] + ], + "geneset_genes_values_resp_1": { + "data": [ + { + "symbol": "Hs.629744", + "value": 1.0 + }, + { + "symbol": "Hs.517155", + "value": 1.0 + }, + { + "symbol": "Hs.356063", + "value": 1.0 + } + ] + } } \ No newline at end of file diff --git a/tests/services/test_genset.py b/tests/services/test_genset.py index 5ef116b..effa950 100644 --- a/tests/services/test_genset.py +++ b/tests/services/test_genset.py @@ -14,6 +14,7 @@ geneset_list_resp = test_geneset_data.get("geneset_list_resp") geneset_w_gene_id_type_resp = test_geneset_data.get("geneset_w_gene_id_type_resp") geneset_metadata_w_pub_info = test_geneset_data.get("geneset_metadata_w_pub_info") +geneset_genes_values_resp = test_geneset_data.get("geneset_genes_values_resp_1") mock_user = User() mock_user.id = 1 @@ -176,10 +177,10 @@ def test_geneset_metadata_db_call_error(mock_db_geneset): @patch("geneweaver.api.services.geneset.db_geneset") def test_visible_geneset_response(mock_db_geneset): """Test general get geneset data no parameters -- default limit.""" - mock_db_geneset.get.return_value = geneset_list_resp.get("geneset_resp_1_list_10") + mock_db_geneset.get.return_value = geneset_list_resp response = geneset.get_visible_genesets(None, mock_user) - assert response.get("data") == geneset_list_resp.get("geneset_resp_1_list_10") + assert response.get("data") == geneset_list_resp def test_visible_geneset_no_user(): @@ -196,7 +197,7 @@ def test_visible_geneset_no_user(): @patch("geneweaver.api.services.geneset.db_geneset") def test_visible_geneset_all_expected_parameters(mock_db_geneset): """Test general get geneset data no parameters -- default limit.""" - mock_db_geneset.get.return_value = geneset_list_resp.get("geneset_resp_1_list_10") + mock_db_geneset.get.return_value = geneset_list_resp response = geneset.get_visible_genesets( cursor=None, @@ -215,7 +216,7 @@ def test_visible_geneset_all_expected_parameters(mock_db_geneset): with_publication_info=True, ) - assert response.get("data") == geneset_list_resp.get("geneset_resp_1_list_10") + assert response.get("data") == geneset_list_resp @patch("geneweaver.api.services.geneset.db_geneset") @@ -249,3 +250,31 @@ def test_map_geneset_homology_db_call_error(mock_db_gene): geneset.map_geneset_homology( None, geneset_by_id_resp["geneset_values"], GeneIdentifier(2) ) + + +@patch("geneweaver.api.services.geneset.db_geneset_value") +def test_geneset_gene_value_response(mock_db_geneset_value): + """Test geneset gene value data response.""" + mock_db_geneset_value.by_geneset_id.return_value = geneset_by_id_resp.get( + "geneset_values" + ) + + response = geneset.get_geneset_gene_values(None, user=mock_user, geneset_id=1234) + assert response == geneset_genes_values_resp + + +@patch("geneweaver.api.services.geneset.db_geneset_value") +def test_get_geneset_gene_values_db_errors(mock_db_geneset_value): + """Test error in get DB call.""" + mock_db_geneset_value.by_geneset_id.side_effect = Exception("ERROR") + + with pytest.raises(expected_exception=Exception): + geneset.get_geneset_gene_values(None, user=mock_user, geneset_id=1234) + + +@patch("geneweaver.api.services.geneset.db_geneset_value") +def test_get_geneset_gene_values_invalid_user(mock_db_geneset_value): + """Test invalid user.""" + response = geneset.get_geneset_gene_values(None, user=None, geneset_id=1234) + assert response.get("error") is True + assert response.get("message") == message.ACCESS_FORBIDDEN