From 8c9d1537bfa7b4caf090834f39c53ab141bd64fa Mon Sep 17 00:00:00 2001 From: Sierra Taylor Moxon Date: Thu, 12 Dec 2024 16:12:52 -0800 Subject: [PATCH 1/2] add a new endpoint with the correct output - gocam --- app/routers/models.py | 44 ++++++++++++++++++++++++++++- app/utils/sparql_utils.py | 6 ++++ poetry.lock | 22 ++++++++++++++- pyproject.toml | 1 + tests/unit/test_models_endpoints.py | 11 ++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/app/routers/models.py b/app/routers/models.py index 894380b..4fb535f 100644 --- a/app/routers/models.py +++ b/app/routers/models.py @@ -1,8 +1,9 @@ """Model API router.""" import logging +from http.client import HTTPException from typing import List - +from pprint import pprint import requests from fastapi import APIRouter, Path, Query from oaklib.implementations.sparql.sparql_implementation import SparqlImplementation @@ -12,12 +13,53 @@ from app.utils import ontology_utils from app.utils.settings import get_sparql_endpoint, get_user_agent from app.utils.sparql_utils import transform_array +from gocam.datamodel.gocam import Model +from gocam.translation.minerva_wrapper import MinervaWrapper USER_AGENT = get_user_agent() SPARQL_ENDPOINT = get_sparql_endpoint() router = APIRouter() logger = logging.getLogger() +@router.get("/api/gocam-model/{id}", + tags=["models"], + description="Returns model details in gocam-py format based on a GO-CAM model ID.") +async def get_gocam_model_by_id_in_gocam_py_format( + id: str = Path( + ..., + description="A GO-CAM identifier (e.g. 581e072c00000820, 581e072c00000295, 5900dc7400000968)", + example="581e072c00000295", + ) +) -> dict: + """ + Returns model details in gocam-py format based on a GO-CAM model ID. + + :param id: A GO-CAM identifier (e.g. 581e072c00000820, 581e072c00000295, 5900dc7400000968) + :return: model details in gocam-py format based on a GO-CAM model ID. + """ + + mw = MinervaWrapper() + stripped_ids = [] + if id.startswith("gomodel:"): + model_id = id.replace("gomodel:", "") + stripped_ids.append(model_id) + else: + stripped_ids.append(id) + for stripped_id in stripped_ids: + path_to_s3 = "https://go-public.s3.amazonaws.com/files/go-cam/%s.json" % stripped_id + print(path_to_s3) + try: + response = requests.get(path_to_s3, timeout=30, headers={"User-Agent": USER_AGENT}) + print(response.json()) + response.raise_for_status() + if response.status_code == 403 or response.status_code == 404: + raise DataNotFoundException("GO-CAM model not found.") + data = response.json() + gocam_reposnse = mw.minerva_object_to_model(data) # convert minerva object to gocam model + pprint(gocam_reposnse) + return gocam_reposnse.model_dump() + except Exception as e: + raise HTTPException(status_code=500, detail=f"Unexpected error: {e}") @router.get("/api/models/go", tags=["models"], description="Returns go term details based on a GO-CAM model ID.") diff --git a/app/utils/sparql_utils.py b/app/utils/sparql_utils.py index 43f83db..f9920da 100644 --- a/app/utils/sparql_utils.py +++ b/app/utils/sparql_utils.py @@ -1,5 +1,11 @@ """Utils for SPARQL queries.""" +from fastapi.responses import ORJSONResponse +from typing import Any + +def create_response(data: Any): + return ORJSONResponse(content=data.dict()) + SEPARATOR = "|" # separator for splitting values diff --git a/poetry.lock b/poetry.lock index 86ffc8a..674bacc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1057,6 +1057,26 @@ files = [ paramiko = ">=2.11.0" pyyaml = ">=6.0" +[[package]] +name = "gocam" +version = "0.2.0" +description = "GO CAM Data Model (Python)" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "gocam-0.2.0-py3-none-any.whl", hash = "sha256:8d7e1ab8d9738a7aaf98936bb02be14af7f616aa6d1c7d0962cff3ff23aa2567"}, + {file = "gocam-0.2.0.tar.gz", hash = "sha256:111400e2d456b92b6c8b0dc6659a0098c7c0e4e3b9ed3939ec4198bb71473543"}, +] + +[package.dependencies] +click = ">=8,<9" +linkml-runtime = ">=1.1.24,<2.0.0" +ndex2 = ">=3.9.0,<4.0.0" +prefixmaps = ">=0.2.5,<0.3.0" +pydantic = ">=2,<3" +pyyaml = ">=6,<7" +requests = ">=2,<3" + [[package]] name = "graphviz" version = "0.20.1" @@ -4731,4 +4751,4 @@ docs = [] [metadata] lock-version = "2.0" python-versions = "^3.10.1" -content-hash = "8d735ddb93eee233c8804c96352332e7c2ef8aa179a262ff726340a198f005fb" +content-hash = "b3a521fa31789a40aefc15f9a85f155311fa8e63d6fb9bc36640dc4260ea3b20" diff --git a/pyproject.toml b/pyproject.toml index f6c3ae9..a4464d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ go-deploy = ">=0.4.1" biothings-client = "^0.3.0" email-validator = "^2.0.0.post2" bmt = "^1.1.2" +gocam = "^0.2.0" [tool.poetry.dev-dependencies] pytest = ">=7.4.0" diff --git a/tests/unit/test_models_endpoints.py b/tests/unit/test_models_endpoints.py index 9946865..71e6b30 100644 --- a/tests/unit/test_models_endpoints.py +++ b/tests/unit/test_models_endpoints.py @@ -8,6 +8,7 @@ from requests import HTTPError from app.main import app +from tests.integration.step_defs.bioentity_function_steps import response_code logging.basicConfig(filename="combined_access_error.log", level=logging.INFO, format="%(asctime)s - %(message)s") logger = logging.getLogger() @@ -151,6 +152,16 @@ def test_get_model_details_by_model_id_json(self): self.assertGreater(len(response.json().get("individuals")), 0) self.assertGreater(len(response.json().get("facts")), 0) + def test_get_model_details_by_model_id_json_gocam_py(self): + """ + Test the endpoint to retrieve model details by model ID in JSON format from the S3 bucket, check for success. + + :return: None + """ + for id in go_cam_ids: + response = test_client.get(f"/api/gocam-model/{id}") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json().get("id"), "gomodel:" + id.replace("gomodel:", "")) def test_get_model_details_by_model_id_not_found_json(self): """ From ff2dff8f524cb44873d47e81c3934e32176067e6 Mon Sep 17 00:00:00 2001 From: Sierra Taylor Moxon Date: Thu, 12 Dec 2024 16:36:03 -0800 Subject: [PATCH 2/2] lint --- app/routers/models.py | 21 ++++++++++++--------- app/utils/sparql_utils.py | 11 ++++++++++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/routers/models.py b/app/routers/models.py index 4fb535f..66f50e0 100644 --- a/app/routers/models.py +++ b/app/routers/models.py @@ -2,10 +2,12 @@ import logging from http.client import HTTPException -from typing import List from pprint import pprint +from typing import List + import requests from fastapi import APIRouter, Path, Query +from gocam.translation.minerva_wrapper import MinervaWrapper from oaklib.implementations.sparql.sparql_implementation import SparqlImplementation from oaklib.resource import OntologyResource @@ -13,17 +15,19 @@ from app.utils import ontology_utils from app.utils.settings import get_sparql_endpoint, get_user_agent from app.utils.sparql_utils import transform_array -from gocam.datamodel.gocam import Model -from gocam.translation.minerva_wrapper import MinervaWrapper USER_AGENT = get_user_agent() SPARQL_ENDPOINT = get_sparql_endpoint() router = APIRouter() logger = logging.getLogger() -@router.get("/api/gocam-model/{id}", - tags=["models"], - description="Returns model details in gocam-py format based on a GO-CAM model ID.") + + +@router.get( + "/api/gocam-model/{id}", + tags=["models"], + description="Returns model details in gocam-py format based on a GO-CAM model ID.", +) async def get_gocam_model_by_id_in_gocam_py_format( id: str = Path( ..., @@ -37,7 +41,6 @@ async def get_gocam_model_by_id_in_gocam_py_format( :param id: A GO-CAM identifier (e.g. 581e072c00000820, 581e072c00000295, 5900dc7400000968) :return: model details in gocam-py format based on a GO-CAM model ID. """ - mw = MinervaWrapper() stripped_ids = [] if id.startswith("gomodel:"): @@ -59,7 +62,7 @@ async def get_gocam_model_by_id_in_gocam_py_format( pprint(gocam_reposnse) return gocam_reposnse.model_dump() except Exception as e: - raise HTTPException(status_code=500, detail=f"Unexpected error: {e}") + raise HTTPException(status_code=500, detail=f"Unexpected error: {e}") from e @router.get("/api/models/go", tags=["models"], description="Returns go term details based on a GO-CAM model ID.") @@ -355,7 +358,7 @@ async def get_pmid_by_model_id( @router.get( "/api/go-cam/{id}", tags=["models"], description="Returns model details based on a GO-CAM model ID in JSON format." -) +) # note: this is the endpoint that is currently used by gocam-py to for use in CTX export. async def get_model_details_by_model_id_json( id: str = Path( ..., diff --git a/app/utils/sparql_utils.py b/app/utils/sparql_utils.py index f9920da..bc11ee2 100644 --- a/app/utils/sparql_utils.py +++ b/app/utils/sparql_utils.py @@ -1,11 +1,20 @@ """Utils for SPARQL queries.""" -from fastapi.responses import ORJSONResponse from typing import Any +from fastapi.responses import ORJSONResponse + + def create_response(data: Any): + """ + Create a response with the given data. + + :param data: + :return: + """ return ORJSONResponse(content=data.dict()) + SEPARATOR = "|" # separator for splitting values