diff --git a/pyproject.toml b/pyproject.toml index 60fa9bb..bc9b643 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "geneweaver-db" -version = "0.5.0a8" +version = "0.5.0a9" description = "Database Interaction Services for GeneWeaver" authors = ["Jax Computational Sciences "] readme = "README.md" diff --git a/src/geneweaver/db/aio/ontology.py b/src/geneweaver/db/aio/ontology.py new file mode 100644 index 0000000..c93d444 --- /dev/null +++ b/src/geneweaver/db/aio/ontology.py @@ -0,0 +1,32 @@ +"""Database code for interacting with ontologies.""" + +from typing import List, Optional + +from geneweaver.db.query import ontology as ontology_query +from psycopg import AsyncCursor +from psycopg.rows import Row + + +async def by_geneset( + cursor: AsyncCursor, + geneset_id: int, + limit: Optional[int] = None, + offset: Optional[int] = None, +) -> List[Row]: + """Get geneset ontologies from the database. + + :param cursor: An async database cursor. + :param geneset_id: Show only results for this geneset identifier + :param limit: Limit the number of results. + :param offset: Offset the results. + @return: + """ + await cursor.execute( + *ontology_query.by_geneset( + geneset_id=geneset_id, + limit=limit, + offset=offset, + ) + ) + + return await cursor.fetchall() diff --git a/src/geneweaver/db/ontology.py b/src/geneweaver/db/ontology.py new file mode 100644 index 0000000..2dace2b --- /dev/null +++ b/src/geneweaver/db/ontology.py @@ -0,0 +1,32 @@ +"""Database code for interacting with ontologies.""" + +from typing import List, Optional + +from geneweaver.db.query import ontology as ontology_query +from psycopg import Cursor +from psycopg.rows import Row + + +def by_geneset( + cursor: Cursor, + geneset_id: int, + limit: Optional[int] = None, + offset: Optional[int] = None, +) -> List[Row]: + """Get geneset ontologies from the database. + + :param cursor: A database cursor. + :param geneset_id: Show only results for this geneset identifier + :param limit: Limit the number of results. + :param offset: Offset the results. + @return: + """ + cursor.execute( + *ontology_query.by_geneset( + geneset_id=geneset_id, + limit=limit, + offset=offset, + ) + ) + + return cursor.fetchall() diff --git a/src/geneweaver/db/query/ontology.py b/src/geneweaver/db/query/ontology.py new file mode 100644 index 0000000..63bab6f --- /dev/null +++ b/src/geneweaver/db/query/ontology.py @@ -0,0 +1,43 @@ +"""Query generation functions for ontologies.""" + +from typing import Optional, Tuple + +from geneweaver.db.utils import limit_and_offset +from psycopg.sql import SQL, Composed + + +def by_geneset( + geneset_id: int, + limit: Optional[int] = None, + offset: Optional[int] = None, +) -> Tuple[Composed, dict]: + """Create a psycopg query to get genesets ontologies. + + :param geneset_id: The geneset identifier to search for. + :param limit: The limit of results to return. + :param offset: The offset of results to return. + + :return: A query (and params) that can be executed on a cursor. + """ + query = SQL("SELECT") + query_fields = ( + SQL("geneset.gs_id AS geneset_id") + + SQL(",ontology.ont_ref_id AS ontology_id") + + SQL(",ontology.ont_name AS name") + + SQL(",ontology.ont_description as description") + + SQL(",ontologydb.ontdb_name as source_ontology") + ) + query = ( + query + + query_fields + + SQL("FROM geneset") + + SQL("JOIN geneset_ontology ON geneset.gs_id = geneset_ontology.gs_id") + + SQL("JOIN ontology ON geneset_ontology.ont_id = ontology.ont_id") + + SQL("JOIN ontologydb ON ontology.ontdb_id = ontologydb.ontdb_id") + + SQL("WHERE geneset.gs_id = %(gs_id)s") + ).join(" ") + params = {"gs_id": geneset_id} + + query = limit_and_offset(query, limit, offset).join(" ") + + return query, params diff --git a/tests/unit/ontology/__init__.py b/tests/unit/ontology/__init__.py new file mode 100644 index 0000000..4485112 --- /dev/null +++ b/tests/unit/ontology/__init__.py @@ -0,0 +1 @@ +"""Tests for the ontology db module.""" diff --git a/tests/unit/ontology/test_by_geneset.py b/tests/unit/ontology/test_by_geneset.py new file mode 100644 index 0000000..d5e0afa --- /dev/null +++ b/tests/unit/ontology/test_by_geneset.py @@ -0,0 +1,87 @@ +"""Test the ontology.by_geneset db function.""" + +import pytest +from geneweaver.db.aio.ontology import ( + by_geneset as async_get_ontology_by_gs_id, +) +from geneweaver.db.ontology import by_geneset + +from tests.unit.testing_utils import ( + async_create_execute_raises_error_test, + async_create_fetchall_raises_error_test, + create_execute_raises_error_test, + create_fetchall_raises_error_test, +) + + +@pytest.mark.parametrize("geneset_id", [12345]) +@pytest.mark.parametrize("limit", [None, 1, 10]) +@pytest.mark.parametrize("offset", [None, 1, 10]) +@pytest.mark.parametrize( + "ontologies_resp", + [ + ["a", "b", "c"], + ["d", "e", "f"], + ["g", "h", "i"], + ], +) +def test_get_ontology_by_gs_id(geneset_id, limit, offset, ontologies_resp, cursor): + """Test the ontology.get_ontology_by_gs_id function.""" + cursor.fetchall.return_value = ontologies_resp + + result = by_geneset( + cursor=cursor, + geneset_id=geneset_id, + limit=limit, + offset=offset, + ) + assert result == ontologies_resp + assert cursor.execute.call_count == 1 + assert cursor.fetchone.call_count == 0 + assert cursor.fetchall.call_count == 1 + + +@pytest.mark.parametrize("geneset_id", [12345]) +@pytest.mark.parametrize("limit", [None, 1, 10]) +@pytest.mark.parametrize("offset", [None, 1, 10]) +@pytest.mark.parametrize( + "ontologies_resp", + [ + ["a", "b", "c"], + ["d", "e", "f"], + ["g", "h", "i"], + ], +) +async def test_async_get_ontology_by_gs_id( + geneset_id, + limit, + offset, + ontologies_resp, + async_cursor, +): + """Test the async ontology.get_ontology_by_gs_id function.""" + async_cursor.fetchall.return_value = ontologies_resp + + result = await async_get_ontology_by_gs_id( + cursor=async_cursor, + geneset_id=geneset_id, + limit=limit, + offset=offset, + ) + assert result == ontologies_resp + assert async_cursor.execute.call_count == 1 + assert async_cursor.fetchone.call_count == 0 + assert async_cursor.fetchall.call_count == 1 + + +test_get_execute_raises_error = create_execute_raises_error_test(by_geneset, 1) + +test_get_fetchall_raises_error = create_fetchall_raises_error_test(by_geneset, 1) + +test_get_execute_raises_error = async_create_execute_raises_error_test( + async_get_ontology_by_gs_id, 1 +) + +test_async_get_fetchall_raises_error = async_create_fetchall_raises_error_test( + async_get_ontology_by_gs_id, 1 +) diff --git a/tests/unit/query/ontology/__init__.py b/tests/unit/query/ontology/__init__.py new file mode 100644 index 0000000..0e94ff1 --- /dev/null +++ b/tests/unit/query/ontology/__init__.py @@ -0,0 +1 @@ +"""Test the sql generation code for ontologies.""" diff --git a/tests/unit/query/ontology/test_by_geneset.py b/tests/unit/query/ontology/test_by_geneset.py new file mode 100644 index 0000000..b26d092 --- /dev/null +++ b/tests/unit/query/ontology/test_by_geneset.py @@ -0,0 +1,23 @@ +"""Test the ontology.by_geneset query generation function.""" + +import pytest +from geneweaver.db.query.ontology import by_geneset + + +@pytest.mark.parametrize("geneset_id", [12345]) +@pytest.mark.parametrize("limit", [None, 1, 10]) +@pytest.mark.parametrize("offset", [None, 1, 10]) +def test_all_kwargs(geneset_id, limit, offset): + """Test all the kwarg combinations for query.ontology.by_geneset.""" + query, params = by_geneset( + geneset_id=geneset_id, + limit=limit, + offset=offset, + ) + + assert "gs_id" in params + + str_query = str(query) + + for key in ["ontology_id", "name", "description", "source_ontology"]: + assert key in str_query